Explorar o código

添加protobuf-net, 6.0将使用protobuf-net,去掉google.protobuf

tanghai %!s(int64=5) %!d(string=hai) anos
pai
achega
bbd4653616
Modificáronse 100 ficheiros con 19212 adicións e 508 borrados
  1. 15 96
      Client-Server.sln
  2. 0 3
      Server/Hotfix/Server.Hotfix.csproj
  3. 0 0
      Server/Model/ConfigPartial/StartProcessConfig.cs
  4. 0 0
      Server/Model/ConfigPartial/StartSceneConfig.cs
  5. 4 5
      Server/Model/Server.Model.csproj
  6. 79 0
      Server/ThirdParty/Google.Protobuf/ByteArray.cs
  7. 11 0
      Server/ThirdParty/Google.Protobuf/ByteArray.cs.meta
  8. 378 0
      Server/ThirdParty/Google.Protobuf/ByteString.cs
  9. 11 0
      Server/ThirdParty/Google.Protobuf/ByteString.cs.meta
  10. 1293 0
      Server/ThirdParty/Google.Protobuf/CodedInputStream.cs
  11. 11 0
      Server/ThirdParty/Google.Protobuf/CodedInputStream.cs.meta
  12. 304 0
      Server/ThirdParty/Google.Protobuf/CodedOutputStream.ComputeSize.cs
  13. 11 0
      Server/ThirdParty/Google.Protobuf/CodedOutputStream.ComputeSize.cs.meta
  14. 770 0
      Server/ThirdParty/Google.Protobuf/CodedOutputStream.cs
  15. 11 0
      Server/ThirdParty/Google.Protobuf/CodedOutputStream.cs.meta
  16. 379 0
      Server/ThirdParty/Google.Protobuf/FieldCodec.cs
  17. 11 0
      Server/ThirdParty/Google.Protobuf/FieldCodec.cs.meta
  18. 0 74
      Server/ThirdParty/Google.Protobuf/Google.Protobuf.csproj
  19. 69 0
      Server/ThirdParty/Google.Protobuf/ICustomDiagnosticMessage.cs
  20. 11 0
      Server/ThirdParty/Google.Protobuf/ICustomDiagnosticMessage.cs.meta
  21. 64 0
      Server/ThirdParty/Google.Protobuf/IMessage.cs
  22. 11 0
      Server/ThirdParty/Google.Protobuf/IMessage.cs.meta
  23. 129 0
      Server/ThirdParty/Google.Protobuf/InvalidProtocolBufferException.cs
  24. 11 0
      Server/ThirdParty/Google.Protobuf/InvalidProtocolBufferException.cs.meta
  25. 110 0
      Server/ThirdParty/Google.Protobuf/LimitedInputStream.cs
  26. 11 0
      Server/ThirdParty/Google.Protobuf/LimitedInputStream.cs.meta
  27. 178 0
      Server/ThirdParty/Google.Protobuf/MessageExtensions.cs
  28. 11 0
      Server/ThirdParty/Google.Protobuf/MessageExtensions.cs.meta
  29. 241 0
      Server/ThirdParty/Google.Protobuf/MessageParser.cs
  30. 11 0
      Server/ThirdParty/Google.Protobuf/MessageParser.cs.meta
  31. 64 0
      Server/ThirdParty/Google.Protobuf/PropertyInfoExtensions.cs
  32. 11 0
      Server/ThirdParty/Google.Protobuf/PropertyInfoExtensions.cs.meta
  33. 79 0
      Server/ThirdParty/Google.Protobuf/ProtoPreconditions.cs
  34. 11 0
      Server/ThirdParty/Google.Protobuf/ProtoPreconditions.cs.meta
  35. 581 0
      Server/ThirdParty/Google.Protobuf/RepeatedField.cs
  36. 11 0
      Server/ThirdParty/Google.Protobuf/RepeatedField.cs.meta
  37. 64 0
      Server/ThirdParty/Google.Protobuf/StreamExtensions.cs
  38. 11 0
      Server/ThirdParty/Google.Protobuf/StreamExtensions.cs.meta
  39. 104 0
      Server/ThirdParty/Google.Protobuf/WireFormat.cs
  40. 11 0
      Server/ThirdParty/Google.Protobuf/WireFormat.cs.meta
  41. 0 33
      Server/ThirdParty/KcpLib/KcpLib.csproj
  42. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/IO/BsonReader.cs
  43. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/IO/BsonStreamAdapter.cs
  44. 0 71
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/MongoDB.Bson.csproj
  45. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/ObjectModel/ObjectId.cs
  46. 0 23
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Properties/AssemblyInfo.cs
  47. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Serialization/BsonClassMap.cs
  48. 2 2
      Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Serialization/Serializers/CultureInfoSerializer.cs
  49. 0 28
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver.Core/Properties/AssemblyInfo.cs
  50. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/GridFSDownloadStream.cs
  51. 0 70
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj
  52. 0 23
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs
  53. 1 1
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver/Linq/MethodHelper.cs
  54. 0 74
      Server/ThirdParty/MongoDBDriver/MongoDB.Driver/MongoDB.Driver.csproj
  55. BIN=BIN
      Server/ThirdParty/Pathfinding.Ionic.Zip.Reduced.dll
  56. 49 0
      Server/ThirdParty/Server.ThirdParty.csproj
  57. 0 0
      Server/ThirdParty/libkcp.dylib
  58. 0 0
      Server/ThirdParty/libkcp.so
  59. 712 0
      Server/ThirdParty/protobuf-net/BclHelpers.cs
  60. 78 0
      Server/ThirdParty/protobuf-net/BufferExtension.cs
  61. 149 0
      Server/ThirdParty/protobuf-net/BufferPool.cs
  62. 33 0
      Server/ThirdParty/protobuf-net/CallbackAttributes.cs
  63. 1435 0
      Server/ThirdParty/protobuf-net/Compiler/CompilerContext.cs
  64. 7 0
      Server/ThirdParty/protobuf-net/Compiler/CompilerDelegates.cs
  65. 58 0
      Server/ThirdParty/protobuf-net/Compiler/Local.cs
  66. 49 0
      Server/ThirdParty/protobuf-net/DataFormat.cs
  67. 176 0
      Server/ThirdParty/protobuf-net/DiscriminatedUnion.Serializable.cs
  68. 416 0
      Server/ThirdParty/protobuf-net/DiscriminatedUnion.cs
  69. 284 0
      Server/ThirdParty/protobuf-net/Extensible.cs
  70. 118 0
      Server/ThirdParty/protobuf-net/ExtensibleUtil.cs
  71. BIN=BIN
      Server/ThirdParty/protobuf-net/GlobalSuppressions.cs
  72. 638 0
      Server/ThirdParty/protobuf-net/Helpers.cs
  73. 23 0
      Server/ThirdParty/protobuf-net/IExtensible.cs
  74. 58 0
      Server/ThirdParty/protobuf-net/IExtension.cs
  75. 13 0
      Server/ThirdParty/protobuf-net/IProtoInputT.cs
  76. 55 0
      Server/ThirdParty/protobuf-net/IProtoOutputT.cs
  77. 29 0
      Server/ThirdParty/protobuf-net/ImplicitFields.cs
  78. 44 0
      Server/ThirdParty/protobuf-net/KeyValuePairProxy.cs
  79. 108 0
      Server/ThirdParty/protobuf-net/Meta/AttributeMap.cs
  80. 267 0
      Server/ThirdParty/protobuf-net/Meta/BasicList.cs
  81. 110 0
      Server/ThirdParty/protobuf-net/Meta/CallbackSet.cs
  82. 2160 0
      Server/ThirdParty/protobuf-net/Meta/MetaType.cs
  83. 17 0
      Server/ThirdParty/protobuf-net/Meta/ProtoSyntax.cs
  84. 2006 0
      Server/ThirdParty/protobuf-net/Meta/RuntimeTypeModel.cs
  85. 97 0
      Server/ThirdParty/protobuf-net/Meta/SubType.cs
  86. 64 0
      Server/ThirdParty/protobuf-net/Meta/TypeFormatEventArgs.cs
  87. 45 0
      Server/ThirdParty/protobuf-net/Meta/TypeModel.InputOutput.cs
  88. 1696 0
      Server/ThirdParty/protobuf-net/Meta/TypeModel.cs
  89. 855 0
      Server/ThirdParty/protobuf-net/Meta/ValueMember.cs
  90. 190 0
      Server/ThirdParty/protobuf-net/NetObjectCache.cs
  91. 26 0
      Server/ThirdParty/protobuf-net/PrefixStyle.cs
  92. 175 0
      Server/ThirdParty/protobuf-net/ProtoContractAttribute.cs
  93. 13 0
      Server/ThirdParty/protobuf-net/ProtoConverterAttribute.cs
  94. 36 0
      Server/ThirdParty/protobuf-net/ProtoEnumAttribute.cs
  95. 30 0
      Server/ThirdParty/protobuf-net/ProtoException.cs
  96. 40 0
      Server/ThirdParty/protobuf-net/ProtoIgnoreAttribute.cs
  97. 60 0
      Server/ThirdParty/protobuf-net/ProtoIncludeAttribute.cs
  98. 29 0
      Server/ThirdParty/protobuf-net/ProtoMapAttribute.cs
  99. 228 0
      Server/ThirdParty/protobuf-net/ProtoMemberAttribute.cs
  100. 1436 0
      Server/ThirdParty/protobuf-net/ProtoReader.cs

+ 15 - 96
Client-Server.sln

@@ -7,16 +7,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{914C77
 EndProject
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}"
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}"
 EndProject
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ThirdParty", "ThirdParty", "{78640FA0-9F11-412D-A639-61F03D02407A}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.App", "Server\App\Server.App.csproj", "{69F1AA11-D05A-4843-B0DD-56B5E2E212A8}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.App", "Server\App\Server.App.csproj", "{69F1AA11-D05A-4843-B0DD-56B5E2E212A8}"
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Model", "Server\Model\Server.Model.csproj", "{EE38E3D2-3D2A-4061-BD50-AE8568126C7D}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Model", "Server\Model\Server.Model.csproj", "{EE38E3D2-3D2A-4061-BD50-AE8568126C7D}"
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Hotfix", "Server\Hotfix\Server.Hotfix.csproj", "{C305ED5B-8425-47A1-BEE1-85830CADC27A}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Hotfix", "Server\Hotfix\Server.Hotfix.csproj", "{C305ED5B-8425-47A1-BEE1-85830CADC27A}"
 EndProject
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Protobuf", "Server\ThirdParty\Google.Protobuf\Google.Protobuf.csproj", "{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Model", "Unity\Unity.Model.csproj", "{6396AEEA-8EF9-9740-4CEF-891CC997106C}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Model", "Unity\Unity.Model.csproj", "{6396AEEA-8EF9-9740-4CEF-891CC997106C}"
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Hotfix", "Unity\Unity.Hotfix.csproj", "{350246F3-F094-675F-855B-FB9B18C2B23E}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Hotfix", "Unity\Unity.Hotfix.csproj", "{350246F3-F094-675F-855B-FB9B18C2B23E}"
@@ -25,24 +21,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Editor", "Unity\Unity
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.ThirdParty", "Unity\Unity.ThirdParty.csproj", "{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.ThirdParty", "Unity\Unity.ThirdParty.csproj", "{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}"
 EndProject
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KcpLib", "Server\ThirdParty\KcpLib\KcpLib.csproj", "{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}"
-EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{4BC66C3C-D55F-4FAA-A2F5-29E8EB1797AE}"
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{4BC66C3C-D55F-4FAA-A2F5-29E8EB1797AE}"
 EndProject
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MongoDBDriver", "MongoDBDriver", "{14963D84-2A63-48CF-AE13-DCF71F1E13A0}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Bson", "Server\ThirdParty\MongoDBDriver\MongoDB.Bson\MongoDB.Bson.csproj", "{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver", "Server\ThirdParty\MongoDBDriver\MongoDB.Driver\MongoDB.Driver.csproj", "{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.Core", "Server\ThirdParty\MongoDBDriver\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj", "{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.GridFS", "Server\ThirdParty\MongoDBDriver\MongoDB.Driver.GridFS\MongoDB.Driver.GridFS.csproj", "{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.HotfixView", "Unity\Unity.HotfixView.csproj", "{D31345DE-9B88-525C-8E9C-744C99AE7509}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.HotfixView", "Unity\Unity.HotfixView.csproj", "{D31345DE-9B88-525C-8E9C-744C99AE7509}"
 EndProject
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.ModelView", "Unity\Unity.ModelView.csproj", "{2F8409C1-DA01-D464-DBF2-00C6449E459E}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.ModelView", "Unity\Unity.ModelView.csproj", "{2F8409C1-DA01-D464-DBF2-00C6449E459E}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.ThirdParty", "Server\ThirdParty\Server.ThirdParty.csproj", "{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -89,18 +75,6 @@ Global
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x64.Build.0 = Release|Any CPU
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x64.Build.0 = Release|Any CPU
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x86.ActiveCfg = Release|Any CPU
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x86.ActiveCfg = Release|Any CPU
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x86.Build.0 = Release|Any CPU
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A}.Release|x86.Build.0 = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|x64.Build.0 = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Debug|x86.Build.0 = Debug|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|x64.ActiveCfg = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|x64.Build.0 = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|x86.ActiveCfg = Release|Any CPU
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B}.Release|x86.Build.0 = Release|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x64.ActiveCfg = Debug|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x64.ActiveCfg = Debug|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x64.Build.0 = Debug|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x64.Build.0 = Debug|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x86.ActiveCfg = Debug|Any CPU
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -149,66 +123,6 @@ Global
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Release|x86.Build.0 = Release|Any CPU
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Release|x86.Build.0 = Release|Any CPU
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|x64.Build.0 = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Debug|x86.Build.0 = Debug|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|x64.ActiveCfg = Release|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|x64.Build.0 = Release|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|x86.ActiveCfg = Release|Any CPU
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725}.Release|x86.Build.0 = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|x64.Build.0 = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Debug|x86.Build.0 = Debug|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|x64.ActiveCfg = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|x64.Build.0 = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|x86.ActiveCfg = Release|Any CPU
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D}.Release|x86.Build.0 = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|x64.Build.0 = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Debug|x86.Build.0 = Debug|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|x64.ActiveCfg = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|x64.Build.0 = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|x86.ActiveCfg = Release|Any CPU
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7}.Release|x86.Build.0 = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|x64.Build.0 = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Debug|x86.Build.0 = Debug|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|Any CPU.Build.0 = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|x64.ActiveCfg = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|x64.Build.0 = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|x86.ActiveCfg = Release|Any CPU
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA}.Release|x86.Build.0 = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|x64.Build.0 = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Debug|x86.Build.0 = Debug|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x64.ActiveCfg = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x64.Build.0 = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x86.ActiveCfg = Release|Any CPU
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7}.Release|x86.Build.0 = Release|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|x64.ActiveCfg = Debug|Any CPU
 		{D31345DE-9B88-525C-8E9C-744C99AE7509}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -233,28 +147,33 @@ Global
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x64.Build.0 = Release|Any CPU
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x64.Build.0 = Release|Any CPU
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x86.ActiveCfg = Release|Any CPU
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x86.ActiveCfg = Release|Any CPU
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x86.Build.0 = Release|Any CPU
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E}.Release|x86.Build.0 = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|x64.Build.0 = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Debug|x86.Build.0 = Debug|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|x64.ActiveCfg = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|x64.Build.0 = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|x86.ActiveCfg = Release|Any CPU
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 	GlobalSection(NestedProjects) = preSolution
-		{78640FA0-9F11-412D-A639-61F03D02407A} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{69F1AA11-D05A-4843-B0DD-56B5E2E212A8} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{69F1AA11-D05A-4843-B0DD-56B5E2E212A8} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{EE38E3D2-3D2A-4061-BD50-AE8568126C7D} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{EE38E3D2-3D2A-4061-BD50-AE8568126C7D} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 		{C305ED5B-8425-47A1-BEE1-85830CADC27A} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
-		{BC06C46E-79F3-4AB6-800E-FA080CA54A0B} = {78640FA0-9F11-412D-A639-61F03D02407A}
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{6396AEEA-8EF9-9740-4CEF-891CC997106C} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{350246F3-F094-675F-855B-FB9B18C2B23E} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{350246F3-F094-675F-855B-FB9B18C2B23E} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{C17F48D3-964E-E97C-3D2E-966F7A6C6D93} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{C17F48D3-964E-E97C-3D2E-966F7A6C6D93} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{CFBC0A95-3456-3439-6B2E-60FDE0FE5EE1} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
-		{CD9683D9-0B4B-42D1-A1E0-1689BEEFD725} = {78640FA0-9F11-412D-A639-61F03D02407A}
-		{14963D84-2A63-48CF-AE13-DCF71F1E13A0} = {78640FA0-9F11-412D-A639-61F03D02407A}
-		{341CF1D2-DE35-4E61-AC7E-AA01A8ED452D} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
-		{B91E7D43-CCF1-49FC-ACF5-1C88A1C81FA7} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
-		{AAF14EDB-BB72-42A9-80AA-3E0E113AC4CA} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
-		{2ACC29FB-55CD-4307-B1D1-CC3922BC2CE7} = {14963D84-2A63-48CF-AE13-DCF71F1E13A0}
 		{D31345DE-9B88-525C-8E9C-744C99AE7509} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{D31345DE-9B88-525C-8E9C-744C99AE7509} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
 		{2F8409C1-DA01-D464-DBF2-00C6449E459E} = {914C77C9-212A-4DD0-8D9A-074620E77FAA}
+		{5CC830C4-3F2E-46AD-8160-3A0B032ACA5B} = {46008B91-6EC0-448B-8D1A-D8C6D2EEBEF4}
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {EABC01E3-3EB5-47EF-B46E-AAD8BB3585F1}
 		SolutionGuid = {EABC01E3-3EB5-47EF-B46E-AAD8BB3585F1}

+ 0 - 3
Server/Hotfix/Server.Hotfix.csproj

@@ -32,9 +32,6 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Model\Server.Model.csproj" />
     <ProjectReference Include="..\Model\Server.Model.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Bson\MongoDB.Bson.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Driver\MongoDB.Driver.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Folder Include="Helper" />
     <Folder Include="Helper" />

+ 0 - 0
Server/Model/Demo/ConfigPartial/StartProcessConfig.cs → Server/Model/ConfigPartial/StartProcessConfig.cs


+ 0 - 0
Server/Model/Demo/ConfigPartial/StartSceneConfig.cs → Server/Model/ConfigPartial/StartSceneConfig.cs


+ 4 - 5
Server/Model/Server.Model.csproj

@@ -179,6 +179,9 @@
     <Compile Include="..\..\Unity\Assets\Model\Module\Message\NetOuterComponent.cs">
     <Compile Include="..\..\Unity\Assets\Model\Module\Message\NetOuterComponent.cs">
       <Link>Module\Message\NetOuterComponent.cs</Link>
       <Link>Module\Message\NetOuterComponent.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\..\Unity\Assets\Model\Module\Message\Network\KCP\Kcp.cs">
+      <Link>Module\Message\Network\KCP\Kcp.cs</Link>
+    </Compile>
     <Compile Include="..\..\Unity\Assets\Model\Module\Message\Network\WebSocket\WChannel.cs">
     <Compile Include="..\..\Unity\Assets\Model\Module\Message\Network\WebSocket\WChannel.cs">
       <Link>Module\Message\Network\WebSocket\WChannel.cs</Link>
       <Link>Module\Message\Network\WebSocket\WChannel.cs</Link>
     </Compile>
     </Compile>
@@ -272,11 +275,7 @@
     <PackageReference Include="SharpZipLib" Version="1.0.0-alpha2" />
     <PackageReference Include="SharpZipLib" Version="1.0.0-alpha2" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\ThirdParty\Google.Protobuf\Google.Protobuf.csproj" />
-    <ProjectReference Include="..\ThirdParty\KcpLib\KcpLib.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Bson\MongoDB.Bson.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj" />
-    <ProjectReference Include="..\ThirdParty\MongoDBDriver\MongoDB.Driver\MongoDB.Driver.csproj" />
+    <ProjectReference Include="..\ThirdParty\Server.ThirdParty.csproj" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Folder Include="Base\RecyclableMemoryStream\" />
     <Folder Include="Base\RecyclableMemoryStream\" />

+ 79 - 0
Server/ThirdParty/Google.Protobuf/ByteArray.cs

@@ -0,0 +1,79 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Provides a utility routine to copy small arrays much more quickly than Buffer.BlockCopy
+    /// </summary>
+    internal static class ByteArray
+    {
+        /// <summary>
+        /// The threshold above which you should use Buffer.BlockCopy rather than ByteArray.Copy
+        /// </summary>
+        private const int CopyThreshold = 12;
+
+        /// <summary>
+        /// Determines which copy routine to use based on the number of bytes to be copied.
+        /// </summary>
+        internal static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
+        {
+            if (count > CopyThreshold)
+            {
+                Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);
+            }
+            else
+            {
+                int stop = srcOffset + count;
+                for (int i = srcOffset; i < stop; i++)
+                {
+                    dst[dstOffset++] = src[i];
+                }
+            }
+        }
+
+        /// <summary>
+        /// Reverses the order of bytes in the array
+        /// </summary>
+        internal static void Reverse(byte[] bytes)
+        {
+            for (int first = 0, last = bytes.Length - 1; first < last; first++, last--)
+            {
+                byte temp = bytes[first];
+                bytes[first] = bytes[last];
+                bytes[last] = temp;
+            }
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/ByteArray.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cc6f6a4c966390c4da2d54d15368bd0d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 378 - 0
Server/ThirdParty/Google.Protobuf/ByteString.cs

@@ -0,0 +1,378 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Google.Protobuf.Compatibility;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Immutable array of bytes.
+    /// </summary>
+    public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString>
+    {
+        private static readonly ByteString empty = new ByteString(new byte[0]);
+
+        public byte[] bytes;
+
+        /// <summary>
+        /// Unsafe operations that can cause IO Failure and/or other catestrophic side-effects.
+        /// </summary>
+        public static class Unsafe
+        {
+            /// <summary>
+            /// Constructs a new ByteString from the given byte array. The array is
+            /// *not* copied, and must not be modified after this constructor is called.
+            /// </summary>
+            public static ByteString FromBytes(byte[] bytes)
+            {
+                return new ByteString(bytes);
+            }
+
+            /// <summary>
+            /// Provides direct, unrestricted access to the bytes contained in this instance.
+            /// You must not modify or resize the byte array returned by this method.
+            /// </summary>
+            public static byte[] GetBuffer(ByteString bytes)
+            {
+                return bytes.bytes;
+            }
+        }
+
+        /// <summary>
+        /// Internal use only.  Ensure that the provided array is not mutated and belongs to this instance.
+        /// </summary>
+        internal static ByteString AttachBytes(byte[] bytes)
+        {
+            return new ByteString(bytes);
+        }
+        
+        public ByteString()
+        {}
+
+        public ByteString(List<byte> list)
+        {
+            this.bytes = list.ToArray();
+        }
+        /// <summary>
+        /// Constructs a new ByteString from the given byte array. The array is
+        /// *not* copied, and must not be modified after this constructor is called.
+        /// </summary>
+        public ByteString(byte[] bytes)
+        {
+            this.bytes = bytes;
+        }
+
+        /// <summary>
+        /// Returns an empty ByteString.
+        /// </summary>
+        public static ByteString Empty
+        {
+            get { return empty; }
+        }
+
+        /// <summary>
+        /// Returns the length of this ByteString in bytes.
+        /// </summary>
+        [BsonIgnore]
+        public int Length
+        {
+            get { return bytes.Length; }
+        }
+
+        /// <summary>
+        /// Returns <c>true</c> if this byte string is empty, <c>false</c> otherwise.
+        /// </summary>
+        [BsonIgnore]
+        public bool IsEmpty
+        {
+            get { return Length == 0; }
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a byte array.
+        /// </summary>
+        /// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks>
+        /// <returns>A byte array with the same data as this <c>ByteString</c>.</returns>
+        public byte[] ToByteArray()
+        {
+            return (byte[]) bytes.Clone();
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a standard base64 representation.
+        /// </summary>
+        /// <returns>A base64 representation of this <c>ByteString</c>.</returns>
+        public string ToBase64()
+        {
+            return Convert.ToBase64String(bytes);
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from the Base64 Encoded String.
+        /// </summary>
+        public static ByteString FromBase64(string bytes)
+        {
+            // By handling the empty string explicitly, we not only optimize but we fix a
+            // problem on CF 2.0. See issue 61 for details.
+            return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes));
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString"/> from data in the given stream, synchronously.
+        /// </summary>
+        /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position
+        /// at the start of the call.</remarks>
+        /// <param name="stream">The stream to copy into a ByteString.</param>
+        /// <returns>A ByteString with content read from the given stream.</returns>
+        public static ByteString FromStream(Stream stream)
+        {
+            ProtoPreconditions.CheckNotNull(stream, "stream");
+            int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
+            var memoryStream = new MemoryStream(capacity);
+            stream.CopyTo(memoryStream);
+
+            // Avoid an extra copy if we can.
+            byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
+
+            return AttachBytes(bytes);
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from the given array. The contents
+        /// are copied, so further modifications to the array will not
+        /// be reflected in the returned ByteString.
+        /// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form
+        /// which is primarily useful for testing.
+        /// </summary>
+        public static ByteString CopyFrom(params byte[] bytes)
+        {
+            return new ByteString((byte[]) bytes.Clone());
+        }
+
+        /// <summary>
+        /// Constructs a <see cref="ByteString" /> from a portion of a byte array.
+        /// </summary>
+        public static ByteString CopyFrom(byte[] bytes, int offset, int count)
+        {
+            byte[] portion = new byte[count];
+            ByteArray.Copy(bytes, offset, portion, 0, count);
+            return new ByteString(portion);
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="ByteString" /> by encoding the specified text with
+        /// the given encoding.
+        /// </summary>
+        public static ByteString CopyFrom(string text, Encoding encoding)
+        {
+            return new ByteString(encoding.GetBytes(text));
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="ByteString" /> by encoding the specified text in UTF-8.
+        /// </summary>
+        public static ByteString CopyFromUtf8(string text)
+        {
+            return CopyFrom(text, Encoding.UTF8);
+        }
+
+        /// <summary>
+        /// Retuns the byte at the given index.
+        /// </summary>
+        [BsonIgnore]
+        public byte this[int index]
+        {
+            get { return bytes[index]; }
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a string by applying the given encoding.
+        /// </summary>
+        /// <remarks>
+        /// This method should only be used to convert binary data which was the result of encoding
+        /// text with the given encoding.
+        /// </remarks>
+        /// <param name="encoding">The encoding to use to decode the binary data into text.</param>
+        /// <returns>The result of decoding the binary data with the given decoding.</returns>
+        public string ToString(Encoding encoding)
+        {
+            return encoding.GetString(bytes, 0, bytes.Length);
+        }
+
+        /// <summary>
+        /// Converts this <see cref="ByteString"/> into a string by applying the UTF-8 encoding.
+        /// </summary>
+        /// <remarks>
+        /// This method should only be used to convert binary data which was the result of encoding
+        /// text with UTF-8.
+        /// </remarks>
+        /// <returns>The result of decoding the binary data with the given decoding.</returns>
+        public string ToStringUtf8()
+        {
+            return ToString(Encoding.UTF8);
+        }
+
+        /// <summary>
+        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.
+        /// </summary>
+        /// <returns>An iterator over the bytes in this object.</returns>
+        public IEnumerator<byte> GetEnumerator()
+        {
+            return ((IEnumerable<byte>) bytes).GetEnumerator();
+        }
+
+        /// <summary>
+        /// Returns an iterator over the bytes in this <see cref="ByteString"/>.
+        /// </summary>
+        /// <returns>An iterator over the bytes in this object.</returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Creates a CodedInputStream from this ByteString's data.
+        /// </summary>
+        public CodedInputStream CreateCodedInput()
+        {
+            // We trust CodedInputStream not to reveal the provided byte array or modify it
+            return new CodedInputStream(bytes);
+        }
+
+        /// <summary>
+        /// Compares two byte strings for equality.
+        /// </summary>
+        /// <param name="lhs">The first byte string to compare.</param>
+        /// <param name="rhs">The second byte string to compare.</param>
+        /// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns>
+        public static bool operator ==(ByteString lhs, ByteString rhs)
+        {
+            if (ReferenceEquals(lhs, rhs))
+            {
+                return true;
+            }
+            if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
+            {
+                return false;
+            }
+            if (lhs.bytes.Length != rhs.bytes.Length)
+            {
+                return false;
+            }
+            for (int i = 0; i < lhs.Length; i++)
+            {
+                if (rhs.bytes[i] != lhs.bytes[i])
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Compares two byte strings for inequality.
+        /// </summary>
+        /// <param name="lhs">The first byte string to compare.</param>
+        /// <param name="rhs">The second byte string to compare.</param>
+        /// <returns><c>false</c> if the byte strings are equal; true otherwise.</returns>
+        public static bool operator !=(ByteString lhs, ByteString rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        /// <summary>
+        /// Compares this byte string with another object.
+        /// </summary>
+        /// <param name="obj">The object to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns>
+        public override bool Equals(object obj)
+        {
+            return this == (obj as ByteString);
+        }
+
+        /// <summary>
+        /// Returns a hash code for this object. Two equal byte strings
+        /// will return the same hash code.
+        /// </summary>
+        /// <returns>A hash code for this object.</returns>
+        public override int GetHashCode()
+        {
+            int ret = 23;
+            foreach (byte b in bytes)
+            {
+                ret = (ret * 31) + b;
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// Compares this byte string with another.
+        /// </summary>
+        /// <param name="other">The <see cref="ByteString"/> to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal byte string; <c>false</c> otherwise.</returns>
+        public bool Equals(ByteString other)
+        {
+            return this == other;
+        }
+
+        /// <summary>
+        /// Used internally by CodedOutputStream to avoid creating a copy for the write
+        /// </summary>
+        internal void WriteRawBytesTo(CodedOutputStream outputStream)
+        {
+            outputStream.WriteRawBytes(bytes, 0, bytes.Length);
+        }
+
+        /// <summary>
+        /// Copies the entire byte array to the destination array provided at the offset specified.
+        /// </summary>
+        public void CopyTo(byte[] array, int position)
+        {
+            ByteArray.Copy(bytes, 0, array, position, bytes.Length);
+        }
+
+        /// <summary>
+        /// Writes the entire byte array to the provided stream
+        /// </summary>
+        public void WriteTo(Stream outputStream)
+        {
+            outputStream.Write(bytes, 0, bytes.Length);
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/ByteString.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0495e4b3ba1ec7f498d877a234323a35
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1293 - 0
Server/ThirdParty/Google.Protobuf/CodedInputStream.cs

@@ -0,0 +1,1293 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Reads and decodes protocol message fields.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// This class is generally used by generated code to read appropriate
+    /// primitives from the stream. It effectively encapsulates the lowest
+    /// levels of protocol buffer format.
+    /// </para>
+    /// <para>
+    /// Repeated fields and map fields are not handled by this class; use <see cref="RepeatedField{T}"/>
+    /// and <see cref="MapField{TKey, TValue}"/> to serialize such fields.
+    /// </para>
+    /// </remarks>
+    public sealed class CodedInputStream : IDisposable
+    {
+        /// <summary>
+        /// Whether to leave the underlying stream open when disposing of this stream.
+        /// This is always true when there's no stream.
+        /// </summary>
+        private bool leaveOpen;
+
+        /// <summary>
+        /// Buffer of data read from the stream or provided at construction time.
+        /// </summary>
+        private byte[] buffer;
+
+        /// <summary>
+        /// The index of the buffer at which we need to refill from the stream (if there is one).
+        /// </summary>
+        private int bufferSize;
+
+        private int bufferSizeAfterLimit = 0;
+        /// <summary>
+        /// The position within the current buffer (i.e. the next byte to read)
+        /// </summary>
+        private int bufferPos = 0;
+
+        /// <summary>
+        /// The stream to read further input from, or null if the byte array buffer was provided
+        /// directly on construction, with no further data available.
+        /// </summary>
+        private readonly Stream input;
+
+        /// <summary>
+        /// The last tag we read. 0 indicates we've read to the end of the stream
+        /// (or haven't read anything yet).
+        /// </summary>
+        private uint lastTag = 0;
+
+        /// <summary>
+        /// The next tag, used to store the value read by PeekTag.
+        /// </summary>
+        private uint nextTag = 0;
+        private bool hasNextTag = false;
+
+        internal const int DefaultRecursionLimit = 64;
+        internal const int DefaultSizeLimit = 64 << 20; // 64MB
+        internal const int BufferSize = 4096;
+
+        /// <summary>
+        /// The total number of bytes read before the current buffer. The
+        /// total bytes read up to the current position can be computed as
+        /// totalBytesRetired + bufferPos.
+        /// </summary>
+        private int totalBytesRetired = 0;
+
+        /// <summary>
+        /// The absolute position of the end of the current message.
+        /// </summary> 
+        private int currentLimit = int.MaxValue;
+
+        private int recursionDepth = 0;
+
+        private int recursionLimit;
+        private int sizeLimit;
+
+        #region Construction
+        // Note that the checks are performed such that we don't end up checking obviously-valid things
+        // like non-null references for arrays we've just created.
+
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given byte array.
+        /// </summary>
+        public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true)
+        {            
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice.
+        /// </summary>
+        public CodedInputStream(byte[] buffer, int offset, int length)
+            : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true)
+        {            
+            if (offset < 0 || offset > buffer.Length)
+            {
+                throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
+            }
+            if (length < 0 || offset + length > buffer.Length)
+            {
+                throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
+            }
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed
+        /// when the returned object is disposed.
+        /// </summary>
+        /// <param name="input">The stream to read from.</param>
+        public CodedInputStream(Stream input) : this(input, false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream.
+        /// </summary>
+        /// <param name="input">The stream to read from.</param>
+        /// <param name="leaveOpen"><c>true</c> to leave <paramref name="input"/> open when the returned
+        /// <c cref="CodedInputStream"/> is disposed; <c>false</c> to dispose of the given stream when the
+        /// returned object is disposed.</param>
+        public CodedInputStream(Stream input, bool leaveOpen)
+            : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen)
+        {
+        }
+        
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given
+        /// stream and buffer, using the default limits.
+        /// </summary>
+        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)
+        {
+            this.input = input;
+            this.buffer = buffer;
+            this.bufferPos = bufferPos;
+            this.bufferSize = bufferSize;
+            this.sizeLimit = DefaultSizeLimit;
+            this.recursionLimit = DefaultRecursionLimit;
+            this.leaveOpen = leaveOpen;
+        }
+        
+        public void Reset(byte[] buf, int offset, int length)
+        {
+            this.buffer = buf;
+            this.bufferPos = offset;
+            this.bufferSize = length;
+            this.sizeLimit = DefaultSizeLimit;
+            this.recursionLimit = DefaultRecursionLimit;
+            this.leaveOpen = true;
+
+            bufferSizeAfterLimit = 0;
+            lastTag = 0;
+            nextTag = 0;
+            hasNextTag = false;
+            totalBytesRetired = 0;
+            currentLimit = int.MaxValue;
+            sizeLimit = 0;
+            recursionDepth = 0;
+        }
+
+        /// <summary>
+        /// Creates a new CodedInputStream reading data from the given
+        /// stream and buffer, using the specified limits.
+        /// </summary>
+        /// <remarks>
+        /// This chains to the version with the default limits instead of vice versa to avoid
+        /// having to check that the default values are valid every time.
+        /// </remarks>
+        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)
+            : this(input, buffer, bufferPos, bufferSize, leaveOpen)
+        {
+            if (sizeLimit <= 0)
+            {
+                throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");
+            }
+            if (recursionLimit <= 0)
+            {
+                throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");
+            }
+            this.sizeLimit = sizeLimit;
+            this.recursionLimit = recursionLimit;
+        }
+        #endregion
+
+        /// <summary>
+        /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading
+        /// from an input stream.
+        /// </summary>
+        /// <remarks>
+        /// This method exists separately from the constructor to reduce the number of constructor overloads.
+        /// It is likely to be used considerably less frequently than the constructors, as the default limits
+        /// are suitable for most use cases.
+        /// </remarks>
+        /// <param name="input">The input stream to read from</param>
+        /// <param name="sizeLimit">The total limit of data to read from the stream.</param>
+        /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param>
+        /// <returns>A <c>CodedInputStream</c> reading from <paramref name="input"/> with the specified size
+        /// and recursion limits.</returns>
+        public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)
+        {
+            // Note: we may want an overload accepting leaveOpen
+            return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit, false);
+        }
+
+        /// <summary>
+        /// Returns the current position in the input stream, or the position in the input buffer
+        /// </summary>
+        public long Position 
+        {
+            get
+            {
+                if (input != null)
+                {
+                    return input.Position - ((bufferSize + bufferSizeAfterLimit) - bufferPos);
+                }
+                return bufferPos;
+            }
+        }
+
+        /// <summary>
+        /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
+        /// the end of the stream.
+        /// </summary>
+        internal uint LastTag { get { return lastTag; } }
+
+        /// <summary>
+        /// Returns the size limit for this stream.
+        /// </summary>
+        /// <remarks>
+        /// This limit is applied when reading from the underlying stream, as a sanity check. It is
+        /// not applied when reading from a byte array data source without an underlying stream.
+        /// The default value is 64MB.
+        /// </remarks>
+        /// <value>
+        /// The size limit.
+        /// </value>
+        public int SizeLimit { get { return sizeLimit; } }
+
+        /// <summary>
+        /// Returns the recursion limit for this stream. This limit is applied whilst reading messages,
+        /// to avoid maliciously-recursive data.
+        /// </summary>
+        /// <remarks>
+        /// The default limit is 64.
+        /// </remarks>
+        /// <value>
+        /// The recursion limit for this stream.
+        /// </value>
+        public int RecursionLimit { get { return recursionLimit; } }
+
+        /// <summary>
+        /// Disposes of this instance, potentially closing any underlying stream.
+        /// </summary>
+        /// <remarks>
+        /// As there is no flushing to perform here, disposing of a <see cref="CodedInputStream"/> which
+        /// was constructed with the <c>leaveOpen</c> option parameter set to <c>true</c> (or one which
+        /// was constructed to read from a byte array) has no effect.
+        /// </remarks>
+        public void Dispose()
+        {
+            if (!leaveOpen)
+            {
+                input.Dispose();
+            }
+        }
+
+        #region Validation
+        /// <summary>
+        /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
+        /// we've reached the end of the stream when we expected to.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">The 
+        /// tag read was not the one specified</exception>
+        internal void CheckReadEndOfStreamTag()
+        {
+            if (lastTag != 0)
+            {
+                throw InvalidProtocolBufferException.MoreDataAvailable();
+            }
+        }
+        #endregion
+
+        #region Reading of tags etc
+
+        /// <summary>
+        /// Peeks at the next field tag. This is like calling <see cref="ReadTag"/>, but the
+        /// tag is not consumed. (So a subsequent call to <see cref="ReadTag"/> will return the
+        /// same value.)
+        /// </summary>
+        public uint PeekTag()
+        {
+            if (hasNextTag)
+            {
+                return nextTag;
+            }
+
+            uint savedLast = lastTag;
+            nextTag = ReadTag();
+            hasNextTag = true;
+            lastTag = savedLast; // Undo the side effect of ReadTag
+            return nextTag;
+        }
+
+        /// <summary>
+        /// Reads a field tag, returning the tag of 0 for "end of stream".
+        /// </summary>
+        /// <remarks>
+        /// If this method returns 0, it doesn't necessarily mean the end of all
+        /// the data in this CodedInputStream; it may be the end of the logical stream
+        /// for an embedded message, for example.
+        /// </remarks>
+        /// <returns>The next field tag, or 0 for end of stream. (0 is never a valid tag.)</returns>
+        public uint ReadTag()
+        {
+            if (hasNextTag)
+            {
+                lastTag = nextTag;
+                hasNextTag = false;
+                return lastTag;
+            }
+
+            // Optimize for the incredibly common case of having at least two bytes left in the buffer,
+            // and those two bytes being enough to get the tag. This will be true for fields up to 4095.
+            if (bufferPos + 2 <= bufferSize)
+            {
+                int tmp = buffer[bufferPos++];
+                if (tmp < 128)
+                {
+                    lastTag = (uint)tmp;
+                }
+                else
+                {
+                    int result = tmp & 0x7f;
+                    if ((tmp = buffer[bufferPos++]) < 128)
+                    {
+                        result |= tmp << 7;
+                        lastTag = (uint) result;
+                    }
+                    else
+                    {
+                        // Nope, rewind and go the potentially slow route.
+                        bufferPos -= 2;
+                        lastTag = ReadRawVarint32();
+                    }
+                }
+            }
+            else
+            {
+                if (IsAtEnd)
+                {
+                    lastTag = 0;
+                    return 0; // This is the only case in which we return 0.
+                }
+
+                lastTag = ReadRawVarint32();
+            }
+            if (lastTag == 0)
+            {
+                // If we actually read zero, that's not a valid tag.
+                throw InvalidProtocolBufferException.InvalidTag();
+            }
+            return lastTag;
+        }
+
+        /// <summary>
+        /// Skips the data for the field with the tag we've just read.
+        /// This should be called directly after <see cref="ReadTag"/>, when
+        /// the caller wishes to skip an unknown field.
+        /// </summary>
+        /// <remarks>
+        /// This method throws <see cref="InvalidProtocolBufferException"/> if the last-read tag was an end-group tag.
+        /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the
+        /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly
+        /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag.
+        /// </remarks>
+        /// <exception cref="InvalidProtocolBufferException">The last tag was an end-group tag</exception>
+        /// <exception cref="InvalidOperationException">The last read operation read to the end of the logical stream</exception>
+        public void SkipLastField()
+        {
+            if (lastTag == 0)
+            {
+                throw new InvalidOperationException("SkipLastField cannot be called at the end of a stream");
+            }
+            switch (WireFormat.GetTagWireType(lastTag))
+            {
+                case WireFormat.WireType.StartGroup:
+                    SkipGroup(lastTag);
+                    break;
+                case WireFormat.WireType.EndGroup:
+                    throw new InvalidProtocolBufferException("SkipLastField called on an end-group tag, indicating that the corresponding start-group was missing");
+                case WireFormat.WireType.Fixed32:
+                    ReadFixed32();
+                    break;
+                case WireFormat.WireType.Fixed64:
+                    ReadFixed64();
+                    break;
+                case WireFormat.WireType.LengthDelimited:
+                    var length = ReadLength();
+                    SkipRawBytes(length);
+                    break;
+                case WireFormat.WireType.Varint:
+                    ReadRawVarint32();
+                    break;
+            }
+        }
+
+        private void SkipGroup(uint startGroupTag)
+        {
+            // Note: Currently we expect this to be the way that groups are read. We could put the recursion
+            // depth changes into the ReadTag method instead, potentially...
+            recursionDepth++;
+            if (recursionDepth >= recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            uint tag;
+            while (true)
+            {
+                tag = ReadTag();
+                if (tag == 0)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                // Can't call SkipLastField for this case- that would throw.
+                if (WireFormat.GetTagWireType(tag) == WireFormat.WireType.EndGroup)
+                {
+                    break;
+                }
+                // This recursion will allow us to handle nested groups.
+                SkipLastField();
+            }
+            int startField = WireFormat.GetTagFieldNumber(startGroupTag);
+            int endField = WireFormat.GetTagFieldNumber(tag);
+            if (startField != endField)
+            {
+                throw new InvalidProtocolBufferException("Mismatched end-group tag. Started with field " + startField + "; ended with field " + endField);
+            }
+            recursionDepth--;
+        }
+
+        /// <summary>
+        /// Reads a double field from the stream.
+        /// </summary>
+        public double ReadDouble()
+        {
+            return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
+        }
+
+        /// <summary>
+        /// Reads a float field from the stream.
+        /// </summary>
+        public float ReadFloat()
+        {
+            if (BitConverter.IsLittleEndian && 4 <= bufferSize - bufferPos)
+            {
+                float ret = BitConverter.ToSingle(buffer, bufferPos);
+                bufferPos += 4;
+                return ret;
+            }
+            else
+            {
+                byte[] rawBytes = ReadRawBytes(4);
+                if (!BitConverter.IsLittleEndian)
+                {
+                    ByteArray.Reverse(rawBytes);
+                }
+                return BitConverter.ToSingle(rawBytes, 0);
+            }
+        }
+
+        /// <summary>
+        /// Reads a uint64 field from the stream.
+        /// </summary>
+        public ulong ReadUInt64()
+        {
+            return ReadRawVarint64();
+        }
+
+        /// <summary>
+        /// Reads an int64 field from the stream.
+        /// </summary>
+        public long ReadInt64()
+        {
+            return (long) ReadRawVarint64();
+        }
+
+        /// <summary>
+        /// Reads an int32 field from the stream.
+        /// </summary>
+        public int ReadInt32()
+        {
+            return (int) ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads a fixed64 field from the stream.
+        /// </summary>
+        public ulong ReadFixed64()
+        {
+            return ReadRawLittleEndian64();
+        }
+
+        /// <summary>
+        /// Reads a fixed32 field from the stream.
+        /// </summary>
+        public uint ReadFixed32()
+        {
+            return ReadRawLittleEndian32();
+        }
+
+        /// <summary>
+        /// Reads a bool field from the stream.
+        /// </summary>
+        public bool ReadBool()
+        {
+            return ReadRawVarint32() != 0;
+        }
+
+        /// <summary>
+        /// Reads a string field from the stream.
+        /// </summary>
+        public string ReadString()
+        {
+            int length = ReadLength();
+            // No need to read any data for an empty string.
+            if (length == 0)
+            {
+                return "";
+            }
+            if (length <= bufferSize - bufferPos)
+            {
+                // Fast path:  We already have the bytes in a contiguous buffer, so
+                //   just copy directly from it.
+                String result = CodedOutputStream.Utf8Encoding.GetString(buffer, bufferPos, length);
+                bufferPos += length;
+                return result;
+            }
+            // Slow path: Build a byte array first then copy it.
+            return CodedOutputStream.Utf8Encoding.GetString(ReadRawBytes(length), 0, length);
+        }
+
+        /// <summary>
+        /// Reads an embedded message field value from the stream.
+        /// </summary>   
+        public void ReadMessage(IMessage builder)
+        {
+            int length = ReadLength();
+            if (recursionDepth >= recursionLimit)
+            {
+                throw InvalidProtocolBufferException.RecursionLimitExceeded();
+            }
+            int oldLimit = PushLimit(length);
+            ++recursionDepth;
+            builder.MergeFrom(this);
+            CheckReadEndOfStreamTag();
+            // Check that we've read exactly as much data as expected.
+            if (!ReachedLimit)
+            {
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+            --recursionDepth;
+            PopLimit(oldLimit);
+        }
+
+        /// <summary>
+        /// Reads a bytes field value from the stream.
+        /// </summary>   
+        public ByteString ReadBytes()
+        {
+            int length = ReadLength();
+            if (length <= bufferSize - bufferPos && length > 0)
+            {
+                // Fast path:  We already have the bytes in a contiguous buffer, so
+                //   just copy directly from it.
+                ByteString result = ByteString.CopyFrom(buffer, bufferPos, length);
+                bufferPos += length;
+                return result;
+            }
+            else
+            {
+                // Slow path:  Build a byte array and attach it to a new ByteString.
+                return ByteString.AttachBytes(ReadRawBytes(length));
+            }
+        }
+
+        /// <summary>
+        /// Reads a uint32 field value from the stream.
+        /// </summary>   
+        public uint ReadUInt32()
+        {
+            return ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads an enum field value from the stream.
+        /// </summary>   
+        public int ReadEnum()
+        {
+            // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
+            return (int) ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Reads an sfixed32 field value from the stream.
+        /// </summary>   
+        public int ReadSFixed32()
+        {
+            return (int) ReadRawLittleEndian32();
+        }
+
+        /// <summary>
+        /// Reads an sfixed64 field value from the stream.
+        /// </summary>   
+        public long ReadSFixed64()
+        {
+            return (long) ReadRawLittleEndian64();
+        }
+
+        /// <summary>
+        /// Reads an sint32 field value from the stream.
+        /// </summary>   
+        public int ReadSInt32()
+        {
+            return DecodeZigZag32(ReadRawVarint32());
+        }
+
+        /// <summary>
+        /// Reads an sint64 field value from the stream.
+        /// </summary>   
+        public long ReadSInt64()
+        {
+            return DecodeZigZag64(ReadRawVarint64());
+        }
+
+        /// <summary>
+        /// Reads a length for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This is internally just reading a varint, but this method exists
+        /// to make the calling code clearer.
+        /// </remarks>
+        public int ReadLength()
+        {
+            return (int) ReadRawVarint32();
+        }
+
+        /// <summary>
+        /// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
+        /// the tag is consumed and the method returns <c>true</c>; otherwise, the
+        /// stream is left in the original position and the method returns <c>false</c>.
+        /// </summary>
+        public bool MaybeConsumeTag(uint tag)
+        {
+            if (PeekTag() == tag)
+            {
+                hasNextTag = false;
+                return true;
+            }
+            return false;
+        }
+
+        #endregion
+
+        #region Underlying reading primitives
+
+        /// <summary>
+        /// Same code as ReadRawVarint32, but read each byte individually, checking for
+        /// buffer overflow.
+        /// </summary>
+        private uint SlowReadRawVarint32()
+        {
+            int tmp = ReadRawByte();
+            if (tmp < 128)
+            {
+                return (uint) tmp;
+            }
+            int result = tmp & 0x7f;
+            if ((tmp = ReadRawByte()) < 128)
+            {
+                result |= tmp << 7;
+            }
+            else
+            {
+                result |= (tmp & 0x7f) << 7;
+                if ((tmp = ReadRawByte()) < 128)
+                {
+                    result |= tmp << 14;
+                }
+                else
+                {
+                    result |= (tmp & 0x7f) << 14;
+                    if ((tmp = ReadRawByte()) < 128)
+                    {
+                        result |= tmp << 21;
+                    }
+                    else
+                    {
+                        result |= (tmp & 0x7f) << 21;
+                        result |= (tmp = ReadRawByte()) << 28;
+                        if (tmp >= 128)
+                        {
+                            // Discard upper 32 bits.
+                            for (int i = 0; i < 5; i++)
+                            {
+                                if (ReadRawByte() < 128)
+                                {
+                                    return (uint) result;
+                                }
+                            }
+                            throw InvalidProtocolBufferException.MalformedVarint();
+                        }
+                    }
+                }
+            }
+            return (uint) result;
+        }
+
+        /// <summary>
+        /// Reads a raw Varint from the stream.  If larger than 32 bits, discard the upper bits.
+        /// This method is optimised for the case where we've got lots of data in the buffer.
+        /// That means we can check the size just once, then just read directly from the buffer
+        /// without constant rechecking of the buffer length.
+        /// </summary>
+        internal uint ReadRawVarint32()
+        {
+            if (bufferPos + 5 > bufferSize)
+            {
+                return SlowReadRawVarint32();
+            }
+
+            int tmp = buffer[bufferPos++];
+            if (tmp < 128)
+            {
+                return (uint) tmp;
+            }
+            int result = tmp & 0x7f;
+            if ((tmp = buffer[bufferPos++]) < 128)
+            {
+                result |= tmp << 7;
+            }
+            else
+            {
+                result |= (tmp & 0x7f) << 7;
+                if ((tmp = buffer[bufferPos++]) < 128)
+                {
+                    result |= tmp << 14;
+                }
+                else
+                {
+                    result |= (tmp & 0x7f) << 14;
+                    if ((tmp = buffer[bufferPos++]) < 128)
+                    {
+                        result |= tmp << 21;
+                    }
+                    else
+                    {
+                        result |= (tmp & 0x7f) << 21;
+                        result |= (tmp = buffer[bufferPos++]) << 28;
+                        if (tmp >= 128)
+                        {
+                            // Discard upper 32 bits.
+                            // Note that this has to use ReadRawByte() as we only ensure we've
+                            // got at least 5 bytes at the start of the method. This lets us
+                            // use the fast path in more cases, and we rarely hit this section of code.
+                            for (int i = 0; i < 5; i++)
+                            {
+                                if (ReadRawByte() < 128)
+                                {
+                                    return (uint) result;
+                                }
+                            }
+                            throw InvalidProtocolBufferException.MalformedVarint();
+                        }
+                    }
+                }
+            }
+            return (uint) result;
+        }
+
+        /// <summary>
+        /// Reads a varint from the input one byte at a time, so that it does not
+        /// read any bytes after the end of the varint. If you simply wrapped the
+        /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
+        /// then you would probably end up reading past the end of the varint since
+        /// CodedInputStream buffers its input.
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        internal static uint ReadRawVarint32(Stream input)
+        {
+            int result = 0;
+            int offset = 0;
+            for (; offset < 32; offset += 7)
+            {
+                int b = input.ReadByte();
+                if (b == -1)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                result |= (b & 0x7f) << offset;
+                if ((b & 0x80) == 0)
+                {
+                    return (uint) result;
+                }
+            }
+            // Keep reading up to 64 bits.
+            for (; offset < 64; offset += 7)
+            {
+                int b = input.ReadByte();
+                if (b == -1)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                if ((b & 0x80) == 0)
+                {
+                    return (uint) result;
+                }
+            }
+            throw InvalidProtocolBufferException.MalformedVarint();
+        }
+
+        /// <summary>
+        /// Reads a raw varint from the stream.
+        /// </summary>
+        internal ulong ReadRawVarint64()
+        {
+            int shift = 0;
+            ulong result = 0;
+            while (shift < 64)
+            {
+                byte b = ReadRawByte();
+                result |= (ulong) (b & 0x7F) << shift;
+                if ((b & 0x80) == 0)
+                {
+                    return result;
+                }
+                shift += 7;
+            }
+            throw InvalidProtocolBufferException.MalformedVarint();
+        }
+
+        /// <summary>
+        /// Reads a 32-bit little-endian integer from the stream.
+        /// </summary>
+        internal uint ReadRawLittleEndian32()
+        {
+            uint b1 = ReadRawByte();
+            uint b2 = ReadRawByte();
+            uint b3 = ReadRawByte();
+            uint b4 = ReadRawByte();
+            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+        }
+
+        /// <summary>
+        /// Reads a 64-bit little-endian integer from the stream.
+        /// </summary>
+        internal ulong ReadRawLittleEndian64()
+        {
+            ulong b1 = ReadRawByte();
+            ulong b2 = ReadRawByte();
+            ulong b3 = ReadRawByte();
+            ulong b4 = ReadRawByte();
+            ulong b5 = ReadRawByte();
+            ulong b6 = ReadRawByte();
+            ulong b7 = ReadRawByte();
+            ulong b8 = ReadRawByte();
+            return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24)
+                   | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56);
+        }
+
+        /// <summary>
+        /// Decode a 32-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        internal static int DecodeZigZag32(uint n)
+        {
+            return (int)(n >> 1) ^ -(int)(n & 1);
+        }
+
+        /// <summary>
+        /// Decode a 32-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        internal static long DecodeZigZag64(ulong n)
+        {
+            return (long)(n >> 1) ^ -(long)(n & 1);
+        }
+        #endregion
+
+        #region Internal reading and buffer management
+
+        /// <summary>
+        /// Sets currentLimit to (current position) + byteLimit. This is called
+        /// when descending into a length-delimited embedded message. The previous
+        /// limit is returned.
+        /// </summary>
+        /// <returns>The old limit.</returns>
+        internal int PushLimit(int byteLimit)
+        {
+            if (byteLimit < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+            byteLimit += totalBytesRetired + bufferPos;
+            int oldLimit = currentLimit;
+            if (byteLimit > oldLimit)
+            {
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+            currentLimit = byteLimit;
+
+            RecomputeBufferSizeAfterLimit();
+
+            return oldLimit;
+        }
+
+        private void RecomputeBufferSizeAfterLimit()
+        {
+            bufferSize += bufferSizeAfterLimit;
+            int bufferEnd = totalBytesRetired + bufferSize;
+            if (bufferEnd > currentLimit)
+            {
+                // Limit is in current buffer.
+                bufferSizeAfterLimit = bufferEnd - currentLimit;
+                bufferSize -= bufferSizeAfterLimit;
+            }
+            else
+            {
+                bufferSizeAfterLimit = 0;
+            }
+        }
+
+        /// <summary>
+        /// Discards the current limit, returning the previous limit.
+        /// </summary>
+        internal void PopLimit(int oldLimit)
+        {
+            currentLimit = oldLimit;
+            RecomputeBufferSizeAfterLimit();
+        }
+
+        /// <summary>
+        /// Returns whether or not all the data before the limit has been read.
+        /// </summary>
+        /// <returns></returns>
+        internal bool ReachedLimit
+        {
+            get
+            {
+                if (currentLimit == int.MaxValue)
+                {
+                    return false;
+                }
+                int currentAbsolutePosition = totalBytesRetired + bufferPos;
+                return currentAbsolutePosition >= currentLimit;
+            }
+        }
+
+        /// <summary>
+        /// Returns true if the stream has reached the end of the input. This is the
+        /// case if either the end of the underlying input source has been reached or
+        /// the stream has reached a limit created using PushLimit.
+        /// </summary>
+        public bool IsAtEnd
+        {
+            get { return bufferPos == bufferSize && !RefillBuffer(false); }
+        }
+
+        /// <summary>
+        /// Called when buffer is empty to read more bytes from the
+        /// input.  If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that
+        /// either there will be at least one byte in the buffer when it returns
+        /// or it will throw an exception.  If <paramref name="mustSucceed"/> is false,
+        /// RefillBuffer() returns false if no more bytes were available.
+        /// </summary>
+        /// <param name="mustSucceed"></param>
+        /// <returns></returns>
+        private bool RefillBuffer(bool mustSucceed)
+        {
+            if (bufferPos < bufferSize)
+            {
+                throw new InvalidOperationException("RefillBuffer() called when buffer wasn't empty.");
+            }
+
+            if (totalBytesRetired + bufferSize == currentLimit)
+            {
+                // Oops, we hit a limit.
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            totalBytesRetired += bufferSize;
+
+            bufferPos = 0;
+            bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
+            if (bufferSize < 0)
+            {
+                throw new InvalidOperationException("Stream.Read returned a negative count");
+            }
+            if (bufferSize == 0)
+            {
+                if (mustSucceed)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+                else
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                RecomputeBufferSizeAfterLimit();
+                int totalBytesRead =
+                    totalBytesRetired + bufferSize + bufferSizeAfterLimit;
+                if (totalBytesRead > sizeLimit || totalBytesRead < 0)
+                {
+                    throw InvalidProtocolBufferException.SizeLimitExceeded();
+                }
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// Read one byte from the input.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">
+        /// the end of the stream or the current limit was reached
+        /// </exception>
+        internal byte ReadRawByte()
+        {
+            if (bufferPos == bufferSize)
+            {
+                RefillBuffer(true);
+            }
+            return buffer[bufferPos++];
+        }
+
+        /// <summary>
+        /// Reads a fixed size of bytes from the input.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">
+        /// the end of the stream or the current limit was reached
+        /// </exception>
+        internal byte[] ReadRawBytes(int size)
+        {
+            if (size < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+
+            if (totalBytesRetired + bufferPos + size > currentLimit)
+            {
+                // Read to the end of the stream (up to the current limit) anyway.
+                SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
+                // Then fail.
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+
+            if (size <= bufferSize - bufferPos)
+            {
+                // We have all the bytes we need already.
+                byte[] bytes = new byte[size];
+                ByteArray.Copy(buffer, bufferPos, bytes, 0, size);
+                bufferPos += size;
+                return bytes;
+            }
+            else if (size < buffer.Length)
+            {
+                // Reading more bytes than are in the buffer, but not an excessive number
+                // of bytes.  We can safely allocate the resulting array ahead of time.
+
+                // First copy what we have.
+                byte[] bytes = new byte[size];
+                int pos = bufferSize - bufferPos;
+                ByteArray.Copy(buffer, bufferPos, bytes, 0, pos);
+                bufferPos = bufferSize;
+
+                // We want to use RefillBuffer() and then copy from the buffer into our
+                // byte array rather than reading directly into our byte array because
+                // the input may be unbuffered.
+                RefillBuffer(true);
+
+                while (size - pos > bufferSize)
+                {
+                    Buffer.BlockCopy(buffer, 0, bytes, pos, bufferSize);
+                    pos += bufferSize;
+                    bufferPos = bufferSize;
+                    RefillBuffer(true);
+                }
+
+                ByteArray.Copy(buffer, 0, bytes, pos, size - pos);
+                bufferPos = size - pos;
+
+                return bytes;
+            }
+            else
+            {
+                // The size is very large.  For security reasons, we can't allocate the
+                // entire byte array yet.  The size comes directly from the input, so a
+                // maliciously-crafted message could provide a bogus very large size in
+                // order to trick the app into allocating a lot of memory.  We avoid this
+                // by allocating and reading only a small chunk at a time, so that the
+                // malicious message must actually *be* extremely large to cause
+                // problems.  Meanwhile, we limit the allowed size of a message elsewhere.
+
+                // Remember the buffer markers since we'll have to copy the bytes out of
+                // it later.
+                int originalBufferPos = bufferPos;
+                int originalBufferSize = bufferSize;
+
+                // Mark the current buffer consumed.
+                totalBytesRetired += bufferSize;
+                bufferPos = 0;
+                bufferSize = 0;
+
+                // Read all the rest of the bytes we need.
+                int sizeLeft = size - (originalBufferSize - originalBufferPos);
+                List<byte[]> chunks = new List<byte[]>();
+
+                while (sizeLeft > 0)
+                {
+                    byte[] chunk = new byte[Math.Min(sizeLeft, buffer.Length)];
+                    int pos = 0;
+                    while (pos < chunk.Length)
+                    {
+                        int n = (input == null) ? -1 : input.Read(chunk, pos, chunk.Length - pos);
+                        if (n <= 0)
+                        {
+                            throw InvalidProtocolBufferException.TruncatedMessage();
+                        }
+                        totalBytesRetired += n;
+                        pos += n;
+                    }
+                    sizeLeft -= chunk.Length;
+                    chunks.Add(chunk);
+                }
+
+                // OK, got everything.  Now concatenate it all into one buffer.
+                byte[] bytes = new byte[size];
+
+                // Start by copying the leftover bytes from this.buffer.
+                int newPos = originalBufferSize - originalBufferPos;
+                ByteArray.Copy(buffer, originalBufferPos, bytes, 0, newPos);
+
+                // And now all the chunks.
+                foreach (byte[] chunk in chunks)
+                {
+                    Buffer.BlockCopy(chunk, 0, bytes, newPos, chunk.Length);
+                    newPos += chunk.Length;
+                }
+
+                // Done.
+                return bytes;
+            }
+        }
+
+        /// <summary>
+        /// Reads and discards <paramref name="size"/> bytes.
+        /// </summary>
+        /// <exception cref="InvalidProtocolBufferException">the end of the stream
+        /// or the current limit was reached</exception>
+        private void SkipRawBytes(int size)
+        {
+            if (size < 0)
+            {
+                throw InvalidProtocolBufferException.NegativeSize();
+            }
+
+            if (totalBytesRetired + bufferPos + size > currentLimit)
+            {
+                // Read to the end of the stream anyway.
+                SkipRawBytes(currentLimit - totalBytesRetired - bufferPos);
+                // Then fail.
+                throw InvalidProtocolBufferException.TruncatedMessage();
+            }
+
+            if (size <= bufferSize - bufferPos)
+            {
+                // We have all the bytes we need already.
+                bufferPos += size;
+            }
+            else
+            {
+                // Skipping more bytes than are in the buffer.  First skip what we have.
+                int pos = bufferSize - bufferPos;
+
+                // ROK 5/7/2013 Issue #54: should retire all bytes in buffer (bufferSize)
+                // totalBytesRetired += pos;
+                totalBytesRetired += bufferSize;
+                
+                bufferPos = 0;
+                bufferSize = 0;
+
+                // Then skip directly from the InputStream for the rest.
+                if (pos < size)
+                {
+                    if (input == null)
+                    {
+                        throw InvalidProtocolBufferException.TruncatedMessage();
+                    }
+                    SkipImpl(size - pos);
+                    totalBytesRetired += size - pos;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Abstraction of skipping to cope with streams which can't really skip.
+        /// </summary>
+        private void SkipImpl(int amountToSkip)
+        {
+            if (input.CanSeek)
+            {
+                long previousPosition = input.Position;
+                input.Position += amountToSkip;
+                if (input.Position != previousPosition + amountToSkip)
+                {
+                    throw InvalidProtocolBufferException.TruncatedMessage();
+                }
+            }
+            else
+            {
+                byte[] skipBuffer = new byte[Math.Min(1024, amountToSkip)];
+                while (amountToSkip > 0)
+                {
+                    int bytesRead = input.Read(skipBuffer, 0, Math.Min(skipBuffer.Length, amountToSkip));
+                    if (bytesRead <= 0)
+                    {
+                        throw InvalidProtocolBufferException.TruncatedMessage();
+                    }
+                    amountToSkip -= bytesRead;
+                }
+            }
+        }
+
+        #endregion
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/CodedInputStream.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1df2eb703cafa984fa2b4ea1d596b0cd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 304 - 0
Server/ThirdParty/Google.Protobuf/CodedOutputStream.ComputeSize.cs

@@ -0,0 +1,304 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    // This part of CodedOutputStream provides all the static entry points that are used
+    // by generated code and internally to compute the size of messages prior to being
+    // written to an instance of CodedOutputStream.
+    public sealed partial class CodedOutputStream
+    {
+        private const int LittleEndian64Size = 8;
+        private const int LittleEndian32Size = 4;        
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// double field, including the tag.
+        /// </summary>
+        public static int ComputeDoubleSize(double value)
+        {
+            return LittleEndian64Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// float field, including the tag.
+        /// </summary>
+        public static int ComputeFloatSize(float value)
+        {
+            return LittleEndian32Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// uint64 field, including the tag.
+        /// </summary>
+        public static int ComputeUInt64Size(ulong value)
+        {
+            return ComputeRawVarint64Size(value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// int64 field, including the tag.
+        /// </summary>
+        public static int ComputeInt64Size(long value)
+        {
+            return ComputeRawVarint64Size((ulong) value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// int32 field, including the tag.
+        /// </summary>
+        public static int ComputeInt32Size(int value)
+        {
+            if (value >= 0)
+            {
+                return ComputeRawVarint32Size((uint) value);
+            }
+            else
+            {
+                // Must sign-extend.
+                return 10;
+            }
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// fixed64 field, including the tag.
+        /// </summary>
+        public static int ComputeFixed64Size(ulong value)
+        {
+            return LittleEndian64Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// fixed32 field, including the tag.
+        /// </summary>
+        public static int ComputeFixed32Size(uint value)
+        {
+            return LittleEndian32Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// bool field, including the tag.
+        /// </summary>
+        public static int ComputeBoolSize(bool value)
+        {
+            return 1;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// string field, including the tag.
+        /// </summary>
+        public static int ComputeStringSize(String value)
+        {
+            int byteArraySize = Utf8Encoding.GetByteCount(value);
+            return ComputeLengthSize(byteArraySize) + byteArraySize;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// group field, including the tag.
+        /// </summary>
+        public static int ComputeGroupSize(IMessage value)
+        {
+            return value.CalculateSize();
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// embedded message field, including the tag.
+        /// </summary>
+        public static int ComputeMessageSize(IMessage value)
+        {
+            int size = value.CalculateSize();
+            return ComputeLengthSize(size) + size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// bytes field, including the tag.
+        /// </summary>
+        public static int ComputeBytesSize(ByteString value)
+        {
+            return ComputeLengthSize(value.Length) + value.Length;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// uint32 field, including the tag.
+        /// </summary>
+        public static int ComputeUInt32Size(uint value)
+        {
+            return ComputeRawVarint32Size(value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a
+        /// enum field, including the tag. The caller is responsible for
+        /// converting the enum value to its numeric value.
+        /// </summary>
+        public static int ComputeEnumSize(int value)
+        {
+            // Currently just a pass-through, but it's nice to separate it logically.
+            return ComputeInt32Size(value);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sfixed32 field, including the tag.
+        /// </summary>
+        public static int ComputeSFixed32Size(int value)
+        {
+            return LittleEndian32Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sfixed64 field, including the tag.
+        /// </summary>
+        public static int ComputeSFixed64Size(long value)
+        {
+            return LittleEndian64Size;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sint32 field, including the tag.
+        /// </summary>
+        public static int ComputeSInt32Size(int value)
+        {
+            return ComputeRawVarint32Size(EncodeZigZag32(value));
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode an
+        /// sint64 field, including the tag.
+        /// </summary>
+        public static int ComputeSInt64Size(long value)
+        {
+            return ComputeRawVarint64Size(EncodeZigZag64(value));
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a length,
+        /// as written by <see cref="WriteLength"/>.
+        /// </summary>
+        public static int ComputeLengthSize(int length)
+        {
+            return ComputeRawVarint32Size((uint) length);
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a varint.
+        /// </summary>
+        public static int ComputeRawVarint32Size(uint value)
+        {
+            if ((value & (0xffffffff << 7)) == 0)
+            {
+                return 1;
+            }
+            if ((value & (0xffffffff << 14)) == 0)
+            {
+                return 2;
+            }
+            if ((value & (0xffffffff << 21)) == 0)
+            {
+                return 3;
+            }
+            if ((value & (0xffffffff << 28)) == 0)
+            {
+                return 4;
+            }
+            return 5;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a varint.
+        /// </summary>
+        public static int ComputeRawVarint64Size(ulong value)
+        {
+            if ((value & (0xffffffffffffffffL << 7)) == 0)
+            {
+                return 1;
+            }
+            if ((value & (0xffffffffffffffffL << 14)) == 0)
+            {
+                return 2;
+            }
+            if ((value & (0xffffffffffffffffL << 21)) == 0)
+            {
+                return 3;
+            }
+            if ((value & (0xffffffffffffffffL << 28)) == 0)
+            {
+                return 4;
+            }
+            if ((value & (0xffffffffffffffffL << 35)) == 0)
+            {
+                return 5;
+            }
+            if ((value & (0xffffffffffffffffL << 42)) == 0)
+            {
+                return 6;
+            }
+            if ((value & (0xffffffffffffffffL << 49)) == 0)
+            {
+                return 7;
+            }
+            if ((value & (0xffffffffffffffffL << 56)) == 0)
+            {
+                return 8;
+            }
+            if ((value & (0xffffffffffffffffL << 63)) == 0)
+            {
+                return 9;
+            }
+            return 10;
+        }
+
+        /// <summary>
+        /// Computes the number of bytes that would be needed to encode a tag.
+        /// </summary>
+        public static int ComputeTagSize(int fieldNumber)
+        {
+            return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/CodedOutputStream.ComputeSize.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c94acc4c66cb8e14ca00789b21ef4dde
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 770 - 0
Server/ThirdParty/Google.Protobuf/CodedOutputStream.cs

@@ -0,0 +1,770 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using Google.Protobuf.Collections;
+using System;
+using System.IO;
+using System.Text;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Encodes and writes protocol message fields.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// This class is generally used by generated code to write appropriate
+    /// primitives to the stream. It effectively encapsulates the lowest
+    /// levels of protocol buffer format. Unlike some other implementations,
+    /// this does not include combined "write tag and value" methods. Generated
+    /// code knows the exact byte representations of the tags they're going to write,
+    /// so there's no need to re-encode them each time. Manually-written code calling
+    /// this class should just call one of the <c>WriteTag</c> overloads before each value.
+    /// </para>
+    /// <para>
+    /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField&lt;T&gt;</c>
+    /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.
+    /// </para>
+    /// </remarks>
+    public sealed partial class CodedOutputStream : IDisposable
+    {
+        // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
+        internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
+
+        /// <summary>
+        /// The buffer size used by CreateInstance(Stream).
+        /// </summary>
+        public static readonly int DefaultBufferSize = 4096;
+
+        private bool leaveOpen;
+        private byte[] buffer;
+        private int limit;
+        private int position;
+        private Stream output;
+
+        #region Construction
+        /// <summary>
+        /// Creates a new CodedOutputStream that writes directly to the given
+        /// byte array. If more bytes are written than fit in the array,
+        /// OutOfSpaceException will be thrown.
+        /// </summary>
+        public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream that writes directly to the given
+        /// byte array slice. If more bytes are written than fit in the array,
+        /// OutOfSpaceException will be thrown.
+        /// </summary>
+        private CodedOutputStream(byte[] buffer, int offset, int length)
+        {
+            this.output = null;
+            this.buffer = buffer;
+            this.position = offset;
+            this.limit = offset + length;
+            leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
+        }
+        
+        public void Reset(byte[] buf, int offset, int length)
+        {
+            this.output = null;
+            this.buffer = buf;
+            this.position = offset;
+            this.limit = offset + length;
+            leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
+        }
+
+        private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
+        {
+            this.output = ProtoPreconditions.CheckNotNull(output, "output");
+            this.buffer = buffer;
+            this.position = 0;
+            this.limit = buffer.Length;
+            this.leaveOpen = leaveOpen;
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that
+        /// stream when the returned <c>CodedOutputStream</c> is disposed.
+        /// </summary>
+        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
+        public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream and uses
+        /// the specified buffer size.
+        /// </summary>
+        /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
+        /// <param name="bufferSize">The size of buffer to use internally.</param>
+        public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream.
+        /// </summary>
+        /// <param name="output">The stream to write to.</param>
+        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
+        /// if <c>false</c>, the provided stream is disposed as well.</param>
+        public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new CodedOutputStream which write to the given stream and uses
+        /// the specified buffer size.
+        /// </summary>
+        /// <param name="output">The stream to write to.</param>
+        /// <param name="bufferSize">The size of buffer to use internally.</param>
+        /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
+        /// if <c>false</c>, the provided stream is disposed as well.</param>
+        public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
+        {
+        }
+        #endregion
+
+        /// <summary>
+        /// Returns the current position in the stream, or the position in the output buffer
+        /// </summary>
+        public long Position
+        {
+            get
+            {
+                if (output != null)
+                {
+                    return output.Position + position;
+                }
+                return position;
+            }
+        }
+
+        #region Writing of values (not including tags)
+
+        /// <summary>
+        /// Writes a double field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteDouble(double value)
+        {
+            WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
+        }
+
+        /// <summary>
+        /// Writes a float field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFloat(float value)
+        {
+            byte[] rawBytes = BitConverter.GetBytes(value);
+            if (!BitConverter.IsLittleEndian)
+            {
+                ByteArray.Reverse(rawBytes);
+            }
+
+            if (limit - position >= 4)
+            {
+                buffer[position++] = rawBytes[0];
+                buffer[position++] = rawBytes[1];
+                buffer[position++] = rawBytes[2];
+                buffer[position++] = rawBytes[3];
+            }
+            else
+            {
+                WriteRawBytes(rawBytes, 0, 4);
+            }
+        }
+
+        /// <summary>
+        /// Writes a uint64 field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteUInt64(ulong value)
+        {
+            WriteRawVarint64(value);
+        }
+
+        /// <summary>
+        /// Writes an int64 field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteInt64(long value)
+        {
+            WriteRawVarint64((ulong) value);
+        }
+
+        /// <summary>
+        /// Writes an int32 field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteInt32(int value)
+        {
+            if (value >= 0)
+            {
+                WriteRawVarint32((uint) value);
+            }
+            else
+            {
+                // Must sign-extend.
+                WriteRawVarint64((ulong) value);
+            }
+        }
+
+        /// <summary>
+        /// Writes a fixed64 field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFixed64(ulong value)
+        {
+            WriteRawLittleEndian64(value);
+        }
+
+        /// <summary>
+        /// Writes a fixed32 field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteFixed32(uint value)
+        {
+            WriteRawLittleEndian32(value);
+        }
+
+        /// <summary>
+        /// Writes a bool field value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteBool(bool value)
+        {
+            WriteRawByte(value ? (byte) 1 : (byte) 0);
+        }
+
+        /// <summary>
+        /// Writes a string field value, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteString(string value)
+        {
+            // Optimise the case where we have enough space to write
+            // the string directly to the buffer, which should be common.
+            int length = Utf8Encoding.GetByteCount(value);
+            WriteLength(length);
+            if (limit - position >= length)
+            {
+                if (length == value.Length) // Must be all ASCII...
+                {
+                    for (int i = 0; i < length; i++)
+                    {
+                        buffer[position + i] = (byte)value[i];
+                    }
+                }
+                else
+                {
+                    Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
+                }
+                position += length;
+            }
+            else
+            {
+                byte[] bytes = Utf8Encoding.GetBytes(value);
+                WriteRawBytes(bytes);
+            }
+        }
+
+        /// <summary>
+        /// Writes a message, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteMessage(IMessage value)
+        {
+            WriteLength(value.CalculateSize());
+            value.WriteTo(this);
+        }
+
+        /// <summary>
+        /// Write a byte string, without a tag, to the stream.
+        /// The data is length-prefixed.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteBytes(ByteString value)
+        {
+            WriteLength(value.Length);
+            value.WriteRawBytesTo(this);
+        }
+
+        /// <summary>
+        /// Writes a uint32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteUInt32(uint value)
+        {
+            WriteRawVarint32(value);
+        }
+
+        /// <summary>
+        /// Writes an enum value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteEnum(int value)
+        {
+            WriteInt32(value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write.</param>
+        public void WriteSFixed32(int value)
+        {
+            WriteRawLittleEndian32((uint) value);
+        }
+
+        /// <summary>
+        /// Writes an sfixed64 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSFixed64(long value)
+        {
+            WriteRawLittleEndian64((ulong) value);
+        }
+
+        /// <summary>
+        /// Writes an sint32 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSInt32(int value)
+        {
+            WriteRawVarint32(EncodeZigZag32(value));
+        }
+
+        /// <summary>
+        /// Writes an sint64 value, without a tag, to the stream.
+        /// </summary>
+        /// <param name="value">The value to write</param>
+        public void WriteSInt64(long value)
+        {
+            WriteRawVarint64(EncodeZigZag64(value));
+        }
+
+        /// <summary>
+        /// Writes a length (in bytes) for length-delimited data.
+        /// </summary>
+        /// <remarks>
+        /// This method simply writes a rawint, but exists for clarity in calling code.
+        /// </remarks>
+        /// <param name="length">Length value, in bytes.</param>
+        public void WriteLength(int length)
+        {
+            WriteRawVarint32((uint) length);
+        }
+
+        #endregion
+
+        #region Raw tag writing
+        /// <summary>
+        /// Encodes and writes a tag.
+        /// </summary>
+        /// <param name="fieldNumber">The number of the field to write the tag for</param>
+        /// <param name="type">The wire format type of the tag to write</param>
+        public void WriteTag(int fieldNumber, WireFormat.WireType type)
+        {
+            WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
+        }
+
+        /// <summary>
+        /// Writes an already-encoded tag.
+        /// </summary>
+        /// <param name="tag">The encoded tag</param>
+        public void WriteTag(uint tag)
+        {
+            WriteRawVarint32(tag);
+        }
+
+        /// <summary>
+        /// Writes the given single-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The encoded tag</param>
+        public void WriteRawTag(byte b1)
+        {
+            WriteRawByte(b1);
+        }
+
+        /// <summary>
+        /// Writes the given two-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2)
+        {
+            WriteRawByte(b1);
+            WriteRawByte(b2);
+        }
+
+        /// <summary>
+        /// Writes the given three-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3)
+        {
+            WriteRawByte(b1);
+            WriteRawByte(b2);
+            WriteRawByte(b3);
+        }
+
+        /// <summary>
+        /// Writes the given four-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
+        {
+            WriteRawByte(b1);
+            WriteRawByte(b2);
+            WriteRawByte(b3);
+            WriteRawByte(b4);
+        }
+
+        /// <summary>
+        /// Writes the given five-byte tag directly to the stream.
+        /// </summary>
+        /// <param name="b1">The first byte of the encoded tag</param>
+        /// <param name="b2">The second byte of the encoded tag</param>
+        /// <param name="b3">The third byte of the encoded tag</param>
+        /// <param name="b4">The fourth byte of the encoded tag</param>
+        /// <param name="b5">The fifth byte of the encoded tag</param>
+        public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
+        {
+            WriteRawByte(b1);
+            WriteRawByte(b2);
+            WriteRawByte(b3);
+            WriteRawByte(b4);
+            WriteRawByte(b5);
+        }
+        #endregion
+
+        #region Underlying writing primitives
+        /// <summary>
+        /// Writes a 32 bit value as a varint. The fast route is taken when
+        /// there's enough buffer space left to whizz through without checking
+        /// for each byte; otherwise, we resort to calling WriteRawByte each time.
+        /// </summary>
+        internal void WriteRawVarint32(uint value)
+        {
+            // Optimize for the common case of a single byte value
+            if (value < 128 && position < limit)
+            {
+                buffer[position++] = (byte)value;
+                return;
+            }
+
+            while (value > 127 && position < limit)
+            {
+                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
+                value >>= 7;
+            }
+            while (value > 127)
+            {
+                WriteRawByte((byte) ((value & 0x7F) | 0x80));
+                value >>= 7;
+            }
+            if (position < limit)
+            {
+                buffer[position++] = (byte) value;
+            }
+            else
+            {
+                WriteRawByte((byte) value);
+            }
+        }
+
+        internal void WriteRawVarint64(ulong value)
+        {
+            while (value > 127 && position < limit)
+            {
+                buffer[position++] = (byte) ((value & 0x7F) | 0x80);
+                value >>= 7;
+            }
+            while (value > 127)
+            {
+                WriteRawByte((byte) ((value & 0x7F) | 0x80));
+                value >>= 7;
+            }
+            if (position < limit)
+            {
+                buffer[position++] = (byte) value;
+            }
+            else
+            {
+                WriteRawByte((byte) value);
+            }
+        }
+
+        internal void WriteRawLittleEndian32(uint value)
+        {
+            if (position + 4 > limit)
+            {
+                WriteRawByte((byte) value);
+                WriteRawByte((byte) (value >> 8));
+                WriteRawByte((byte) (value >> 16));
+                WriteRawByte((byte) (value >> 24));
+            }
+            else
+            {
+                buffer[position++] = ((byte) value);
+                buffer[position++] = ((byte) (value >> 8));
+                buffer[position++] = ((byte) (value >> 16));
+                buffer[position++] = ((byte) (value >> 24));
+            }
+        }
+
+        internal void WriteRawLittleEndian64(ulong value)
+        {
+            if (position + 8 > limit)
+            {
+                WriteRawByte((byte) value);
+                WriteRawByte((byte) (value >> 8));
+                WriteRawByte((byte) (value >> 16));
+                WriteRawByte((byte) (value >> 24));
+                WriteRawByte((byte) (value >> 32));
+                WriteRawByte((byte) (value >> 40));
+                WriteRawByte((byte) (value >> 48));
+                WriteRawByte((byte) (value >> 56));
+            }
+            else
+            {
+                buffer[position++] = ((byte) value);
+                buffer[position++] = ((byte) (value >> 8));
+                buffer[position++] = ((byte) (value >> 16));
+                buffer[position++] = ((byte) (value >> 24));
+                buffer[position++] = ((byte) (value >> 32));
+                buffer[position++] = ((byte) (value >> 40));
+                buffer[position++] = ((byte) (value >> 48));
+                buffer[position++] = ((byte) (value >> 56));
+            }
+        }
+
+        internal void WriteRawByte(byte value)
+        {
+            if (position == limit)
+            {
+                RefreshBuffer();
+            }
+
+            buffer[position++] = value;
+        }
+
+        internal void WriteRawByte(uint value)
+        {
+            WriteRawByte((byte) value);
+        }
+
+        /// <summary>
+        /// Writes out an array of bytes.
+        /// </summary>
+        internal void WriteRawBytes(byte[] value)
+        {
+            WriteRawBytes(value, 0, value.Length);
+        }
+
+        /// <summary>
+        /// Writes out part of an array of bytes.
+        /// </summary>
+        internal void WriteRawBytes(byte[] value, int offset, int length)
+        {
+            if (limit - position >= length)
+            {
+                ByteArray.Copy(value, offset, buffer, position, length);
+                // We have room in the current buffer.
+                position += length;
+            }
+            else
+            {
+                // Write extends past current buffer.  Fill the rest of this buffer and
+                // flush.
+                int bytesWritten = limit - position;
+                ByteArray.Copy(value, offset, buffer, position, bytesWritten);
+                offset += bytesWritten;
+                length -= bytesWritten;
+                position = limit;
+                RefreshBuffer();
+
+                // Now deal with the rest.
+                // Since we have an output stream, this is our buffer
+                // and buffer offset == 0
+                if (length <= limit)
+                {
+                    // Fits in new buffer.
+                    ByteArray.Copy(value, offset, buffer, 0, length);
+                    position = length;
+                }
+                else
+                {
+                    // Write is very big.  Let's do it all at once.
+                    output.Write(value, offset, length);
+                }
+            }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Encode a 32-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        internal static uint EncodeZigZag32(int n)
+        {
+            // Note:  the right-shift must be arithmetic
+            return (uint) ((n << 1) ^ (n >> 31));
+        }
+
+        /// <summary>
+        /// Encode a 64-bit value with ZigZag encoding.
+        /// </summary>
+        /// <remarks>
+        /// ZigZag encodes signed integers into values that can be efficiently
+        /// encoded with varint.  (Otherwise, negative values must be 
+        /// sign-extended to 64 bits to be varint encoded, thus always taking
+        /// 10 bytes on the wire.)
+        /// </remarks>
+        internal static ulong EncodeZigZag64(long n)
+        {
+            return (ulong) ((n << 1) ^ (n >> 63));
+        }
+
+        private void RefreshBuffer()
+        {
+            if (output == null)
+            {
+                // We're writing to a single buffer.
+                throw new OutOfSpaceException();
+            }
+
+            // Since we have an output stream, this is our buffer
+            // and buffer offset == 0
+            output.Write(buffer, 0, position);
+            position = 0;
+        }
+
+        /// <summary>
+        /// Indicates that a CodedOutputStream wrapping a flat byte array
+        /// ran out of space.
+        /// </summary>
+        public sealed class OutOfSpaceException : IOException
+        {
+            internal OutOfSpaceException()
+                : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
+            {
+            }
+        }
+
+        /// <summary>
+        /// Flushes any buffered data and optionally closes the underlying stream, if any.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// By default, any underlying stream is closed by this method. To configure this behaviour,
+        /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not
+        /// have an underlying stream, this method does nothing.
+        /// </para>
+        /// <para>
+        /// For the sake of efficiency, calling this method does not prevent future write calls - but
+        /// if a later write ends up writing to a stream which has been disposed, that is likely to
+        /// fail. It is recommend that you not call any other methods after this.
+        /// </para>
+        /// </remarks>
+        public void Dispose()
+        {
+            Flush();
+            if (!leaveOpen)
+            {
+                output.Dispose();
+            }
+        }
+
+        /// <summary>
+        /// Flushes any buffered data to the underlying stream (if there is one).
+        /// </summary>
+        public void Flush()
+        {
+            if (output != null)
+            {
+                RefreshBuffer();
+            }
+        }
+
+        /// <summary>
+        /// Verifies that SpaceLeft returns zero. It's common to create a byte array
+        /// that is exactly big enough to hold a message, then write to it with
+        /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
+        /// the message was actually as big as expected, which can help bugs.
+        /// </summary>
+        public void CheckNoSpaceLeft()
+        {
+            if (SpaceLeft != 0)
+            {
+                throw new InvalidOperationException("Did not write as much data as expected.");
+            }
+        }
+
+        /// <summary>
+        /// If writing to a flat array, returns the space left in the array. Otherwise,
+        /// throws an InvalidOperationException.
+        /// </summary>
+        public int SpaceLeft
+        {
+            get
+            {
+                if (output == null)
+                {
+                    return limit - position;
+                }
+                else
+                {
+                    throw new InvalidOperationException(
+                        "SpaceLeft can only be called on CodedOutputStreams that are " +
+                        "writing to a flat array.");
+                }
+            }
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/CodedOutputStream.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e4768e92c8082c14db86e6dd81843cf7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 379 - 0
Server/ThirdParty/Google.Protobuf/FieldCodec.cs

@@ -0,0 +1,379 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Factory methods for <see cref="FieldCodec{T}"/>.
+    /// </summary>
+    public static class FieldCodec
+    {
+        // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
+
+        /// <summary>
+        /// Retrieves a codec suitable for a string field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<string> ForString(uint tag)
+        {
+            return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a bytes field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<ByteString> ForBytes(uint tag)
+        {
+            return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a bool field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<bool> ForBool(uint tag)
+        {
+            return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an int32 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<int> ForInt32(uint tag)
+        {
+            return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an sint32 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<int> ForSInt32(uint tag)
+        {
+            return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a fixed32 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<uint> ForFixed32(uint tag)
+        {
+            return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an sfixed32 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<int> ForSFixed32(uint tag)
+        {
+            return new FieldCodec<int>(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a uint32 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<uint> ForUInt32(uint tag)
+        {
+            return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an int64 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<long> ForInt64(uint tag)
+        {
+            return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an sint64 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<long> ForSInt64(uint tag)
+        {
+            return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a fixed64 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<ulong> ForFixed64(uint tag)
+        {
+            return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for an sfixed64 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<long> ForSFixed64(uint tag)
+        {
+            return new FieldCodec<long>(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a uint64 field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<ulong> ForUInt64(uint tag)
+        {
+            return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a float field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<float> ForFloat(uint tag)
+        {
+            return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a double field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<double> ForDouble(uint tag)
+        {
+            return new FieldCodec<double>(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag);
+        }
+
+        // Enums are tricky. We can probably use expression trees to build these delegates automatically,
+        // but it's easy to generate the code for it.
+
+        /// <summary>
+        /// Retrieves a codec suitable for an enum field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
+        /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
+        {
+            return new FieldCodec<T>(input => fromInt32(
+                input.ReadEnum()),
+                (output, value) => output.WriteEnum(toInt32(value)),
+                value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag);
+        }
+
+        /// <summary>
+        /// Retrieves a codec suitable for a message field with the given tag.
+        /// </summary>
+        /// <param name="tag">The tag.</param>
+        /// <param name="parser">A parser to use for the message type.</param>
+        /// <returns>A codec for the given tag.</returns>
+        public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage
+        {
+            return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
+                (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
+        }
+    }
+
+    /// <summary>
+    /// <para>
+    /// An encode/decode pair for a single field. This effectively encapsulates
+    /// all the information needed to read or write the field value from/to a coded
+    /// stream.
+    /// </para>
+    /// <para>
+    /// This class is public and has to be as it is used by generated code, but its public
+    /// API is very limited - just what the generated code needs to call directly.
+    /// </para>
+    /// </summary>
+    /// <remarks>
+    /// This never writes default values to the stream, and does not address "packedness"
+    /// in repeated fields itself, other than to know whether or not the field *should* be packed.
+    /// </remarks>
+    public sealed class FieldCodec<T>
+    {
+        private static readonly T DefaultDefault;
+        // Only non-nullable value types support packing. This is the simplest way of detecting that.
+        private static readonly bool TypeSupportsPacking = default(T) != null;
+
+        static FieldCodec()
+        {
+            if (typeof(T) == typeof(string))
+            {
+                DefaultDefault = (T)(object)"";
+            }
+            else if (typeof(T) == typeof(ByteString))
+            {
+                DefaultDefault = (T)(object)ByteString.Empty;
+            }
+            // Otherwise it's the default value of the CLR type
+        }
+
+        internal static bool IsPackedRepeatedField(uint tag)
+        {
+            return TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited;
+        }
+
+        internal readonly bool PackedRepeatedField;
+
+        /// <summary>
+        /// Returns a delegate to write a value (unconditionally) to a coded output stream.
+        /// </summary>
+        internal readonly Action<CodedOutputStream, T> ValueWriter;
+
+        /// <summary>
+        /// Returns the size calculator for just a value.
+        /// </summary>
+        internal readonly Func<T, int> ValueSizeCalculator;
+
+        /// <summary>
+        /// Returns a delegate to read a value from a coded input stream. It is assumed that
+        /// the stream is already positioned on the appropriate tag.
+        /// </summary>
+        internal readonly Func<CodedInputStream, T> ValueReader;
+
+        /// <summary>
+        /// Returns the fixed size for an entry, or 0 if sizes vary.
+        /// </summary>
+        internal readonly int FixedSize;
+
+        /// <summary>
+        /// Gets the tag of the codec.
+        /// </summary>
+        /// <value>
+        /// The tag of the codec.
+        /// </value>
+        internal readonly uint Tag;
+
+        /// <summary>
+        /// Default value for this codec. Usually the same for every instance of the same type, but
+        /// for string/ByteString wrapper fields the codec's default value is null, whereas for
+        /// other string/ByteString fields it's "" or ByteString.Empty.
+        /// </summary>
+        /// <value>
+        /// The default value of the codec's type.
+        /// </value>
+        internal readonly T DefaultValue;
+
+        private readonly int tagSize;
+        
+        internal FieldCodec(
+                Func<CodedInputStream, T> reader,
+                Action<CodedOutputStream, T> writer,
+                int fixedSize,
+                uint tag) : this(reader, writer, _ => fixedSize, tag)
+        {
+            FixedSize = fixedSize;
+        }
+
+        internal FieldCodec(
+            Func<CodedInputStream, T> reader,
+            Action<CodedOutputStream, T> writer,
+            Func<T, int> sizeCalculator,
+            uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
+        {
+        }
+
+        internal FieldCodec(
+            Func<CodedInputStream, T> reader,
+            Action<CodedOutputStream, T> writer,
+            Func<T, int> sizeCalculator,
+            uint tag,
+            T defaultValue)
+        {
+            ValueReader = reader;
+            ValueWriter = writer;
+            ValueSizeCalculator = sizeCalculator;
+            FixedSize = 0;
+            Tag = tag;
+            DefaultValue = defaultValue;
+            tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
+            // Detect packed-ness once, so we can check for it within RepeatedField<T>.
+            PackedRepeatedField = IsPackedRepeatedField(tag);
+        }
+
+        /// <summary>
+        /// Write a tag and the given value, *if* the value is not the default.
+        /// </summary>
+        public void WriteTagAndValue(CodedOutputStream output, T value)
+        {
+            if (!IsDefault(value))
+            {
+                output.WriteTag(Tag);
+                ValueWriter(output, value);
+            }
+        }
+
+        /// <summary>
+        /// Reads a value of the codec type from the given <see cref="CodedInputStream"/>.
+        /// </summary>
+        /// <param name="input">The input stream to read from.</param>
+        /// <returns>The value read from the stream.</returns>
+        public T Read(CodedInputStream input)
+        {
+            return ValueReader(input);
+        }
+
+        /// <summary>
+        /// Calculates the size required to write the given value, with a tag,
+        /// if the value is not the default.
+        /// </summary>
+        public int CalculateSizeWithTag(T value)
+        {
+            return IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
+        }
+
+        private bool IsDefault(T value)
+        {
+            return EqualityComparer<T>.Default.Equals(value, DefaultValue);
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/FieldCodec.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bc2f63c5dd762a74785e957e9e2ff0c9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 0 - 74
Server/ThirdParty/Google.Protobuf/Google.Protobuf.csproj

@@ -1,74 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <OutputType>Library</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-    <ApplicationIcon />
-    <StartupObject />
-    <LangVersion>7.3</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup>
-    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <OutputPath>..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <OutputPath>..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\ByteArray.cs">
-      <Link>ByteArray.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\ByteString.cs">
-      <Link>ByteString.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\CodedInputStream.cs">
-      <Link>CodedInputStream.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\CodedOutputStream.ComputeSize.cs">
-      <Link>CodedOutputStream.ComputeSize.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\CodedOutputStream.cs">
-      <Link>CodedOutputStream.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\FieldCodec.cs">
-      <Link>FieldCodec.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\ICustomDiagnosticMessage.cs">
-      <Link>ICustomDiagnosticMessage.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\IMessage.cs">
-      <Link>IMessage.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\InvalidProtocolBufferException.cs">
-      <Link>InvalidProtocolBufferException.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\LimitedInputStream.cs">
-      <Link>LimitedInputStream.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\MessageExtensions.cs">
-      <Link>MessageExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\MessageParser.cs">
-      <Link>MessageParser.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\PropertyInfoExtensions.cs">
-      <Link>PropertyInfoExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\ProtoPreconditions.cs">
-      <Link>ProtoPreconditions.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\RepeatedField.cs">
-      <Link>RepeatedField.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\StreamExtensions.cs">
-      <Link>StreamExtensions.cs</Link>
-    </Compile>
-    <Compile Include="..\..\..\Unity\Assets\ThirdParty\Google.Protobuf\WireFormat.cs">
-      <Link>WireFormat.cs</Link>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\MongoDBDriver\MongoDB.Bson\MongoDB.Bson.csproj" />
-  </ItemGroup>
-</Project>

+ 69 - 0
Server/ThirdParty/Google.Protobuf/ICustomDiagnosticMessage.cs

@@ -0,0 +1,69 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// A message type that has a custom string format for diagnostic purposes.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// Calling <see cref="object.ToString"/> on a generated message type normally
+    /// returns the JSON representation. If a message type implements this interface,
+    /// then the <see cref="ToDiagnosticString"/> method will be called instead of the regular
+    /// JSON formatting code, but only when <c>ToString()</c> is called either on the message itself
+    /// or on another message which contains it. This does not affect the normal JSON formatting of
+    /// the message.
+    /// </para>
+    /// <para>
+    /// For example, if you create a proto message representing a GUID, the internal
+    /// representation may be a <c>bytes</c> field or four <c>fixed32</c> fields. However, when debugging
+    /// it may be more convenient to see a result in the same format as <see cref="System.Guid"/> provides.
+    /// </para>
+    /// <para>This interface extends <see cref="IMessage"/> to avoid it accidentally being implemented
+    /// on types other than messages, where it would not be used by anything in the framework.</para>
+    /// </remarks>
+    public interface ICustomDiagnosticMessage : IMessage
+    {
+        /// <summary>
+        /// Returns a string representation of this object, for diagnostic purposes.
+        /// </summary>
+        /// <remarks>
+        /// This method is called when a message is formatted as part of a <see cref="object.ToString"/>
+        /// call. It does not affect the JSON representation used by <see cref="JsonFormatter"/> other than
+        /// in calls to <see cref="JsonFormatter.ToDiagnosticString(IMessage)"/>. While it is recommended
+        /// that the result is valid JSON, this is never assumed by the Protobuf library.
+        /// </remarks>
+        /// <returns>A string representation of this object, for diagnostic purposes.</returns>
+        string ToDiagnosticString();
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/ICustomDiagnosticMessage.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4e3f9f48b78790d40b93778ba6fc08aa
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 64 - 0
Server/ThirdParty/Google.Protobuf/IMessage.cs

@@ -0,0 +1,64 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Interface for a Protocol Buffers message, supporting
+    /// basic operations required for serialization.
+    /// </summary>
+    public interface IMessage
+    {
+        /// <summary>
+        /// Merges the data from the specified coded input stream with the current message.
+        /// </summary>
+        /// <remarks>See the user guide for precise merge semantics.</remarks>
+        /// <param name="input"></param>
+        void MergeFrom(CodedInputStream input);
+
+        /// <summary>
+        /// Writes the data to the given coded output stream.
+        /// </summary>
+        /// <param name="output">Coded output stream to write the data to. Must not be null.</param>
+        void WriteTo(CodedOutputStream output);
+
+        /// <summary>
+        /// Calculates the size of this message in Protocol Buffer wire format, in bytes.
+        /// </summary>
+        /// <returns>The number of bytes required to write this message
+        /// to a coded output stream.</returns>
+        int CalculateSize();
+
+    }
+
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/IMessage.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 99b4394158577934492f893a057dc06f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 129 - 0
Server/ThirdParty/Google.Protobuf/InvalidProtocolBufferException.cs

@@ -0,0 +1,129 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Thrown when a protocol message being parsed is invalid in some way,
+    /// e.g. it contains a malformed varint or a negative byte length.
+    /// </summary>
+    public sealed class InvalidProtocolBufferException : IOException
+    {
+        internal InvalidProtocolBufferException(string message)
+            : base(message)
+        {
+        }
+
+        internal InvalidProtocolBufferException(string message, Exception innerException)
+            : base(message, innerException)
+        {
+        }
+
+        internal static InvalidProtocolBufferException MoreDataAvailable()
+        {
+            return new InvalidProtocolBufferException(
+                "Completed reading a message while more data was available in the stream.");
+        }
+
+        internal static InvalidProtocolBufferException TruncatedMessage()
+        {
+            return new InvalidProtocolBufferException(
+                "While parsing a protocol message, the input ended unexpectedly " +
+                "in the middle of a field.  This could mean either that the " +
+                "input has been truncated or that an embedded message " +
+                "misreported its own length.");
+        }
+
+        internal static InvalidProtocolBufferException NegativeSize()
+        {
+            return new InvalidProtocolBufferException(
+                "CodedInputStream encountered an embedded string or message " +
+                "which claimed to have negative size.");
+        }
+
+        internal static InvalidProtocolBufferException MalformedVarint()
+        {
+            return new InvalidProtocolBufferException(
+                "CodedInputStream encountered a malformed varint.");
+        }
+
+        /// <summary>
+        /// Creates an exception for an error condition of an invalid tag being encountered.
+        /// </summary>
+        internal static InvalidProtocolBufferException InvalidTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message contained an invalid tag (zero).");
+        }
+
+        internal static InvalidProtocolBufferException InvalidBase64(Exception innerException)
+        {
+            return new InvalidProtocolBufferException("Invalid base64 data", innerException);
+        }
+
+        internal static InvalidProtocolBufferException InvalidEndTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message end-group tag did not match expected tag.");
+        }
+
+        internal static InvalidProtocolBufferException RecursionLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message had too many levels of nesting.  May be malicious.  " +
+                "Use CodedInputStream.SetRecursionLimit() to increase the depth limit.");
+        }
+
+        internal static InvalidProtocolBufferException JsonRecursionLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message had too many levels of nesting.  May be malicious.  " +
+                "Use JsonParser.Settings to increase the depth limit.");
+        }
+
+        internal static InvalidProtocolBufferException SizeLimitExceeded()
+        {
+            return new InvalidProtocolBufferException(
+                "Protocol message was too large.  May be malicious.  " +
+                "Use CodedInputStream.SetSizeLimit() to increase the size limit.");
+        }
+
+        internal static InvalidProtocolBufferException InvalidMessageStreamTag()
+        {
+            return new InvalidProtocolBufferException(
+                "Stream of protocol messages had invalid tag. Expected tag is length-delimited field 1.");
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/InvalidProtocolBufferException.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3eb2be644ccc6fc4187e38443cc47ea5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 110 - 0
Server/ThirdParty/Google.Protobuf/LimitedInputStream.cs

@@ -0,0 +1,110 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+    
+using System;
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Stream implementation which proxies another stream, only allowing a certain amount
+    /// of data to be read. Note that this is only used to read delimited streams, so it
+    /// doesn't attempt to implement everything.
+    /// </summary>
+    internal sealed class LimitedInputStream : Stream
+    {
+        private readonly Stream proxied;
+        private int bytesLeft;
+
+        internal LimitedInputStream(Stream proxied, int size)
+        {
+            this.proxied = proxied;
+            bytesLeft = size;
+        }
+
+        public override bool CanRead
+        {
+            get { return true; }
+        }
+
+        public override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return false; }
+        }
+
+        public override void Flush()
+        {
+        }
+
+        public override long Length
+        {
+            get { throw new NotSupportedException(); }
+        }
+
+        public override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            if (bytesLeft > 0)
+            {
+                int bytesRead = proxied.Read(buffer, offset, Math.Min(bytesLeft, count));
+                bytesLeft -= bytesRead;
+                return bytesRead;
+            }
+            return 0;
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void SetLength(long value)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            throw new NotSupportedException();
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/LimitedInputStream.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f02379e5cbe40714dba4d002f8980dbf
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 178 - 0
Server/ThirdParty/Google.Protobuf/MessageExtensions.cs

@@ -0,0 +1,178 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Extension methods on <see cref="IMessage"/> and <see cref="IMessage{T}"/>.
+    /// </summary>
+    public static class MessageExtensions
+    {
+        public static CodedInputStream inputStream = new CodedInputStream(new byte[0]);
+        public static CodedOutputStream outputStream = new CodedOutputStream(new byte[0]);
+        
+        /// <summary>
+        /// Merges data from the given byte array into an existing message.
+        /// </summary>
+        /// <param name="message">The message to merge the data into.</param>
+        /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
+        public static void MergeFrom(this IMessage message, byte[] data)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(data, "data");
+            inputStream.Reset(data, 0, data.Length);
+            CodedInputStream input = inputStream;
+            message.MergeFrom(input);
+            input.CheckReadEndOfStreamTag();
+        }
+        
+        /// <summary>
+        /// Merges data from the given byte array into an existing message.
+        /// </summary>
+        /// <param name="message">The message to merge the data into.</param>
+        /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
+        public static void MergeFrom(this IMessage message, byte[] data, int offset, int length)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(data, "data");
+            inputStream.Reset(data, offset, length);
+            CodedInputStream input = inputStream;
+            //CodedInputStream input = new CodedInputStream(data, offset, length);
+            message.MergeFrom(input);
+            input.CheckReadEndOfStreamTag();
+        }
+
+        /// <summary>
+        /// Merges data from the given byte string into an existing message.
+        /// </summary>
+        /// <param name="message">The message to merge the data into.</param>
+        /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
+        public static void MergeFrom(this IMessage message, ByteString data)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(data, "data");
+            CodedInputStream input = data.CreateCodedInput();
+            message.MergeFrom(input);
+            input.CheckReadEndOfStreamTag();
+        }
+
+        /// <summary>
+        /// Merges data from the given stream into an existing message.
+        /// </summary>
+        /// <param name="message">The message to merge the data into.</param>
+        /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
+        public static void MergeFrom(this IMessage message, Stream input)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(input, "input");
+            CodedInputStream codedInput = new CodedInputStream(input);
+            message.MergeFrom(codedInput);
+            codedInput.CheckReadEndOfStreamTag();
+        }
+
+        /// <summary>
+        /// Merges length-delimited data from the given stream into an existing message.
+        /// </summary>
+        /// <remarks>
+        /// The stream is expected to contain a length and then the data. Only the amount of data
+        /// specified by the length will be consumed.
+        /// </remarks>
+        /// <param name="message">The message to merge the data into.</param>
+        /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
+        public static void MergeDelimitedFrom(this IMessage message, Stream input)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(input, "input");
+            int size = (int) CodedInputStream.ReadRawVarint32(input);
+            Stream limitedStream = new LimitedInputStream(input, size);
+            message.MergeFrom(limitedStream);
+        }
+
+        /// <summary>
+        /// Converts the given message into a byte array in protobuf encoding.
+        /// </summary>
+        /// <param name="message">The message to convert.</param>
+        /// <returns>The message data as a byte array.</returns>
+        public static byte[] ToByteArray(this IMessage message)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            byte[] result = new byte[message.CalculateSize()];
+            CodedOutputStream output = new CodedOutputStream(result);
+            message.WriteTo(output);
+            output.CheckNoSpaceLeft();
+            return result;
+        }
+
+        /// <summary>
+        /// Writes the given message data to the given stream in protobuf encoding.
+        /// </summary>
+        /// <param name="message">The message to write to the stream.</param>
+        /// <param name="output">The stream to write to.</param>
+        public static void WriteTo(this IMessage message, MemoryStream output)
+        {
+            // 这里做了修改,去掉CodedOutputStream的gc
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(output, "output");
+            outputStream.Reset(output.GetBuffer(), (int)output.Length, (int)(output.Capacity - output.Length));
+            message.WriteTo(outputStream);
+            output.SetLength(outputStream.Position);
+        }
+
+        /// <summary>
+        /// Writes the length and then data of the given message to a stream.
+        /// </summary>
+        /// <param name="message">The message to write.</param>
+        /// <param name="output">The output stream to write to.</param>
+        public static void WriteDelimitedTo(this IMessage message, Stream output)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            ProtoPreconditions.CheckNotNull(output, "output");
+            CodedOutputStream codedOutput = new CodedOutputStream(output);
+            codedOutput.WriteRawVarint32((uint)message.CalculateSize());
+            message.WriteTo(codedOutput);
+            codedOutput.Flush();
+        }
+
+        /// <summary>
+        /// Converts the given message into a byte string in protobuf encoding.
+        /// </summary>
+        /// <param name="message">The message to convert.</param>
+        /// <returns>The message data as a byte string.</returns>
+        public static ByteString ToByteString(this IMessage message)
+        {
+            ProtoPreconditions.CheckNotNull(message, "message");
+            return ByteString.AttachBytes(message.ToByteArray());
+        }        
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/MessageExtensions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 23cd5cb094c72654abdb3a2a71ee5a80
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 241 - 0
Server/ThirdParty/Google.Protobuf/MessageParser.cs

@@ -0,0 +1,241 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+    
+using System;
+using System.IO;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// A general message parser, typically used by reflection-based code as all the methods
+    /// return simple <see cref="IMessage"/>.
+    /// </summary>
+    public class MessageParser
+    {
+        private Func<IMessage> factory;
+
+        internal MessageParser(Func<IMessage> factory)
+        {
+            this.factory = factory;
+        }
+
+        /// <summary>
+        /// Creates a template instance ready for population.
+        /// </summary>
+        /// <returns>An empty message.</returns>
+        internal IMessage CreateTemplate()
+        {
+            return factory();
+        }
+
+        /// <summary>
+        /// Parses a message from a byte array.
+        /// </summary>
+        /// <param name="data">The byte array containing the message. Must not be null.</param>
+        /// <returns>The newly parsed message.</returns>
+        public IMessage ParseFrom(byte[] data)
+        {
+            ProtoPreconditions.CheckNotNull(data, "data");
+            IMessage message = factory();
+            message.MergeFrom(data);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given byte string.
+        /// </summary>
+        /// <param name="data">The data to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public IMessage ParseFrom(ByteString data)
+        {
+            ProtoPreconditions.CheckNotNull(data, "data");
+            IMessage message = factory();
+            message.MergeFrom(data);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given stream.
+        /// </summary>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public IMessage ParseFrom(Stream input)
+        {
+            IMessage message = factory();
+            message.MergeFrom(input);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a length-delimited message from the given stream.
+        /// </summary>
+        /// <remarks>
+        /// The stream is expected to contain a length and then the data. Only the amount of data
+        /// specified by the length will be consumed.
+        /// </remarks>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public IMessage ParseDelimitedFrom(Stream input)
+        {
+            IMessage message = factory();
+            message.MergeDelimitedFrom(input);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given coded input stream.
+        /// </summary>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public IMessage ParseFrom(CodedInputStream input)
+        {
+            IMessage message = factory();
+            message.MergeFrom(input);
+            return message;
+        }
+
+    }
+
+    /// <summary>
+    /// A parser for a specific message type.
+    /// </summary>
+    /// <remarks>
+    /// <p>
+    /// This delegates most behavior to the
+    /// <see cref="IMessage.MergeFrom"/> implementation within the original type, but
+    /// provides convenient overloads to parse from a variety of sources.
+    /// </p>
+    /// <p>
+    /// Most applications will never need to create their own instances of this type;
+    /// instead, use the static <c>Parser</c> property of a generated message type to obtain a
+    /// parser for that type.
+    /// </p>
+    /// </remarks>
+    /// <typeparam name="T">The type of message to be parsed.</typeparam>
+    public sealed class MessageParser<T> : MessageParser where T : IMessage
+    {
+        // Implementation note: all the methods here *could* just delegate up to the base class and cast the result.
+        // The current implementation avoids a virtual method call and a cast, which *may* be significant in some cases.
+        // Benchmarking work is required to measure the significance - but it's only a few lines of code in any case.
+        // The API wouldn't change anyway - just the implementation - so this work can be deferred.
+        private readonly Func<T> factory; 
+
+        /// <summary>
+        /// Creates a new parser.
+        /// </summary>
+        /// <remarks>
+        /// The factory method is effectively an optimization over using a generic constraint
+        /// to require a parameterless constructor: delegates are significantly faster to execute.
+        /// </remarks>
+        /// <param name="factory">Function to invoke when a new, empty message is required.</param>
+        public MessageParser(Func<T> factory) : base(() => factory())
+        {
+            this.factory = factory;
+        }
+
+        /// <summary>
+        /// Creates a template instance ready for population.
+        /// </summary>
+        /// <returns>An empty message.</returns>
+        internal new T CreateTemplate()
+        {
+            return factory();
+        }
+
+        /// <summary>
+        /// Parses a message from a byte array.
+        /// </summary>
+        /// <param name="data">The byte array containing the message. Must not be null.</param>
+        /// <returns>The newly parsed message.</returns>
+        public new T ParseFrom(byte[] data)
+        {
+            ProtoPreconditions.CheckNotNull(data, "data");
+            T message = factory();
+            message.MergeFrom(data);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given byte string.
+        /// </summary>
+        /// <param name="data">The data to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public new T ParseFrom(ByteString data)
+        {
+            ProtoPreconditions.CheckNotNull(data, "data");
+            T message = factory();
+            message.MergeFrom(data);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given stream.
+        /// </summary>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public new T ParseFrom(Stream input)
+        {
+            T message = factory();
+            message.MergeFrom(input);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a length-delimited message from the given stream.
+        /// </summary>
+        /// <remarks>
+        /// The stream is expected to contain a length and then the data. Only the amount of data
+        /// specified by the length will be consumed.
+        /// </remarks>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public new T ParseDelimitedFrom(Stream input)
+        {
+            T message = factory();
+            message.MergeDelimitedFrom(input);
+            return message;
+        }
+
+        /// <summary>
+        /// Parses a message from the given coded input stream.
+        /// </summary>
+        /// <param name="input">The stream to parse.</param>
+        /// <returns>The parsed message.</returns>
+        public new T ParseFrom(CodedInputStream input)
+        {
+            T message = factory();
+            message.MergeFrom(input);
+            return message;
+        }
+
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/MessageParser.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1c7045bb59482bd4ba45e875f377820b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 64 - 0
Server/ThirdParty/Google.Protobuf/PropertyInfoExtensions.cs

@@ -0,0 +1,64 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Reflection;
+
+namespace Google.Protobuf.Compatibility
+{
+    /// <summary>
+    /// Extension methods for <see cref="PropertyInfo"/>, effectively providing
+    /// the familiar members from previous desktop framework versions while
+    /// targeting the newer releases, .NET Core etc.
+    /// </summary>
+    internal static class PropertyInfoExtensions
+    {
+        /// <summary>
+        /// Returns the public getter of a property, or null if there is no such getter
+        /// (either because it's read-only, or the getter isn't public).
+        /// </summary>
+        internal static MethodInfo GetGetMethod(this PropertyInfo target)
+        {
+            var method = target.GetGetMethod();
+            return method != null && method.IsPublic ? method : null;
+        }
+
+        /// <summary>
+        /// Returns the public setter of a property, or null if there is no such setter
+        /// (either because it's write-only, or the setter isn't public).
+        /// </summary>
+        internal static MethodInfo GetSetMethod(this PropertyInfo target)
+        {
+            var method = target.GetSetMethod();
+            return method != null && method.IsPublic ? method : null;
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/PropertyInfoExtensions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a1ba303b1b8d9d943a4c0189a878a77f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 79 - 0
Server/ThirdParty/Google.Protobuf/ProtoPreconditions.cs

@@ -0,0 +1,79 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// Helper methods for throwing exceptions when preconditions are not met.
+    /// </summary>
+    /// <remarks>
+    /// This class is used internally and by generated code; it is not particularly
+    /// expected to be used from application code, although nothing prevents it
+    /// from being used that way.
+    /// </remarks>
+    public static class ProtoPreconditions
+    {
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        public static T CheckNotNull<T>(T value, string name) where T : class
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+
+        /// <summary>
+        /// Throws an ArgumentNullException if the given value is null, otherwise
+        /// return the value to the caller.
+        /// </summary>
+        /// <remarks>
+        /// This is equivalent to <see cref="CheckNotNull{T}(T, string)"/> but without the type parameter
+        /// constraint. In most cases, the constraint is useful to prevent you from calling CheckNotNull
+        /// with a value type - but it gets in the way if either you want to use it with a nullable
+        /// value type, or you want to use it with an unconstrained type parameter.
+        /// </remarks>
+        internal static T CheckNotNullUnconstrained<T>(T value, string name)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(name);
+            }
+            return value;
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/ProtoPreconditions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ec2d66cb17ca74d49890f00206696580
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 581 - 0
Server/ThirdParty/Google.Protobuf/RepeatedField.cs

@@ -0,0 +1,581 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Google.Protobuf.Collections
+{
+    /// <summary>
+    /// The contents of a repeated field: essentially, a collection with some extra
+    /// restrictions (no null values) and capabilities (deep cloning).
+    /// </summary>
+    /// <remarks>
+    /// This implementation does not generally prohibit the use of types which are not
+    /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.
+    /// </remarks>
+    /// <typeparam name="T">The element type of the repeated field.</typeparam>
+    public sealed class RepeatedField<T> : IList<T>, IList 
+    {
+        private static readonly T[] EmptyArray = new T[0];
+        private const int MinArraySize = 8;
+
+        public T[] array = EmptyArray;
+        public int count = 0;
+
+        /// <summary>
+        /// Adds the entries from the given input stream, decoding them with the specified codec.
+        /// </summary>
+        /// <param name="input">The input stream to read from.</param>
+        /// <param name="codec">The codec to use in order to read each entry.</param>
+        public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
+        {
+            // TODO: Inline some of the Add code, so we can avoid checking the size on every
+            // iteration.
+            uint tag = input.LastTag;
+            var reader = codec.ValueReader;
+            // Non-nullable value types can be packed or not.
+            if (FieldCodec<T>.IsPackedRepeatedField(tag))
+            {
+                int length = input.ReadLength();
+                if (length > 0)
+                {
+                    int oldLimit = input.PushLimit(length);
+                    while (!input.ReachedLimit)
+                    {
+                        Add(reader(input));
+                    }
+                    input.PopLimit(oldLimit);
+                }
+                // Empty packed field. Odd, but valid - just ignore.
+            }
+            else
+            {
+                // Not packed... (possibly not packable)
+                do
+                {
+                    Add(reader(input));
+                } while (input.MaybeConsumeTag(tag));
+            }
+        }
+
+        /// <summary>
+        /// Calculates the size of this collection based on the given codec.
+        /// </summary>
+        /// <param name="codec">The codec to use when encoding each field.</param>
+        /// <returns>The number of bytes that would be written to a <see cref="CodedOutputStream"/> by <see cref="WriteTo"/>,
+        /// using the same codec.</returns>
+        public int CalculateSize(FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return 0;
+            }
+            uint tag = codec.Tag;
+            if (codec.PackedRepeatedField)
+            {
+                int dataSize = CalculatePackedDataSize(codec);
+                return CodedOutputStream.ComputeRawVarint32Size(tag) +
+                    CodedOutputStream.ComputeLengthSize(dataSize) +
+                    dataSize;
+            }
+            else
+            {
+                var sizeCalculator = codec.ValueSizeCalculator;
+                int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
+                for (int i = 0; i < count; i++)
+                {
+                    size += sizeCalculator(array[i]);
+                }
+                return size;
+            }
+        }
+
+        private int CalculatePackedDataSize(FieldCodec<T> codec)
+        {
+            int fixedSize = codec.FixedSize;
+            if (fixedSize == 0)
+            {
+                var calculator = codec.ValueSizeCalculator;
+                int tmp = 0;
+                for (int i = 0; i < count; i++)
+                {
+                    tmp += calculator(array[i]);
+                }
+                return tmp;
+            }
+            else
+            {
+                return fixedSize * Count;
+            }
+        }
+
+        /// <summary>
+        /// Writes the contents of this collection to the given <see cref="CodedOutputStream"/>,
+        /// encoding each value using the specified codec.
+        /// </summary>
+        /// <param name="output">The output stream to write to.</param>
+        /// <param name="codec">The codec to use when encoding each value.</param>
+        public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
+        {
+            if (count == 0)
+            {
+                return;
+            }
+            var writer = codec.ValueWriter;
+            var tag = codec.Tag;
+            if (codec.PackedRepeatedField)
+            {
+                // Packed primitive type
+                uint size = (uint)CalculatePackedDataSize(codec);
+                output.WriteTag(tag);
+                output.WriteRawVarint32(size);
+                for (int i = 0; i < count; i++)
+                {
+                    writer(output, array[i]);
+                }
+            }
+            else
+            {
+                // Not packed: a simple tag/value pair for each value.
+                // Can't use codec.WriteTagAndValue, as that omits default values.
+                for (int i = 0; i < count; i++)
+                {
+                    output.WriteTag(tag);
+                    writer(output, array[i]);
+                }
+            }
+        }
+
+        private void EnsureSize(int size)
+        {
+            if (array.Length < size)
+            {
+                size = Math.Max(size, MinArraySize);
+                int newSize = Math.Max(array.Length * 2, size);
+                var tmp = new T[newSize];
+                Array.Copy(array, 0, tmp, 0, array.Length);
+                array = tmp;
+            }
+        }
+
+        /// <summary>
+        /// Adds the specified item to the collection.
+        /// </summary>
+        /// <param name="item">The item to add.</param>
+        public void Add(T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, "item");
+            EnsureSize(count + 1);
+            array[count++] = item;
+        }
+
+        /// <summary>
+        /// Removes all items from the collection.
+        /// </summary>
+        public void Clear()
+        {
+            // ET修改,这里不释放数组,避免gc
+            //array = EmptyArray;
+            count = 0;
+        }
+
+        /// <summary>
+        /// Determines whether this collection contains the given item.
+        /// </summary>
+        /// <param name="item">The item to find.</param>
+        /// <returns><c>true</c> if this collection contains the given item; <c>false</c> otherwise.</returns>
+        public bool Contains(T item)
+        {
+            return IndexOf(item) != -1;
+        }
+
+        /// <summary>
+        /// Copies this collection to the given array.
+        /// </summary>
+        /// <param name="array">The array to copy to.</param>
+        /// <param name="arrayIndex">The first index of the array to copy to.</param>
+        public void CopyTo(T[] array, int arrayIndex)
+        {
+            Array.Copy(this.array, 0, array, arrayIndex, count);
+        }
+
+        /// <summary>
+        /// Removes the specified item from the collection
+        /// </summary>
+        /// <param name="item">The item to remove.</param>
+        /// <returns><c>true</c> if the item was found and removed; <c>false</c> otherwise.</returns>
+        public bool Remove(T item)
+        {
+            int index = IndexOf(item);
+            if (index == -1)
+            {
+                return false;
+            }
+            Array.Copy(array, index + 1, array, index, count - index - 1);
+            count--;
+            array[count] = default(T);
+            return true;
+        }
+
+        /// <summary>
+        /// Gets the number of elements contained in the collection.
+        /// </summary>
+        public int Count
+        {
+            get
+            {
+                return count;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the collection is read-only.
+        /// </summary>
+        public bool IsReadOnly
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Adds all of the specified values into this collection.
+        /// </summary>
+        /// <param name="values">The values to add to this collection.</param>
+        public void AddRange(IEnumerable<T> values)
+        {
+            ProtoPreconditions.CheckNotNull(values, "values");
+
+            // Optimization 1: If the collection we're adding is already a RepeatedField<T>,
+            // we know the values are valid.
+            var otherRepeatedField = values as RepeatedField<T>;
+            if (otherRepeatedField != null)
+            {
+                EnsureSize(count + otherRepeatedField.count);
+                Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
+                count += otherRepeatedField.count;
+                return;
+            }
+
+            // Optimization 2: The collection is an ICollection, so we can expand
+            // just once and ask the collection to copy itself into the array.
+            var collection = values as ICollection;
+            if (collection != null)
+            {
+                var extraCount = collection.Count;
+                // For reference types and nullable value types, we need to check that there are no nulls
+                // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
+                // We expect the JITter to optimize this test to true/false, so it's effectively conditional
+                // specialization.
+                if (default(T) == null)
+                {
+                    // TODO: Measure whether iterating once to check and then letting the collection copy
+                    // itself is faster or slower than iterating and adding as we go. For large
+                    // collections this will not be great in terms of cache usage... but the optimized
+                    // copy may be significantly faster than doing it one at a time.
+                    foreach (var item in collection)
+                    {
+                        if (item == null)
+                        {
+                            throw new ArgumentException("Sequence contained null element", "values");
+                        }
+                    }
+                }
+                EnsureSize(count + extraCount);
+                collection.CopyTo(array, count);
+                count += extraCount;
+                return;
+            }
+
+            // We *could* check for ICollection<T> as well, but very very few collections implement
+            // ICollection<T> but not ICollection. (HashSet<T> does, for one...)
+
+            // Fall back to a slower path of adding items one at a time.
+            foreach (T item in values)
+            {
+                Add(item);
+            }
+        }
+
+        /// <summary>
+        /// Adds all of the specified values into this collection. This method is present to
+        /// allow repeated fields to be constructed from queries within collection initializers.
+        /// Within non-collection-initializer code, consider using the equivalent <see cref="AddRange"/>
+        /// method instead for clarity.
+        /// </summary>
+        /// <param name="values">The values to add to this collection.</param>
+        public void Add(IEnumerable<T> values)
+        {
+            AddRange(values);
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the collection.
+        /// </summary>
+        /// <returns>
+        /// An enumerator that can be used to iterate through the collection.
+        /// </returns>
+        public IEnumerator<T> GetEnumerator()
+        {
+            for (int i = 0; i < count; i++)
+            {
+                yield return array[i];
+            }
+        }
+
+        /// <summary>
+        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
+        /// </summary>
+        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
+        /// <returns>
+        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
+        /// </returns>
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as RepeatedField<T>);
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through a collection.
+        /// </summary>
+        /// <returns>
+        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
+        /// </returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        /// <summary>
+        /// Returns a hash code for this instance.
+        /// </summary>
+        /// <returns>
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// </returns>
+        public override int GetHashCode()
+        {
+            int hash = 0;
+            for (int i = 0; i < count; i++)
+            {
+                hash = hash * 31 + array[i].GetHashCode();
+            }
+            return hash;
+        }
+
+        /// <summary>
+        /// Compares this repeated field with another for equality.
+        /// </summary>
+        /// <param name="other">The repeated field to compare this with.</param>
+        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal repeated field; <c>false</c> otherwise.</returns>
+        public bool Equals(RepeatedField<T> other)
+        {
+            if (ReferenceEquals(other, null))
+            {
+                return false;
+            }
+            if (ReferenceEquals(other, this))
+            {
+                return true;
+            }
+            if (other.Count != this.Count)
+            {
+                return false;
+            }
+            EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+            for (int i = 0; i < count; i++)
+            {
+                if (!comparer.Equals(array[i], other.array[i]))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Returns the index of the given item within the collection, or -1 if the item is not
+        /// present.
+        /// </summary>
+        /// <param name="item">The item to find in the collection.</param>
+        /// <returns>The zero-based index of the item, or -1 if it is not found.</returns>
+        public int IndexOf(T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, "item");
+            EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+            for (int i = 0; i < count; i++)
+            {
+                if (comparer.Equals(array[i], item))
+                {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /// <summary>
+        /// Inserts the given item at the specified index.
+        /// </summary>
+        /// <param name="index">The index at which to insert the item.</param>
+        /// <param name="item">The item to insert.</param>
+        public void Insert(int index, T item)
+        {
+            ProtoPreconditions.CheckNotNullUnconstrained(item, "item");
+            if (index < 0 || index > count)
+            {
+                throw new ArgumentOutOfRangeException("index");
+            }
+            EnsureSize(count + 1);
+            Array.Copy(array, index, array, index + 1, count - index);
+            array[index] = item;
+            count++;
+        }
+
+        /// <summary>
+        /// Removes the item at the given index.
+        /// </summary>
+        /// <param name="index">The zero-based index of the item to remove.</param>
+        public void RemoveAt(int index)
+        {
+            if (index < 0 || index >= count)
+            {
+                throw new ArgumentOutOfRangeException("index");
+            }
+            Array.Copy(array, index + 1, array, index, count - index - 1);
+            count--;
+            array[count] = default(T);
+        }
+
+        /// <summary>
+        /// Gets or sets the item at the specified index.
+        /// </summary>
+        /// <value>
+        /// The element at the specified index.
+        /// </value>
+        /// <param name="index">The zero-based index of the element to get or set.</param>
+        /// <returns>The item at the specified index.</returns>
+        public T this[int index]
+        {
+            get
+            {
+                if (index < 0 || index >= count)
+                {
+                    throw new ArgumentOutOfRangeException("index");
+                }
+                return array[index];
+            }
+            set
+            {
+                if (index < 0 || index >= count)
+                {
+                    throw new ArgumentOutOfRangeException("index");
+                }
+                ProtoPreconditions.CheckNotNullUnconstrained(value, "value");
+                array[index] = value;
+            }
+        }
+
+        #region Explicit interface implementation for IList and ICollection.
+        bool IList.IsFixedSize
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        void ICollection.CopyTo(Array array, int index)
+        {
+            Array.Copy(this.array, 0, array, index, count);
+        }
+
+        bool ICollection.IsSynchronized
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        object ICollection.SyncRoot
+        {
+            get
+            {
+                return this;
+            }
+        }
+
+        object IList.this[int index]
+        {
+            get { return this[index]; }
+            set { this[index] = (T)value; }
+        }
+
+        int IList.Add(object value)
+        {
+            Add((T)value);
+            return count - 1;
+        }
+
+        bool IList.Contains(object value)
+        {
+            return (value is T && Contains((T)value));
+        }
+
+        int IList.IndexOf(object value)
+        {
+            if (!(value is T))
+            {
+                return -1;
+            }
+            return IndexOf((T)value);
+        }
+
+        void IList.Insert(int index, object value)
+        {
+            Insert(index, (T)value);
+        }
+
+        void IList.Remove(object value)
+        {
+            if (!(value is T))
+            {
+                return;
+            }
+            Remove((T)value);
+        }
+        #endregion        
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/RepeatedField.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 462fd9852b348c34094dbb0e82649335
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 64 - 0
Server/ThirdParty/Google.Protobuf/StreamExtensions.cs

@@ -0,0 +1,64 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+
+namespace Google.Protobuf.Compatibility
+{
+    /// <summary>
+    /// Extension methods for <see cref="Stream"/> in order to provide
+    /// backwards compatibility with .NET 3.5
+    /// </summary>
+    public static class StreamExtensions
+    {
+        // 81920 seems to be the default buffer size used in .NET 4.5.1
+        private const int BUFFER_SIZE = 81920;
+
+        /// <summary>
+        /// Write the contents of the current stream to the destination stream
+        /// </summary>
+        public static void CopyTo(this Stream source, Stream destination)
+        {
+            if (destination == null)
+            {
+                throw new ArgumentNullException("destination");
+            }
+
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int numBytesRead;
+            while ((numBytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
+                destination.Write(buffer, 0, numBytesRead);
+            }
+        }
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/StreamExtensions.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 51467fdc5aeb0374baba8cb484d68f4c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 104 - 0
Server/ThirdParty/Google.Protobuf/WireFormat.cs

@@ -0,0 +1,104 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+namespace Google.Protobuf
+{
+    /// <summary>
+    /// This class is used internally by the Protocol Buffer Library and generated
+    /// message implementations. It is public only for the sake of those generated
+    /// messages. Others should not use this class directly.
+    /// <para>
+    /// This class contains constants and helper functions useful for dealing with
+    /// the Protocol Buffer wire format.
+    /// </para>
+    /// </summary>
+    public static class WireFormat
+    {
+        /// <summary>
+        /// Wire types within protobuf encoding.
+        /// </summary>
+        public enum WireType : uint
+        {
+            /// <summary>
+            /// Variable-length integer.
+            /// </summary>
+            Varint = 0,
+            /// <summary>
+            /// A fixed-length 64-bit value.
+            /// </summary>
+            Fixed64 = 1,
+            /// <summary>
+            /// A length-delimited value, i.e. a length followed by that many bytes of data.
+            /// </summary>
+            LengthDelimited = 2,
+            /// <summary>
+            /// A "start group" value - not supported by this implementation.
+            /// </summary>
+            StartGroup = 3,
+            /// <summary>
+            /// An "end group" value - not supported by this implementation.
+            /// </summary>
+            EndGroup = 4,
+            /// <summary>
+            /// A fixed-length 32-bit value.
+            /// </summary>
+            Fixed32 = 5
+        }
+        
+        private const int TagTypeBits = 3;
+        private const uint TagTypeMask = (1 << TagTypeBits) - 1;
+
+        /// <summary>
+        /// Given a tag value, determines the wire type (lower 3 bits).
+        /// </summary>
+        public static WireType GetTagWireType(uint tag)
+        {
+            return (WireType) (tag & TagTypeMask);
+        }
+
+        /// <summary>
+        /// Given a tag value, determines the field number (the upper 29 bits).
+        /// </summary>
+        public static int GetTagFieldNumber(uint tag)
+        {
+            return (int) tag >> TagTypeBits;
+        }
+
+        /// <summary>
+        /// Makes a tag value given a field number and wire type.
+        /// </summary>
+        public static uint MakeTag(int fieldNumber, WireType wireType)
+        {
+            return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
+        }        
+    }
+}

+ 11 - 0
Server/ThirdParty/Google.Protobuf/WireFormat.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7bf59a3ce666cd14392ea36d61668d87
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 0 - 33
Server/ThirdParty/KcpLib/KcpLib.csproj

@@ -1,33 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-    <LangVersion>7.3</LangVersion>
-  </PropertyGroup>
-  <PropertyGroup>
-    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
-    <OutputPath>..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <OutputPath>..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-  <ItemGroup>
-    <None Include="..\..\..\Unity\Assets\Plugins\x86_64\kcp.dll" Link="kcp.dll">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
-    <None Update="libkcp.dylib">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
-    <None Update="libkcp.so">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="..\..\..\Unity\Assets\Model\Module\Message\Network\KCP\Kcp.cs">
-      <Link>Kcp.cs</Link>
-    </Compile>
-  </ItemGroup>
-</Project>

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/IO/BsonReader.cs

@@ -279,7 +279,7 @@ namespace MongoDB.Bson.IO
                 bsonWriter.WriteEndDocument();
                 bsonWriter.WriteEndDocument();
 
 
                 byte[] memoryStreamBuffer;
                 byte[] memoryStreamBuffer;
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
                 memoryStreamBuffer = memoryStream.ToArray();
                 memoryStreamBuffer = memoryStream.ToArray();
 #else
 #else
                 memoryStreamBuffer = memoryStream.GetBuffer();
                 memoryStreamBuffer = memoryStream.GetBuffer();

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/IO/BsonStreamAdapter.cs

@@ -297,7 +297,7 @@ namespace MongoDB.Bson.IO
                 if (b == 0)
                 if (b == 0)
                 {
                 {
                     byte[] memoryStreamBuffer;
                     byte[] memoryStreamBuffer;
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
                     memoryStreamBuffer = memoryStream.ToArray();
                     memoryStreamBuffer = memoryStream.ToArray();
 #else
 #else
                     memoryStreamBuffer = memoryStream.GetBuffer();
                     memoryStreamBuffer = memoryStream.GetBuffer();

+ 0 - 71
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/MongoDB.Bson.csproj

@@ -1,71 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <LangVersion>7.3</LangVersion>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <WarningsAsErrors />
-    <CodeAnalysisRuleSet>../MongoDBLegacy.ruleset</CodeAnalysisRuleSet>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <AssemblyTitle>MongoDB.Bson</AssemblyTitle>
-    <Product>MongoDB.Bson</Product>
-    <Company>MongoDB Inc.</Company>
-    <Copyright>Copyright © 2010-present MongoDB Inc.</Copyright>
-    <Description>Official MongoDB supported BSON library. See http://www.mongodb.org/display/DOCS/CSharp+Language+Center for more details.</Description>
-    <Authors>rstam;craiggwilson</Authors>
-    <PackageIconUrl>http://jobs.mongodb.org/files/logos/889002/889002.png</PackageIconUrl>
-    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-    <PackageDescription>MongoDB's Official Bson Library.</PackageDescription>
-    <PackageProjectUrl>http://www.mongodb.org/display/DOCS/CSharp+Language+Center</PackageProjectUrl>
-    <PackageLicenseFile>License.txt</PackageLicenseFile>
-    <PackageTags>mongodb;mongo;nosql;bson</PackageTags>
-    <PackageLanguage>en-US</PackageLanguage>
-    <IncludeSymbols>true</IncludeSymbols>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Version)'==''">
-    <Version>0.0.0-local</Version>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <DefineConstants>TRACE</DefineConstants>
-  </PropertyGroup>
-  
-  <PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
-    <NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.1" PrivateAssets="All" />
-  </ItemGroup>
-
-  <ItemGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
-    <PackageReference Include="System.Collections.NonGeneric" Version="4.0.1" />
-    <PackageReference Include="System.Diagnostics.Process" Version="4.1.0" />
-    <PackageReference Include="System.Dynamic.Runtime" Version="4.0.11" />
-    <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\MongoDB.Shared\Hasher.cs" Link="Hasher.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\..\License.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
-  </ItemGroup>
-
-</Project>

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/ObjectModel/ObjectId.cs

@@ -377,7 +377,7 @@ namespace MongoDB.Bson
         // private static methods
         // private static methods
         private static int GetAppDomainId()
         private static int GetAppDomainId()
         {
         {
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
             return 1;
             return 1;
 #else
 #else
             return AppDomain.CurrentDomain.Id;
             return AppDomain.CurrentDomain.Id;

+ 0 - 23
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Properties/AssemblyInfo.cs

@@ -1,23 +0,0 @@
-/* Copyright 2010-present MongoDB Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-[assembly: CLSCompliant(true)]
-[assembly: ComVisible(false)]
-
-[assembly: InternalsVisibleTo("MongoDB.Bson.Tests")]

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Serialization/BsonClassMap.cs

@@ -1563,7 +1563,7 @@ namespace MongoDB.Bson.Serialization
         {
         {
             var interfaceType = interfacePropertyInfo.DeclaringType;
             var interfaceType = interfacePropertyInfo.DeclaringType;
 
 
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
             var actualTypeInfo = actualType.GetTypeInfo();
             var actualTypeInfo = actualType.GetTypeInfo();
             var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
             var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
             var actualTypePropertyInfos = actualTypeInfo.GetMembers(bindingFlags).OfType<PropertyInfo>();
             var actualTypePropertyInfos = actualTypeInfo.GetMembers(bindingFlags).OfType<PropertyInfo>();

+ 2 - 2
Server/ThirdParty/MongoDBDriver/MongoDB.Bson/Serialization/Serializers/CultureInfoSerializer.cs

@@ -73,7 +73,7 @@ namespace MongoDB.Bson.Serialization.Serializers
                             case Flags.UseUserOverride: useUserOverride = _booleanSerializer.Deserialize(context); break;
                             case Flags.UseUserOverride: useUserOverride = _booleanSerializer.Deserialize(context); break;
                         }
                         }
                     });
                     });
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
                                         if (!useUserOverride)
                                         if (!useUserOverride)
                     {
                     {
                         throw new FormatException("CultureInfo does not support useUserOverride on this version of the .NET Framework.");
                         throw new FormatException("CultureInfo does not support useUserOverride on this version of the .NET Framework.");
@@ -101,7 +101,7 @@ namespace MongoDB.Bson.Serialization.Serializers
         {
         {
             var bsonWriter = context.Writer;
             var bsonWriter = context.Writer;
 
 
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
             var useUserOverride = true;
             var useUserOverride = true;
 #else
 #else
             var useUserOverride = value.UseUserOverride;
             var useUserOverride = value.UseUserOverride;

+ 0 - 28
Server/ThirdParty/MongoDBDriver/MongoDB.Driver.Core/Properties/AssemblyInfo.cs

@@ -1,28 +0,0 @@
-/* Copyright 2010-present MongoDB Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-[assembly: CLSCompliant(true)]
-[assembly: ComVisible(false)]
-
-[assembly: InternalsVisibleTo("MongoDB.Driver.Core.FunctionalTests")]
-[assembly: InternalsVisibleTo("MongoDB.Driver.Core.TestHelpers")]
-[assembly: InternalsVisibleTo("MongoDB.Driver.Core.Tests")]
-[assembly: InternalsVisibleTo("MongoDB.Driver.Legacy.Tests")]
-[assembly: InternalsVisibleTo("MongoDB.Driver.Tests")]
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/GridFSDownloadStream.cs

@@ -41,7 +41,7 @@ namespace MongoDB.Driver.GridFS
         public abstract GridFSFileInfo<TFileId> FileInfo { get; }
         public abstract GridFSFileInfo<TFileId> FileInfo { get; }
 
 
         // public methods
         // public methods
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
         /// <summary>
         /// <summary>
         /// Closes the GridFS stream.
         /// Closes the GridFS stream.
         /// </summary>
         /// </summary>

+ 0 - 70
Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/MongoDB.Driver.GridFS.csproj

@@ -1,70 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <LangVersion>7.3</LangVersion>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <WarningsAsErrors />
-    <CodeAnalysisRuleSet>../MongoDB.ruleset</CodeAnalysisRuleSet>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <AssemblyTitle>MongoDB.Driver.GridFS</AssemblyTitle>
-    <Product>MongoDB.Driver.GridFS</Product>
-    <Company>MongoDB Inc.</Company>
-    <Copyright>Copyright © 2010-present MongoDB Inc.</Copyright>
-    <Description>Official MongoDB supported driver for MongoDB GridFS implementation.  See http://www.mongodb.org/display/DOCS/CSharp+Language+Center for more details.</Description>
-    <Authors>rstam;craiggwilson</Authors>
-    <PackageIconUrl>http://jobs.mongodb.org/files/logos/889002/889002.png</PackageIconUrl>
-    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-    <PackageDescription>GridFS Component of the Official MongoDB .NET Driver.</PackageDescription>
-    <PackageProjectUrl>http://www.mongodb.org/display/DOCS/CSharp+Language+Center</PackageProjectUrl>
-    <PackageLicenseFile>License.txt</PackageLicenseFile>
-    <PackageTags>mongodb;mongo;nosql;gridfs</PackageTags>
-    <PackageLanguage>en-US</PackageLanguage>
-    <IncludeSymbols>true</IncludeSymbols>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Version)'==''">
-    <Version>0.0.0-local</Version>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <DefineConstants>TRACE</DefineConstants>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
-    <NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.1" PrivateAssets="All" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\MongoDB.Bson\MongoDB.Bson.csproj" />
-    <ProjectReference Include="..\MongoDB.Driver\MongoDB.Driver.csproj" />
-    <ProjectReference Include="..\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\MongoDB.Shared\IncrementalMD5.cs" Link="IncrementalMD5.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\..\License.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
-  </ItemGroup>
-
-</Project>

+ 0 - 23
Server/ThirdParty/MongoDBDriver/MongoDB.Driver.GridFS/Properties/AssemblyInfo.cs

@@ -1,23 +0,0 @@
-/* Copyright 2015-present MongoDB Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-[assembly: CLSCompliant(true)]
-[assembly: ComVisible(false)]
-
-[assembly: InternalsVisibleTo("MongoDB.Driver.GridFS.Tests")]

+ 1 - 1
Server/ThirdParty/MongoDBDriver/MongoDB.Driver/Linq/MethodHelper.cs

@@ -89,7 +89,7 @@ namespace MongoDB.Driver.Linq
             }
             }
 
 
             var declaringTypeDefinition = methodInfo.DeclaringType.GetGenericTypeDefinition();
             var declaringTypeDefinition = methodInfo.DeclaringType.GetGenericTypeDefinition();
-#if NETSTANDARD1_5 || NETSTANDARD1_6
+#if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
             var bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
             var bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
             var parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
             var parameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
             return declaringTypeDefinition.GetTypeInfo().GetMethods(bindingFlags)
             return declaringTypeDefinition.GetTypeInfo().GetMethods(bindingFlags)

+ 0 - 74
Server/ThirdParty/MongoDBDriver/MongoDB.Driver/MongoDB.Driver.csproj

@@ -1,74 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <LangVersion>7.3</LangVersion>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <WarningsAsErrors />
-    <CodeAnalysisRuleSet>../MongoDB.ruleset</CodeAnalysisRuleSet>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <AssemblyTitle>MongoDB.Driver</AssemblyTitle>
-    <Product>MongoDB.Driver</Product>
-    <Company>MongoDB Inc.</Company>
-    <Copyright>Copyright © 2010-present MongoDB Inc.</Copyright>
-    <Description>Official MongoDB supported driver for MongoDB.  See http://www.mongodb.org/display/DOCS/CSharp+Language+Center for more details.</Description>
-    <Authors>rstam;craiggwilson</Authors>
-    <PackageIconUrl>http://jobs.mongodb.org/files/logos/889002/889002.png</PackageIconUrl>
-    <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
-    <PackageDescription>Official .NET driver for MongoDB.</PackageDescription>
-    <PackageProjectUrl>http://www.mongodb.org/display/DOCS/CSharp+Language+Center</PackageProjectUrl>
-    <PackageLicenseFile>License.txt</PackageLicenseFile>
-    <PackageTags>mongodb;mongo;nosql</PackageTags>
-    <PackageLanguage>en-US</PackageLanguage>
-    <IncludeSymbols>true</IncludeSymbols>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Version)'==''">
-    <Version>0.0.0-local</Version>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <DefineConstants>TRACE</DefineConstants>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(TargetFramework)'=='netstandard1.5'">
-    <NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <OutputPath>..\..\..\..\Bin\</OutputPath>
-  </PropertyGroup>
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.1" PrivateAssets="All" />
-  </ItemGroup>
-
-  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.5'">
-    <PackageReference Include="System.ComponentModel.TypeConverter" Version="4.1.0" />
-    <PackageReference Include="System.Linq.Queryable" Version="4.0.1" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\MongoDB.Bson\MongoDB.Bson.csproj" />
-    <ProjectReference Include="..\MongoDB.Driver.Core\MongoDB.Driver.Core.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Compile Include="..\MongoDB.Shared\ApplicationNameHelper.cs" Link="ApplicationNameHelper.cs" />
-    <Compile Include="..\MongoDB.Shared\Hasher.cs" Link="Support\Hasher.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Include="..\..\License.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
-  </ItemGroup>
-
-</Project>

BIN=BIN
Server/ThirdParty/Pathfinding.Ionic.Zip.Reduced.dll


+ 49 - 0
Server/ThirdParty/Server.ThirdParty.csproj

@@ -0,0 +1,49 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>netcoreapp3.1</TargetFramework>
+    </PropertyGroup>
+
+    <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+      <DefineConstants>TRACE;FEAT_SAFE</DefineConstants>
+      <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    </PropertyGroup>
+
+    <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+      <DefineConstants>TRACE;FEAT_SAFE</DefineConstants>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <Compile Remove="MongoDBDriver\MongoDB.Driver.Core\obj\**" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <EmbeddedResource Remove="MongoDBDriver\MongoDB.Driver.Core\obj\**" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <None Remove="MongoDBDriver\MongoDB.Driver.Core\obj\**" />
+      <None Remove="Google.Protobuf\CodedOutputStream.cs.meta" />
+      <None Remove="Google.Protobuf\FieldCodec.cs.meta" />
+      <None Remove="Google.Protobuf\CodedOutputStream.ComputeSize.cs.meta" />
+      <None Remove="Google.Protobuf\CodedInputStream.cs.meta" />
+      <None Remove="Google.Protobuf\ByteString.cs.meta" />
+      <None Remove="Google.Protobuf\ByteArray.cs.meta" />
+      <None Remove="Google.Protobuf\ICustomDiagnosticMessage.cs.meta" />
+      <None Remove="Google.Protobuf\IMessage.cs.meta" />
+      <None Remove="Google.Protobuf\InvalidProtocolBufferException.cs.meta" />
+      <None Remove="Google.Protobuf\LimitedInputStream.cs.meta" />
+      <None Remove="Google.Protobuf\MessageExtensions.cs.meta" />
+      <None Remove="Google.Protobuf\MessageParser.cs.meta" />
+      <None Remove="Google.Protobuf\PropertyInfoExtensions.cs.meta" />
+      <None Remove="Google.Protobuf\ProtoPreconditions.cs.meta" />
+      <None Remove="Google.Protobuf\RepeatedField.cs.meta" />
+      <None Remove="Google.Protobuf\StreamExtensions.cs.meta" />
+      <None Remove="Google.Protobuf\WireFormat.cs.meta" />
+    </ItemGroup>
+
+    <ItemGroup>
+      <PackageReference Include="DnsClient" Version="1.0.7" />
+    </ItemGroup>
+
+</Project>

+ 0 - 0
Server/ThirdParty/KcpLib/libkcp.dylib → Server/ThirdParty/libkcp.dylib


+ 0 - 0
Server/ThirdParty/KcpLib/libkcp.so → Server/ThirdParty/libkcp.so


+ 712 - 0
Server/ThirdParty/protobuf-net/BclHelpers.cs

@@ -0,0 +1,712 @@
+using System;
+using System.Reflection;
+namespace ProtoBuf
+{
+    internal enum TimeSpanScale
+    {
+        Days = 0,
+        Hours = 1,
+        Minutes = 2,
+        Seconds = 3,
+        Milliseconds = 4,
+        Ticks = 5,
+
+        MinMax = 15
+    }
+
+    /// <summary>
+    /// Provides support for common .NET types that do not have a direct representation
+    /// in protobuf, using the definitions from bcl.proto
+    /// </summary>
+    public static class BclHelpers
+    {
+        /// <summary>
+        /// Creates a new instance of the specified type, bypassing the constructor.
+        /// </summary>
+        /// <param name="type">The type to create</param>
+        /// <returns>The new instance</returns>
+        /// <exception cref="NotSupportedException">If the platform does not support constructor-skipping</exception>
+        public static object GetUninitializedObject(Type type)
+        {
+#if COREFX
+            object obj = TryGetUninitializedObjectWithFormatterServices(type);
+            if (obj != null) return obj;
+#endif
+#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
+            return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
+#else
+            throw new NotSupportedException("Constructor-skipping is not supported on this platform");
+#endif
+        }
+
+#if COREFX // this is inspired by DCS: https://github.com/dotnet/corefx/blob/c02d33b18398199f6acc17d375dab154e9a1df66/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs#L854-L894
+        static Func<Type, object> getUninitializedObject;
+        static internal object TryGetUninitializedObjectWithFormatterServices(Type type)
+        {
+            if (getUninitializedObject == null)
+            {
+                try {
+                    var formatterServiceType = typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices");
+                    if (formatterServiceType == null)
+                    {
+                        // fallback for .Net Core 3.0
+                        var formatterAssembly = Assembly.Load(new AssemblyName("System.Runtime.Serialization.Formatters"));
+                        formatterServiceType = formatterAssembly.GetType("System.Runtime.Serialization.FormatterServices");
+                    }
+                    MethodInfo method = formatterServiceType?.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
+                    if (method != null)
+                    {
+                        getUninitializedObject = (Func<Type, object>)method.CreateDelegate(typeof(Func<Type, object>));
+                    }
+                }
+                catch  { /* best efforts only */ }
+                if(getUninitializedObject == null) getUninitializedObject = x => null;
+            }
+            return getUninitializedObject(type);
+        }
+#endif
+
+        const int FieldTimeSpanValue = 0x01, FieldTimeSpanScale = 0x02, FieldTimeSpanKind = 0x03;
+
+        internal static readonly DateTime[] EpochOrigin = {
+            new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
+            new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc),
+            new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local)
+        };
+
+        /// <summary>
+        /// The default value for dates that are following google.protobuf.Timestamp semantics
+        /// </summary>
+        private static readonly DateTime TimestampEpoch = EpochOrigin[(int)DateTimeKind.Utc];
+
+        /// <summary>
+        /// Writes a TimeSpan to a protobuf stream using protobuf-net's own representation, bcl.TimeSpan
+        /// </summary>
+        public static void WriteTimeSpan(TimeSpan timeSpan, ProtoWriter dest)
+        {
+            WriteTimeSpanImpl(timeSpan, dest, DateTimeKind.Unspecified);
+        }
+
+        private static void WriteTimeSpanImpl(TimeSpan timeSpan, ProtoWriter dest, DateTimeKind kind)
+        {
+            if (dest == null) throw new ArgumentNullException(nameof(dest));
+            long value;
+            switch (dest.WireType)
+            {
+                case WireType.String:
+                case WireType.StartGroup:
+                    TimeSpanScale scale;
+                    value = timeSpan.Ticks;
+                    if (timeSpan == TimeSpan.MaxValue)
+                    {
+                        value = 1;
+                        scale = TimeSpanScale.MinMax;
+                    }
+                    else if (timeSpan == TimeSpan.MinValue)
+                    {
+                        value = -1;
+                        scale = TimeSpanScale.MinMax;
+                    }
+                    else if (value % TimeSpan.TicksPerDay == 0)
+                    {
+                        scale = TimeSpanScale.Days;
+                        value /= TimeSpan.TicksPerDay;
+                    }
+                    else if (value % TimeSpan.TicksPerHour == 0)
+                    {
+                        scale = TimeSpanScale.Hours;
+                        value /= TimeSpan.TicksPerHour;
+                    }
+                    else if (value % TimeSpan.TicksPerMinute == 0)
+                    {
+                        scale = TimeSpanScale.Minutes;
+                        value /= TimeSpan.TicksPerMinute;
+                    }
+                    else if (value % TimeSpan.TicksPerSecond == 0)
+                    {
+                        scale = TimeSpanScale.Seconds;
+                        value /= TimeSpan.TicksPerSecond;
+                    }
+                    else if (value % TimeSpan.TicksPerMillisecond == 0)
+                    {
+                        scale = TimeSpanScale.Milliseconds;
+                        value /= TimeSpan.TicksPerMillisecond;
+                    }
+                    else
+                    {
+                        scale = TimeSpanScale.Ticks;
+                    }
+
+                    SubItemToken token = ProtoWriter.StartSubItem(null, dest);
+
+                    if (value != 0)
+                    {
+                        ProtoWriter.WriteFieldHeader(FieldTimeSpanValue, WireType.SignedVariant, dest);
+                        ProtoWriter.WriteInt64(value, dest);
+                    }
+                    if (scale != TimeSpanScale.Days)
+                    {
+                        ProtoWriter.WriteFieldHeader(FieldTimeSpanScale, WireType.Variant, dest);
+                        ProtoWriter.WriteInt32((int)scale, dest);
+                    }
+                    if (kind != DateTimeKind.Unspecified)
+                    {
+                        ProtoWriter.WriteFieldHeader(FieldTimeSpanKind, WireType.Variant, dest);
+                        ProtoWriter.WriteInt32((int)kind, dest);
+                    }
+                    ProtoWriter.EndSubItem(token, dest);
+                    break;
+                case WireType.Fixed64:
+                    ProtoWriter.WriteInt64(timeSpan.Ticks, dest);
+                    break;
+                default:
+                    throw new ProtoException("Unexpected wire-type: " + dest.WireType.ToString());
+            }
+        }
+
+        /// <summary>
+        /// Parses a TimeSpan from a protobuf stream using protobuf-net's own representation, bcl.TimeSpan
+        /// </summary>        
+        public static TimeSpan ReadTimeSpan(ProtoReader source)
+        {
+            long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind);
+            if (ticks == long.MinValue) return TimeSpan.MinValue;
+            if (ticks == long.MaxValue) return TimeSpan.MaxValue;
+            return TimeSpan.FromTicks(ticks);
+        }
+
+        /// <summary>
+        /// Parses a TimeSpan from a protobuf stream using the standardized format, google.protobuf.Duration
+        /// </summary>
+        public static TimeSpan ReadDuration(ProtoReader source)
+        {
+            long seconds = 0;
+            int nanos = 0;
+            SubItemToken token = ProtoReader.StartSubItem(source);
+            int fieldNumber;
+            while ((fieldNumber = source.ReadFieldHeader()) > 0)
+            {
+                switch (fieldNumber)
+                {
+                    case 1:
+                        seconds = source.ReadInt64();
+                        break;
+                    case 2:
+                        nanos = source.ReadInt32();
+                        break;
+                    default:
+                        source.SkipField();
+                        break;
+                }
+            }
+            ProtoReader.EndSubItem(token, source);
+            return FromDurationSeconds(seconds, nanos);
+        }
+
+        /// <summary>
+        /// Writes a TimeSpan to a protobuf stream using the standardized format, google.protobuf.Duration
+        /// </summary>
+        public static void WriteDuration(TimeSpan value, ProtoWriter dest)
+        {
+            var seconds = ToDurationSeconds(value, out int nanos);
+            WriteSecondsNanos(seconds, nanos, dest);
+        }
+
+        private static void WriteSecondsNanos(long seconds, int nanos, ProtoWriter dest)
+        {
+            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
+            if (seconds != 0)
+            {
+                ProtoWriter.WriteFieldHeader(1, WireType.Variant, dest);
+                ProtoWriter.WriteInt64(seconds, dest);
+            }
+            if (nanos != 0)
+            {
+                ProtoWriter.WriteFieldHeader(2, WireType.Variant, dest);
+                ProtoWriter.WriteInt32(nanos, dest);
+            }
+            ProtoWriter.EndSubItem(token, dest);
+        }
+
+        /// <summary>
+        /// Parses a DateTime from a protobuf stream using the standardized format, google.protobuf.Timestamp
+        /// </summary>
+        public static DateTime ReadTimestamp(ProtoReader source)
+        {
+            // note: DateTime is only defined for just over 0000 to just below 10000;
+            // TimeSpan has a range of +/- 10,675,199 days === 29k years;
+            // so we can just use epoch time delta
+            return TimestampEpoch + ReadDuration(source);
+        }
+
+        /// <summary>
+        /// Writes a DateTime to a protobuf stream using the standardized format, google.protobuf.Timestamp
+        /// </summary>
+        public static void WriteTimestamp(DateTime value, ProtoWriter dest)
+        {
+            var seconds = ToDurationSeconds(value - TimestampEpoch, out int nanos);
+
+            if (nanos < 0)
+            {   // from Timestamp.proto:
+                // "Negative second values with fractions must still have
+                // non -negative nanos values that count forward in time."
+                seconds--;
+                nanos += 1000000000;
+            }
+            WriteSecondsNanos(seconds, nanos, dest);
+        }
+
+        static TimeSpan FromDurationSeconds(long seconds, int nanos)
+        {
+
+            long ticks = checked((seconds * TimeSpan.TicksPerSecond)
+                + (nanos * TimeSpan.TicksPerMillisecond) / 1000000);
+            return TimeSpan.FromTicks(ticks);
+        }
+
+        static long ToDurationSeconds(TimeSpan value, out int nanos)
+        {
+            nanos = (int)(((value.Ticks % TimeSpan.TicksPerSecond) * 1000000)
+                / TimeSpan.TicksPerMillisecond);
+            return value.Ticks / TimeSpan.TicksPerSecond;
+        }
+
+        /// <summary>
+        /// Parses a DateTime from a protobuf stream
+        /// </summary>
+        public static DateTime ReadDateTime(ProtoReader source)
+        {
+            long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind);
+            if (ticks == long.MinValue) return DateTime.MinValue;
+            if (ticks == long.MaxValue) return DateTime.MaxValue;
+            return EpochOrigin[(int)kind].AddTicks(ticks);
+        }
+
+        /// <summary>
+        /// Writes a DateTime to a protobuf stream, excluding the <c>Kind</c>
+        /// </summary>
+        public static void WriteDateTime(DateTime value, ProtoWriter dest)
+        {
+            WriteDateTimeImpl(value, dest, false);
+        }
+
+        /// <summary>
+        /// Writes a DateTime to a protobuf stream, including the <c>Kind</c>
+        /// </summary>
+        public static void WriteDateTimeWithKind(DateTime value, ProtoWriter dest)
+        {
+            WriteDateTimeImpl(value, dest, true);
+        }
+
+        private static void WriteDateTimeImpl(DateTime value, ProtoWriter dest, bool includeKind)
+        {
+            if (dest == null) throw new ArgumentNullException(nameof(dest));
+            TimeSpan delta;
+            switch (dest.WireType)
+            {
+                case WireType.StartGroup:
+                case WireType.String:
+                    if (value == DateTime.MaxValue)
+                    {
+                        delta = TimeSpan.MaxValue;
+                        includeKind = false;
+                    }
+                    else if (value == DateTime.MinValue)
+                    {
+                        delta = TimeSpan.MinValue;
+                        includeKind = false;
+                    }
+                    else
+                    {
+                        delta = value - EpochOrigin[0];
+                    }
+                    break;
+                default:
+                    delta = value - EpochOrigin[0];
+                    break;
+            }
+            WriteTimeSpanImpl(delta, dest, includeKind ? value.Kind : DateTimeKind.Unspecified);
+        }
+
+        private static long ReadTimeSpanTicks(ProtoReader source, out DateTimeKind kind)
+        {
+            kind = DateTimeKind.Unspecified;
+            switch (source.WireType)
+            {
+                case WireType.String:
+                case WireType.StartGroup:
+                    SubItemToken token = ProtoReader.StartSubItem(source);
+                    int fieldNumber;
+                    TimeSpanScale scale = TimeSpanScale.Days;
+                    long value = 0;
+                    while ((fieldNumber = source.ReadFieldHeader()) > 0)
+                    {
+                        switch (fieldNumber)
+                        {
+                            case FieldTimeSpanScale:
+                                scale = (TimeSpanScale)source.ReadInt32();
+                                break;
+                            case FieldTimeSpanValue:
+                                source.Assert(WireType.SignedVariant);
+                                value = source.ReadInt64();
+                                break;
+                            case FieldTimeSpanKind:
+                                kind = (DateTimeKind)source.ReadInt32();
+                                switch (kind)
+                                {
+                                    case DateTimeKind.Unspecified:
+                                    case DateTimeKind.Utc:
+                                    case DateTimeKind.Local:
+                                        break; // fine
+                                    default:
+                                        throw new ProtoException("Invalid date/time kind: " + kind.ToString());
+                                }
+                                break;
+                            default:
+                                source.SkipField();
+                                break;
+                        }
+                    }
+                    ProtoReader.EndSubItem(token, source);
+                    switch (scale)
+                    {
+                        case TimeSpanScale.Days:
+                            return value * TimeSpan.TicksPerDay;
+                        case TimeSpanScale.Hours:
+                            return value * TimeSpan.TicksPerHour;
+                        case TimeSpanScale.Minutes:
+                            return value * TimeSpan.TicksPerMinute;
+                        case TimeSpanScale.Seconds:
+                            return value * TimeSpan.TicksPerSecond;
+                        case TimeSpanScale.Milliseconds:
+                            return value * TimeSpan.TicksPerMillisecond;
+                        case TimeSpanScale.Ticks:
+                            return value;
+                        case TimeSpanScale.MinMax:
+                            switch (value)
+                            {
+                                case 1: return long.MaxValue;
+                                case -1: return long.MinValue;
+                                default: throw new ProtoException("Unknown min/max value: " + value.ToString());
+                            }
+                        default:
+                            throw new ProtoException("Unknown timescale: " + scale.ToString());
+                    }
+                case WireType.Fixed64:
+                    return source.ReadInt64();
+                default:
+                    throw new ProtoException("Unexpected wire-type: " + source.WireType.ToString());
+            }
+        }
+
+        const int FieldDecimalLow = 0x01, FieldDecimalHigh = 0x02, FieldDecimalSignScale = 0x03;
+
+        /// <summary>
+        /// Parses a decimal from a protobuf stream
+        /// </summary>
+        public static decimal ReadDecimal(ProtoReader reader)
+        {
+            ulong low = 0;
+            uint high = 0;
+            uint signScale = 0;
+
+            int fieldNumber;
+            SubItemToken token = ProtoReader.StartSubItem(reader);
+            while ((fieldNumber = reader.ReadFieldHeader()) > 0)
+            {
+                switch (fieldNumber)
+                {
+                    case FieldDecimalLow: low = reader.ReadUInt64(); break;
+                    case FieldDecimalHigh: high = reader.ReadUInt32(); break;
+                    case FieldDecimalSignScale: signScale = reader.ReadUInt32(); break;
+                    default: reader.SkipField(); break;
+                }
+
+            }
+            ProtoReader.EndSubItem(token, reader);
+
+            int lo = (int)(low & 0xFFFFFFFFL),
+                mid = (int)((low >> 32) & 0xFFFFFFFFL),
+                hi = (int)high;
+            bool isNeg = (signScale & 0x0001) == 0x0001;
+            byte scale = (byte)((signScale & 0x01FE) >> 1);
+            return new decimal(lo, mid, hi, isNeg, scale);
+        }
+
+        /// <summary>
+        /// Writes a decimal to a protobuf stream
+        /// </summary>
+        public static void WriteDecimal(decimal value, ProtoWriter writer)
+        {
+            int[] bits = decimal.GetBits(value);
+            ulong a = ((ulong)bits[1]) << 32, b = ((ulong)bits[0]) & 0xFFFFFFFFL;
+            ulong low = a | b;
+            uint high = (uint)bits[2];
+            uint signScale = (uint)(((bits[3] >> 15) & 0x01FE) | ((bits[3] >> 31) & 0x0001));
+
+            SubItemToken token = ProtoWriter.StartSubItem(null, writer);
+            if (low != 0)
+            {
+                ProtoWriter.WriteFieldHeader(FieldDecimalLow, WireType.Variant, writer);
+                ProtoWriter.WriteUInt64(low, writer);
+            }
+            if (high != 0)
+            {
+                ProtoWriter.WriteFieldHeader(FieldDecimalHigh, WireType.Variant, writer);
+                ProtoWriter.WriteUInt32(high, writer);
+            }
+            if (signScale != 0)
+            {
+                ProtoWriter.WriteFieldHeader(FieldDecimalSignScale, WireType.Variant, writer);
+                ProtoWriter.WriteUInt32(signScale, writer);
+            }
+            ProtoWriter.EndSubItem(token, writer);
+        }
+
+        const int FieldGuidLow = 1, FieldGuidHigh = 2;
+        /// <summary>
+        /// Writes a Guid to a protobuf stream
+        /// </summary>        
+        public static void WriteGuid(Guid value, ProtoWriter dest)
+        {
+            byte[] blob = value.ToByteArray();
+
+            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
+            if (value != Guid.Empty)
+            {
+                ProtoWriter.WriteFieldHeader(FieldGuidLow, WireType.Fixed64, dest);
+                ProtoWriter.WriteBytes(blob, 0, 8, dest);
+                ProtoWriter.WriteFieldHeader(FieldGuidHigh, WireType.Fixed64, dest);
+                ProtoWriter.WriteBytes(blob, 8, 8, dest);
+            }
+            ProtoWriter.EndSubItem(token, dest);
+        }
+        /// <summary>
+        /// Parses a Guid from a protobuf stream
+        /// </summary>
+        public static Guid ReadGuid(ProtoReader source)
+        {
+            ulong low = 0, high = 0;
+            int fieldNumber;
+            SubItemToken token = ProtoReader.StartSubItem(source);
+            while ((fieldNumber = source.ReadFieldHeader()) > 0)
+            {
+                switch (fieldNumber)
+                {
+                    case FieldGuidLow: low = source.ReadUInt64(); break;
+                    case FieldGuidHigh: high = source.ReadUInt64(); break;
+                    default: source.SkipField(); break;
+                }
+            }
+            ProtoReader.EndSubItem(token, source);
+            if (low == 0 && high == 0) return Guid.Empty;
+            uint a = (uint)(low >> 32), b = (uint)low, c = (uint)(high >> 32), d = (uint)high;
+            return new Guid((int)b, (short)a, (short)(a >> 16),
+                (byte)d, (byte)(d >> 8), (byte)(d >> 16), (byte)(d >> 24),
+                (byte)c, (byte)(c >> 8), (byte)(c >> 16), (byte)(c >> 24));
+
+        }
+
+
+        private const int
+            FieldExistingObjectKey = 1,
+            FieldNewObjectKey = 2,
+            FieldExistingTypeKey = 3,
+            FieldNewTypeKey = 4,
+            FieldTypeName = 8,
+            FieldObject = 10;
+
+        /// <summary>
+        /// Optional behaviours that introduce .NET-specific functionality
+        /// </summary>
+        [Flags]
+        public enum NetObjectOptions : byte
+        {
+            /// <summary>
+            /// No special behaviour
+            /// </summary>
+            None = 0,
+            /// <summary>
+            /// Enables full object-tracking/full-graph support.
+            /// </summary>
+            AsReference = 1,
+            /// <summary>
+            /// Embeds the type information into the stream, allowing usage with types not known in advance.
+            /// </summary>
+            DynamicType = 2,
+            /// <summary>
+            /// If false, the constructor for the type is bypassed during deserialization, meaning any field initializers
+            /// or other initialization code is skipped.
+            /// </summary>
+            UseConstructor = 4,
+            /// <summary>
+            /// Should the object index be reserved, rather than creating an object promptly
+            /// </summary>
+            LateSet = 8
+        }
+
+        /// <summary>
+        /// Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc.
+        /// </summary>
+        public static object ReadNetObject(object value, ProtoReader source, int key, Type type, NetObjectOptions options)
+        {
+            SubItemToken token = ProtoReader.StartSubItem(source);
+            int fieldNumber;
+            int newObjectKey = -1, newTypeKey = -1, tmp;
+            while ((fieldNumber = source.ReadFieldHeader()) > 0)
+            {
+                switch (fieldNumber)
+                {
+                    case FieldExistingObjectKey:
+                        tmp = source.ReadInt32();
+                        value = source.NetCache.GetKeyedObject(tmp);
+                        break;
+                    case FieldNewObjectKey:
+                        newObjectKey = source.ReadInt32();
+                        break;
+                    case FieldExistingTypeKey:
+                        tmp = source.ReadInt32();
+                        type = (Type)source.NetCache.GetKeyedObject(tmp);
+                        key = source.GetTypeKey(ref type);
+                        break;
+                    case FieldNewTypeKey:
+                        newTypeKey = source.ReadInt32();
+                        break;
+                    case FieldTypeName:
+                        string typeName = source.ReadString();
+                        type = source.DeserializeType(typeName);
+                        if (type == null)
+                        {
+                            throw new ProtoException("Unable to resolve type: " + typeName + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)");
+                        }
+                        if (type == typeof(string))
+                        {
+                            key = -1;
+                        }
+                        else
+                        {
+                            key = source.GetTypeKey(ref type);
+                            if (key < 0)
+                                throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name);
+                        }
+                        break;
+                    case FieldObject:
+                        bool isString = type == typeof(string);
+                        bool wasNull = value == null;
+                        bool lateSet = wasNull && (isString || ((options & NetObjectOptions.LateSet) != 0));
+
+                        if (newObjectKey >= 0 && !lateSet)
+                        {
+                            if (value == null)
+                            {
+                                source.TrapNextObject(newObjectKey);
+                            }
+                            else
+                            {
+                                source.NetCache.SetKeyedObject(newObjectKey, value);
+                            }
+                            if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type);
+                        }
+                        object oldValue = value;
+                        if (isString)
+                        {
+                            value = source.ReadString();
+                        }
+                        else
+                        {
+                            value = ProtoReader.ReadTypedObject(oldValue, key, source, type);
+                        }
+
+                        if (newObjectKey >= 0)
+                        {
+                            if (wasNull && !lateSet)
+                            { // this both ensures (via exception) that it *was* set, and makes sure we don't shout
+                                // about changed references
+                                oldValue = source.NetCache.GetKeyedObject(newObjectKey);
+                            }
+                            if (lateSet)
+                            {
+                                source.NetCache.SetKeyedObject(newObjectKey, value);
+                                if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type);
+                            }
+                        }
+                        if (newObjectKey >= 0 && !lateSet && !ReferenceEquals(oldValue, value))
+                        {
+                            throw new ProtoException("A reference-tracked object changed reference during deserialization");
+                        }
+                        if (newObjectKey < 0 && newTypeKey >= 0)
+                        {  // have a new type, but not a new object
+                            source.NetCache.SetKeyedObject(newTypeKey, type);
+                        }
+                        break;
+                    default:
+                        source.SkipField();
+                        break;
+                }
+            }
+            if (newObjectKey >= 0 && (options & NetObjectOptions.AsReference) == 0)
+            {
+                throw new ProtoException("Object key in input stream, but reference-tracking was not expected");
+            }
+            ProtoReader.EndSubItem(token, source);
+
+            return value;
+        }
+
+        /// <summary>
+        /// Writes an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc.
+        /// </summary>
+        public static void WriteNetObject(object value, ProtoWriter dest, int key, NetObjectOptions options)
+        {
+            if (dest == null) throw new ArgumentNullException("dest");
+            bool dynamicType = (options & NetObjectOptions.DynamicType) != 0,
+                 asReference = (options & NetObjectOptions.AsReference) != 0;
+            WireType wireType = dest.WireType;
+            SubItemToken token = ProtoWriter.StartSubItem(null, dest);
+            bool writeObject = true;
+            if (asReference)
+            {
+                int objectKey = dest.NetCache.AddObjectKey(value, out bool existing);
+                ProtoWriter.WriteFieldHeader(existing ? FieldExistingObjectKey : FieldNewObjectKey, WireType.Variant, dest);
+                ProtoWriter.WriteInt32(objectKey, dest);
+                if (existing)
+                {
+                    writeObject = false;
+                }
+            }
+
+            if (writeObject)
+            {
+                if (dynamicType)
+                {
+                    Type type = value.GetType();
+
+                    if (!(value is string))
+                    {
+                        key = dest.GetTypeKey(ref type);
+                        if (key < 0) throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name);
+                    }
+                    int typeKey = dest.NetCache.AddObjectKey(type, out bool existing);
+                    ProtoWriter.WriteFieldHeader(existing ? FieldExistingTypeKey : FieldNewTypeKey, WireType.Variant, dest);
+                    ProtoWriter.WriteInt32(typeKey, dest);
+                    if (!existing)
+                    {
+                        ProtoWriter.WriteFieldHeader(FieldTypeName, WireType.String, dest);
+                        ProtoWriter.WriteString(dest.SerializeType(type), dest);
+                    }
+
+                }
+                ProtoWriter.WriteFieldHeader(FieldObject, wireType, dest);
+                if (value is string)
+                {
+                    ProtoWriter.WriteString((string)value, dest);
+                }
+                else
+                {
+                    ProtoWriter.WriteObject(value, key, dest);
+                }
+            }
+            ProtoWriter.EndSubItem(token, dest);
+        }
+    }
+}

+ 78 - 0
Server/ThirdParty/protobuf-net/BufferExtension.cs

@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Provides a simple buffer-based implementation of an <see cref="IExtension">extension</see> object.
+    /// </summary>
+    public sealed class BufferExtension : IExtension, IExtensionResettable
+    {
+        private byte[] buffer;
+
+        void IExtensionResettable.Reset()
+        {
+            buffer = null;
+        }
+
+        int IExtension.GetLength()
+        {
+            return buffer == null ? 0 : buffer.Length;
+        }
+
+        Stream IExtension.BeginAppend()
+        {
+            return new MemoryStream();
+        }
+
+        void IExtension.EndAppend(Stream stream, bool commit)
+        {
+            using (stream)
+            {
+                int len;
+                if (commit && (len = (int)stream.Length) > 0)
+                {
+                    MemoryStream ms = (MemoryStream)stream;
+
+                    if (buffer == null)
+                    {   // allocate new buffer
+                        buffer = ms.ToArray();
+                    }
+                    else
+                    {   // resize and copy the data
+                        // note: Array.Resize not available on CF
+                        int offset = buffer.Length;
+                        byte[] tmp = new byte[offset + len];
+                        Buffer.BlockCopy(buffer, 0, tmp, 0, offset);
+
+#if PORTABLE // no GetBuffer() - fine, we'll use Read instead
+                        int bytesRead;
+                        long oldPos = ms.Position;
+                        ms.Position = 0;
+                        while (len > 0 && (bytesRead = ms.Read(tmp, offset, len)) > 0)
+                        {
+                            len -= bytesRead;
+                            offset += bytesRead;
+                        }
+                        if(len != 0) throw new EndOfStreamException();
+                        ms.Position = oldPos;
+#else
+                        Buffer.BlockCopy(Helpers.GetBuffer(ms), 0, tmp, offset, len);
+#endif
+                        buffer = tmp;
+                    }
+                }
+            }
+        }
+
+        Stream IExtension.BeginQuery()
+        {
+            return buffer == null ? Stream.Null : new MemoryStream(buffer);
+        }
+
+        void IExtension.EndQuery(Stream stream)
+        {
+            using (stream) { } // just clean up
+        }
+    }
+}

+ 149 - 0
Server/ThirdParty/protobuf-net/BufferPool.cs

@@ -0,0 +1,149 @@
+using System;
+
+namespace ProtoBuf
+{
+    internal sealed class BufferPool
+    {
+        internal static void Flush()
+        {
+            lock (Pool)
+            {
+                for (var i = 0; i < Pool.Length; i++)
+                    Pool[i] = null;
+            }
+        }
+
+        private BufferPool() { }
+        private const int POOL_SIZE = 20;
+        internal const int BUFFER_LENGTH = 1024;
+        private static readonly CachedBuffer[] Pool = new CachedBuffer[POOL_SIZE];
+
+        internal static byte[] GetBuffer() => GetBuffer(BUFFER_LENGTH);
+
+        internal static byte[] GetBuffer(int minSize)
+        {
+            byte[] cachedBuff = GetCachedBuffer(minSize);
+            return cachedBuff ?? new byte[minSize];
+        }
+
+        internal static byte[] GetCachedBuffer(int minSize)
+        {
+            lock (Pool)
+            {
+                var bestIndex = -1;
+                byte[] bestMatch = null;
+                for (var i = 0; i < Pool.Length; i++)
+                {
+                    var buffer = Pool[i];
+                    if (buffer == null || buffer.Size < minSize)
+                    {
+                        continue;
+                    }
+                    if (bestMatch != null && bestMatch.Length < buffer.Size)
+                    {
+                        continue;
+                    }
+
+                    var tmp = buffer.Buffer;
+                    if (tmp == null)
+                    {
+                        Pool[i] = null;
+                    }
+                    else
+                    {
+                        bestMatch = tmp;
+                        bestIndex = i;
+                    }
+                }
+
+                if (bestIndex >= 0)
+                {
+                    Pool[bestIndex] = null;
+                }
+
+                return bestMatch;
+            }
+        }
+
+        /// <remarks>
+        /// https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element
+        /// </remarks>
+        private const int MaxByteArraySize = int.MaxValue - 56;
+
+        internal static void ResizeAndFlushLeft(ref byte[] buffer, int toFitAtLeastBytes, int copyFromIndex, int copyBytes)
+        {
+            Helpers.DebugAssert(buffer != null);
+            Helpers.DebugAssert(toFitAtLeastBytes > buffer.Length);
+            Helpers.DebugAssert(copyFromIndex >= 0);
+            Helpers.DebugAssert(copyBytes >= 0);
+
+            int newLength = buffer.Length * 2;
+            if (newLength < 0)
+            {
+                newLength = MaxByteArraySize;
+            }
+
+            if (newLength < toFitAtLeastBytes) newLength = toFitAtLeastBytes;
+
+            if (copyBytes == 0)
+            {
+                ReleaseBufferToPool(ref buffer);
+            }
+
+            var newBuffer = GetCachedBuffer(toFitAtLeastBytes) ?? new byte[newLength];
+
+            if (copyBytes > 0)
+            {
+                Buffer.BlockCopy(buffer, copyFromIndex, newBuffer, 0, copyBytes);
+                ReleaseBufferToPool(ref buffer);
+            }
+
+            buffer = newBuffer;
+        }
+
+        internal static void ReleaseBufferToPool(ref byte[] buffer)
+        {
+            if (buffer == null) return;
+
+            lock (Pool)
+            {
+                var minIndex = 0;
+                var minSize = int.MaxValue;
+                for (var i = 0; i < Pool.Length; i++)
+                {
+                    var tmp = Pool[i];
+                    if (tmp == null || !tmp.IsAlive)
+                    {
+                        minIndex = 0;
+                        break;
+                    }
+                    if (tmp.Size < minSize)
+                    {
+                        minIndex = i;
+                        minSize = tmp.Size;
+                    }
+                }
+
+                Pool[minIndex] = new CachedBuffer(buffer);
+            }
+
+            buffer = null;
+        }
+
+        private class CachedBuffer
+        {
+            private readonly WeakReference _reference;
+
+            public int Size { get; }
+
+            public bool IsAlive => _reference.IsAlive;
+            public byte[] Buffer => (byte[])_reference.Target;
+
+            public CachedBuffer(byte[] buffer)
+            {
+                Size = buffer.Length;
+                _reference = new WeakReference(buffer);
+            }
+        }
+    }
+}

+ 33 - 0
Server/ThirdParty/protobuf-net/CallbackAttributes.cs

@@ -0,0 +1,33 @@
+using System;
+using System.ComponentModel;
+
+namespace ProtoBuf
+{
+    /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked before serialization.</summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+#if !CF && !PORTABLE && !COREFX && !PROFILE259
+    [ImmutableObject(true)]
+#endif
+    public sealed class ProtoBeforeSerializationAttribute : Attribute { }
+
+    /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked after serialization.</summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+#if !CF && !PORTABLE && !COREFX && !PROFILE259
+    [ImmutableObject(true)]
+#endif
+    public sealed class ProtoAfterSerializationAttribute : Attribute { }
+
+    /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked before deserialization.</summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+#if !CF && !PORTABLE && !COREFX && !PROFILE259
+    [ImmutableObject(true)]
+#endif
+    public sealed class ProtoBeforeDeserializationAttribute : Attribute { }
+
+    /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked after deserialization.</summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+#if !CF && !PORTABLE && !COREFX && !PROFILE259
+    [ImmutableObject(true)]
+#endif
+    public sealed class ProtoAfterDeserializationAttribute : Attribute { }
+}

+ 1435 - 0
Server/ThirdParty/protobuf-net/Compiler/CompilerContext.cs

@@ -0,0 +1,1435 @@
+#if FEAT_COMPILER
+//#define DEBUG_COMPILE
+using System;
+using System.Threading;
+using ProtoBuf.Meta;
+using ProtoBuf.Serializers;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace ProtoBuf.Compiler
+{
+    internal readonly struct CodeLabel
+    {
+        public readonly Label Value;
+        public readonly int Index;
+        public CodeLabel(Label value, int index)
+        {
+            this.Value = value;
+            this.Index = index;
+        }
+    }
+    internal sealed class CompilerContext
+    {
+        public TypeModel Model => model;
+
+        readonly DynamicMethod method;
+        static int next;
+
+        internal CodeLabel DefineLabel()
+        {
+            CodeLabel result = new CodeLabel(il.DefineLabel(), nextLabel++);
+            return result;
+        }
+#if DEBUG_COMPILE
+        static readonly string traceCompilePath;
+        static CompilerContext()
+        {
+            traceCompilePath = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(),
+                "TraceCompile.txt");
+            Console.WriteLine("DEBUG_COMPILE enabled; writing to " + traceCompilePath);
+        }
+#endif
+        [System.Diagnostics.Conditional("DEBUG_COMPILE")]
+        private void TraceCompile(string value)
+        {
+#if DEBUG_COMPILE
+            if (!string.IsNullOrWhiteSpace(value))
+            {
+                using (System.IO.StreamWriter sw = System.IO.File.AppendText(traceCompilePath))
+                {
+                    sw.WriteLine(value);
+                }
+            }
+#endif
+        }
+        internal void MarkLabel(CodeLabel label)
+        {
+            il.MarkLabel(label.Value);
+            TraceCompile("#: " + label.Index);
+        }
+
+        public static ProtoSerializer BuildSerializer(IProtoSerializer head, TypeModel model)
+        {
+            Type type = head.ExpectedType;
+            try
+            {
+                CompilerContext ctx = new CompilerContext(type, true, true, model, typeof(object));
+                ctx.LoadValue(ctx.InputValue);
+                ctx.CastFromObject(type);
+                ctx.WriteNullCheckedTail(type, head, null);
+                ctx.Emit(OpCodes.Ret);
+                return (ProtoSerializer)ctx.method.CreateDelegate(
+                    typeof(ProtoSerializer));
+            }
+            catch (Exception ex)
+            {
+                string name = type.FullName;
+                if (string.IsNullOrEmpty(name)) name = type.Name;
+                throw new InvalidOperationException("It was not possible to prepare a serializer for: " + name, ex);
+            }
+        }
+        /*public static ProtoCallback BuildCallback(IProtoTypeSerializer head)
+        {
+            Type type = head.ExpectedType;
+            CompilerContext ctx = new CompilerContext(type, true, true);
+            using (Local typedVal = new Local(ctx, type))
+            {
+                ctx.LoadValue(Local.InputValue);
+                ctx.CastFromObject(type);
+                ctx.StoreValue(typedVal);
+                CodeLabel[] jumpTable = new CodeLabel[4];
+                for(int i = 0 ; i < jumpTable.Length ; i++) {
+                    jumpTable[i] = ctx.DefineLabel();
+                }
+                ctx.LoadReaderWriter();
+                ctx.Switch(jumpTable);
+                ctx.Return();
+                for(int i = 0 ; i < jumpTable.Length ; i++) {
+                    ctx.MarkLabel(jumpTable[i]);
+                    if (head.HasCallbacks((TypeModel.CallbackType)i))
+                    {
+                        head.EmitCallback(ctx, typedVal, (TypeModel.CallbackType)i);
+                    }
+                    ctx.Return();
+                }                
+            }
+            
+            ctx.Emit(OpCodes.Ret);
+            return (ProtoCallback)ctx.method.CreateDelegate(
+                typeof(ProtoCallback));
+        }*/
+        public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model)
+        {
+            Type type = head.ExpectedType;
+            CompilerContext ctx = new CompilerContext(type, false, true, model, typeof(object));
+
+            using (Local typedVal = new Local(ctx, type))
+            {
+                if (!Helpers.IsValueType(type))
+                {
+                    ctx.LoadValue(ctx.InputValue);
+                    ctx.CastFromObject(type);
+                    ctx.StoreValue(typedVal);
+                }
+                else
+                {
+                    ctx.LoadValue(ctx.InputValue);
+                    CodeLabel notNull = ctx.DefineLabel(), endNull = ctx.DefineLabel();
+                    ctx.BranchIfTrue(notNull, true);
+
+                    ctx.LoadAddress(typedVal, type);
+                    ctx.EmitCtor(type);
+                    ctx.Branch(endNull, true);
+
+                    ctx.MarkLabel(notNull);
+                    ctx.LoadValue(ctx.InputValue);
+                    ctx.CastFromObject(type);
+                    ctx.StoreValue(typedVal);
+
+                    ctx.MarkLabel(endNull);
+                }
+                head.EmitRead(ctx, typedVal);
+
+                if (head.ReturnsValue)
+                {
+                    ctx.StoreValue(typedVal);
+                }
+
+                ctx.LoadValue(typedVal);
+                ctx.CastToObject(type);
+            }
+            ctx.Emit(OpCodes.Ret);
+            return (ProtoDeserializer)ctx.method.CreateDelegate(
+                typeof(ProtoDeserializer));
+        }
+
+        internal void Return()
+        {
+            Emit(OpCodes.Ret);
+        }
+
+        static bool IsObject(Type type)
+        {
+            return type == typeof(object);
+        }
+
+        internal void CastToObject(Type type)
+        {
+            if (IsObject(type))
+            { }
+            else if (Helpers.IsValueType(type))
+            {
+                il.Emit(OpCodes.Box, type);
+                TraceCompile(OpCodes.Box + ": " + type);
+            }
+            else
+            {
+                il.Emit(OpCodes.Castclass, MapType(typeof(object)));
+                TraceCompile(OpCodes.Castclass + ": " + type);
+            }
+        }
+
+        internal void CastFromObject(Type type)
+        {
+            if (IsObject(type))
+            { }
+            else if (Helpers.IsValueType(type))
+            {
+                switch (MetadataVersion)
+                {
+                    case ILVersion.Net1:
+                        il.Emit(OpCodes.Unbox, type);
+                        il.Emit(OpCodes.Ldobj, type);
+                        TraceCompile(OpCodes.Unbox + ": " + type);
+                        TraceCompile(OpCodes.Ldobj + ": " + type);
+                        break;
+                    default:
+
+                        il.Emit(OpCodes.Unbox_Any, type);
+                        TraceCompile(OpCodes.Unbox_Any + ": " + type);
+                        break;
+                }
+            }
+            else
+            {
+                il.Emit(OpCodes.Castclass, type);
+                TraceCompile(OpCodes.Castclass + ": " + type);
+            }
+        }
+        private readonly bool isStatic;
+        private readonly RuntimeTypeModel.SerializerPair[] methodPairs;
+
+        internal MethodBuilder GetDedicatedMethod(int metaKey, bool read)
+        {
+            if (methodPairs == null) return null;
+            // but if we *do* have pairs, we demand that we find a match...
+            for (int i = 0; i < methodPairs.Length; i++)
+            {
+                if (methodPairs[i].MetaKey == metaKey) { return read ? methodPairs[i].Deserialize : methodPairs[i].Serialize; }
+            }
+            throw new ArgumentException("Meta-key not found", "metaKey");
+        }
+
+        internal int MapMetaKeyToCompiledKey(int metaKey)
+        {
+            if (metaKey < 0 || methodPairs == null) return metaKey; // all meta, or a dummy/wildcard key
+
+            for (int i = 0; i < methodPairs.Length; i++)
+            {
+                if (methodPairs[i].MetaKey == metaKey) return i;
+            }
+            throw new ArgumentException("Key could not be mapped: " + metaKey.ToString(), "metaKey");
+        }
+
+
+        private readonly bool isWriter;
+
+        private readonly bool nonPublic;
+        internal bool NonPublic { get { return nonPublic; } }
+
+        private readonly Local inputValue;
+        public Local InputValue { get { return inputValue; } }
+
+        private readonly string assemblyName;
+        internal CompilerContext(ILGenerator il, bool isStatic, bool isWriter, RuntimeTypeModel.SerializerPair[] methodPairs, TypeModel model, ILVersion metadataVersion, string assemblyName, Type inputType, string traceName)
+        {
+            if (string.IsNullOrEmpty(assemblyName)) throw new ArgumentNullException(nameof(assemblyName));
+            this.assemblyName = assemblyName;
+            this.isStatic = isStatic;
+            this.methodPairs = methodPairs ?? throw new ArgumentNullException(nameof(methodPairs));
+            this.il = il ?? throw new ArgumentNullException(nameof(il));
+            // nonPublic = false; <== implicit
+            this.isWriter = isWriter;
+            this.model = model ?? throw new ArgumentNullException(nameof(model));
+            this.metadataVersion = metadataVersion;
+            if (inputType != null) this.inputValue = new Local(null, inputType);
+            TraceCompile(">> " + traceName);
+        }
+
+        private CompilerContext(Type associatedType, bool isWriter, bool isStatic, TypeModel model, Type inputType)
+        {
+            metadataVersion = ILVersion.Net2;
+            this.isStatic = isStatic;
+            this.isWriter = isWriter;
+            this.model = model ?? throw new ArgumentNullException(nameof(model));
+            nonPublic = true;
+            Type[] paramTypes;
+            Type returnType;
+            if (isWriter)
+            {
+                returnType = typeof(void);
+                paramTypes = new Type[] { typeof(object), typeof(ProtoWriter) };
+            }
+            else
+            {
+                returnType = typeof(object);
+                paramTypes = new Type[] { typeof(object), typeof(ProtoReader) };
+            }
+            int uniqueIdentifier;
+#if PLAT_NO_INTERLOCKED
+            uniqueIdentifier = ++next;
+#else
+            uniqueIdentifier = Interlocked.Increment(ref next);
+#endif
+            method = new DynamicMethod("proto_" + uniqueIdentifier.ToString(), returnType, paramTypes, associatedType
+#if COREFX
+                .GetTypeInfo()
+#endif
+                .IsInterface ? typeof(object) : associatedType, true);
+            this.il = method.GetILGenerator();
+            if (inputType != null) this.inputValue = new Local(null, inputType);
+            TraceCompile(">> " + method.Name);
+        }
+
+        private readonly ILGenerator il;
+
+        private void Emit(OpCode opcode)
+        {
+            il.Emit(opcode);
+            TraceCompile(opcode.ToString());
+        }
+
+        public void LoadValue(string value)
+        {
+            if (value == null)
+            {
+                LoadNullRef();
+            }
+            else
+            {
+                il.Emit(OpCodes.Ldstr, value);
+                TraceCompile(OpCodes.Ldstr + ": " + value);
+            }
+        }
+
+        public void LoadValue(float value)
+        {
+            il.Emit(OpCodes.Ldc_R4, value);
+            TraceCompile(OpCodes.Ldc_R4 + ": " + value);
+        }
+
+        public void LoadValue(double value)
+        {
+            il.Emit(OpCodes.Ldc_R8, value);
+            TraceCompile(OpCodes.Ldc_R8 + ": " + value);
+        }
+
+        public void LoadValue(long value)
+        {
+            il.Emit(OpCodes.Ldc_I8, value);
+            TraceCompile(OpCodes.Ldc_I8 + ": " + value);
+        }
+
+        public void LoadValue(int value)
+        {
+            switch (value)
+            {
+                case 0: Emit(OpCodes.Ldc_I4_0); break;
+                case 1: Emit(OpCodes.Ldc_I4_1); break;
+                case 2: Emit(OpCodes.Ldc_I4_2); break;
+                case 3: Emit(OpCodes.Ldc_I4_3); break;
+                case 4: Emit(OpCodes.Ldc_I4_4); break;
+                case 5: Emit(OpCodes.Ldc_I4_5); break;
+                case 6: Emit(OpCodes.Ldc_I4_6); break;
+                case 7: Emit(OpCodes.Ldc_I4_7); break;
+                case 8: Emit(OpCodes.Ldc_I4_8); break;
+                case -1: Emit(OpCodes.Ldc_I4_M1); break;
+                default:
+                    if (value >= -128 && value <= 127)
+                    {
+                        il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
+                        TraceCompile(OpCodes.Ldc_I4_S + ": " + value);
+                    }
+                    else
+                    {
+                        il.Emit(OpCodes.Ldc_I4, value);
+                        TraceCompile(OpCodes.Ldc_I4 + ": " + value);
+                    }
+                    break;
+
+            }
+        }
+
+        MutableList locals = new MutableList();
+        internal LocalBuilder GetFromPool(Type type)
+        {
+            int count = locals.Count;
+            for (int i = 0; i < count; i++)
+            {
+                LocalBuilder item = (LocalBuilder)locals[i];
+                if (item != null && item.LocalType == type)
+                {
+                    locals[i] = null; // remove from pool
+                    return item;
+                }
+            }
+            LocalBuilder result = il.DeclareLocal(type);
+            TraceCompile("$ " + result + ": " + type);
+            return result;
+        }
+
+        //
+        internal void ReleaseToPool(LocalBuilder value)
+        {
+            int count = locals.Count;
+            for (int i = 0; i < count; i++)
+            {
+                if (locals[i] == null)
+                {
+                    locals[i] = value; // released into existing slot
+                    return;
+                }
+            }
+            locals.Add(value); // create a new slot
+        }
+
+        public void LoadReaderWriter()
+        {
+            Emit(isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2);
+        }
+
+        public void StoreValue(Local local)
+        {
+            if (local == this.InputValue)
+            {
+                byte b = isStatic ? (byte)0 : (byte)1;
+                il.Emit(OpCodes.Starg_S, b);
+                TraceCompile(OpCodes.Starg_S + ": $" + b);
+            }
+            else
+            {
+
+                switch (local.Value.LocalIndex)
+                {
+                    case 0: Emit(OpCodes.Stloc_0); break;
+                    case 1: Emit(OpCodes.Stloc_1); break;
+                    case 2: Emit(OpCodes.Stloc_2); break;
+                    case 3: Emit(OpCodes.Stloc_3); break;
+                    default:
+
+                        OpCode code = UseShortForm(local) ? OpCodes.Stloc_S : OpCodes.Stloc;
+                        il.Emit(code, local.Value);
+                        TraceCompile(code + ": $" + local.Value);
+
+                        break;
+                }
+            }
+        }
+
+        public void LoadValue(Local local)
+        {
+            if (local == null) { /* nothing to do; top of stack */}
+            else if (local == this.InputValue)
+            {
+                Emit(isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1);
+            }
+            else
+            {
+
+                switch (local.Value.LocalIndex)
+                {
+                    case 0: Emit(OpCodes.Ldloc_0); break;
+                    case 1: Emit(OpCodes.Ldloc_1); break;
+                    case 2: Emit(OpCodes.Ldloc_2); break;
+                    case 3: Emit(OpCodes.Ldloc_3); break;
+                    default:
+
+                        OpCode code = UseShortForm(local) ? OpCodes.Ldloc_S : OpCodes.Ldloc;
+                        il.Emit(code, local.Value);
+                        TraceCompile(code + ": $" + local.Value);
+
+                        break;
+                }
+            }
+        }
+
+        public Local GetLocalWithValue(Type type, Compiler.Local fromValue)
+        {
+            if (fromValue != null)
+            {
+                if (fromValue.Type == type) return fromValue.AsCopy();
+                // otherwise, load onto the stack and let the default handling (below) deal with it
+                LoadValue(fromValue);
+                if (!Helpers.IsValueType(type) && (fromValue.Type == null || !type.IsAssignableFrom(fromValue.Type)))
+                { // need to cast
+                    Cast(type);
+                }
+            }
+            // need to store the value from the stack
+            Local result = new Local(this, type);
+            StoreValue(result);
+            return result;
+        }
+
+        internal void EmitBasicRead(string methodName, Type expectedType)
+        {
+            MethodInfo method = MapType(typeof(ProtoReader)).GetMethod(
+                methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+            if (method == null || method.ReturnType != expectedType
+                || method.GetParameters().Length != 0) throw new ArgumentException("methodName");
+            LoadReaderWriter();
+            EmitCall(method);
+        }
+
+        internal void EmitBasicRead(Type helperType, string methodName, Type expectedType)
+        {
+            MethodInfo method = helperType.GetMethod(
+                methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+            if (method == null || method.ReturnType != expectedType
+                || method.GetParameters().Length != 1) throw new ArgumentException("methodName");
+            LoadReaderWriter();
+            EmitCall(method);
+        }
+
+        internal void EmitBasicWrite(string methodName, Compiler.Local fromValue)
+        {
+            if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName");
+            LoadValue(fromValue);
+            LoadReaderWriter();
+            EmitCall(GetWriterMethod(methodName));
+        }
+
+        private MethodInfo GetWriterMethod(string methodName)
+        {
+            Type writerType = MapType(typeof(ProtoWriter));
+            MethodInfo[] methods = writerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+            foreach (MethodInfo method in methods)
+            {
+                if (method.Name != methodName) continue;
+                ParameterInfo[] pis = method.GetParameters();
+                if (pis.Length == 2 && pis[1].ParameterType == writerType) return method;
+            }
+            throw new ArgumentException("No suitable method found for: " + methodName, "methodName");
+        }
+
+        internal void EmitWrite(Type helperType, string methodName, Compiler.Local valueFrom)
+        {
+            if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName");
+            MethodInfo method = helperType.GetMethod(
+                methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+            if (method == null || method.ReturnType != MapType(typeof(void))) throw new ArgumentException("methodName");
+            LoadValue(valueFrom);
+            LoadReaderWriter();
+            EmitCall(method);
+        }
+
+        public void EmitCall(MethodInfo method) { EmitCall(method, null); }
+
+        public void EmitCall(MethodInfo method, Type targetType)
+        {
+            Helpers.DebugAssert(method != null);
+            MemberInfo member = method;
+            CheckAccessibility(ref member);
+            OpCode opcode;
+            if (method.IsStatic || Helpers.IsValueType(method.DeclaringType))
+            {
+                opcode = OpCodes.Call;
+            }
+            else
+            {
+                opcode = OpCodes.Callvirt;
+                if (targetType != null && Helpers.IsValueType(targetType) && !Helpers.IsValueType(method.DeclaringType))
+                {
+                    Constrain(targetType);
+                }
+            }
+            il.EmitCall(opcode, method, null);
+            TraceCompile(opcode + ": " + method + " on " + method.DeclaringType + (targetType == null ? "" : (" via " + targetType)));
+        }
+
+        /// <summary>
+        /// Pushes a null reference onto the stack. Note that this should only
+        /// be used to return a null (or set a variable to null); for null-tests
+        /// use BranchIfTrue / BranchIfFalse.
+        /// </summary>
+        public void LoadNullRef()
+        {
+            Emit(OpCodes.Ldnull);
+        }
+
+        private int nextLabel;
+
+        internal void WriteNullCheckedTail(Type type, IProtoSerializer tail, Compiler.Local valueFrom)
+        {
+            if (Helpers.IsValueType(type))
+            {
+                Type underlyingType = Helpers.GetUnderlyingType(type);
+
+                if (underlyingType == null)
+                { // not a nullable T; can invoke directly
+                    tail.EmitWrite(this, valueFrom);
+                }
+                else
+                { // nullable T; check HasValue
+                    using (Compiler.Local valOrNull = GetLocalWithValue(type, valueFrom))
+                    {
+                        LoadAddress(valOrNull, type);
+                        LoadValue(type.GetProperty("HasValue"));
+                        CodeLabel @end = DefineLabel();
+                        BranchIfFalse(@end, false);
+                        LoadAddress(valOrNull, type);
+                        EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
+                        tail.EmitWrite(this, null);
+                        MarkLabel(@end);
+                    }
+                }
+            }
+            else
+            { // ref-type; do a null-check
+                LoadValue(valueFrom);
+                CopyValue();
+                CodeLabel hasVal = DefineLabel(), @end = DefineLabel();
+                BranchIfTrue(hasVal, true);
+                DiscardValue();
+                Branch(@end, false);
+                MarkLabel(hasVal);
+                tail.EmitWrite(this, null);
+                MarkLabel(@end);
+            }
+        }
+
+        internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Compiler.Local valueFrom)
+        {
+
+            Type underlyingType;
+
+            if (Helpers.IsValueType(type) && (underlyingType = Helpers.GetUnderlyingType(type)) != null)
+            {
+                if (tail.RequiresOldValue)
+                {
+                    // we expect the input value to be in valueFrom; need to unpack it from T?
+                    using (Local loc = GetLocalWithValue(type, valueFrom))
+                    {
+                        LoadAddress(loc, type);
+                        EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
+                    }
+                }
+                else
+                {
+                    Helpers.DebugAssert(valueFrom == null); // not expecting a valueFrom in this case
+                }
+                tail.EmitRead(this, null); // either unwrapped on the stack or not provided
+                if (tail.ReturnsValue)
+                {
+                    // now re-wrap the value
+                    EmitCtor(type, underlyingType);
+                }
+                return;
+            }
+
+            // either a ref-type of a non-nullable struct; treat "as is", even if null
+            // (the type-serializer will handle the null case; it needs to allow null
+            // inputs to perform the correct type of subclass creation)
+            tail.EmitRead(this, valueFrom);
+        }
+
+        public void EmitCtor(Type type)
+        {
+            EmitCtor(type, Helpers.EmptyTypes);
+        }
+
+        public void EmitCtor(ConstructorInfo ctor)
+        {
+            if (ctor == null) throw new ArgumentNullException("ctor");
+            MemberInfo ctorMember = ctor;
+            CheckAccessibility(ref ctorMember);
+            il.Emit(OpCodes.Newobj, ctor);
+            TraceCompile(OpCodes.Newobj + ": " + ctor.DeclaringType);
+        }
+
+        public void InitLocal(Type type, Compiler.Local target)
+        {
+            LoadAddress(target, type, evenIfClass: true); // for class, initobj is a load-null, store-indirect
+            il.Emit(OpCodes.Initobj, type);
+            TraceCompile(OpCodes.Initobj + ": " + type);
+        }
+
+        public void EmitCtor(Type type, params Type[] parameterTypes)
+        {
+            Helpers.DebugAssert(type != null);
+            Helpers.DebugAssert(parameterTypes != null);
+            if (Helpers.IsValueType(type) && parameterTypes.Length == 0)
+            {
+                il.Emit(OpCodes.Initobj, type);
+                TraceCompile(OpCodes.Initobj + ": " + type);
+            }
+            else
+            {
+                ConstructorInfo ctor = Helpers.GetConstructor(type
+#if COREFX
+                .GetTypeInfo()
+#endif
+                    , parameterTypes, true);
+                if (ctor == null) throw new InvalidOperationException("No suitable constructor found for " + type.FullName);
+                EmitCtor(ctor);
+            }
+        }
+
+        BasicList knownTrustedAssemblies, knownUntrustedAssemblies;
+
+        bool InternalsVisible(Assembly assembly)
+        {
+            if (string.IsNullOrEmpty(assemblyName)) return false;
+            if (knownTrustedAssemblies != null)
+            {
+                if (knownTrustedAssemblies.IndexOfReference(assembly) >= 0)
+                {
+                    return true;
+                }
+            }
+            if (knownUntrustedAssemblies != null)
+            {
+                if (knownUntrustedAssemblies.IndexOfReference(assembly) >= 0)
+                {
+                    return false;
+                }
+            }
+            bool isTrusted = false;
+            Type attributeType = MapType(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute));
+            if (attributeType == null) return false;
+
+#if COREFX
+            foreach (System.Runtime.CompilerServices.InternalsVisibleToAttribute attrib in assembly.GetCustomAttributes(attributeType))
+#else
+            foreach (System.Runtime.CompilerServices.InternalsVisibleToAttribute attrib in assembly.GetCustomAttributes(attributeType, false))
+#endif
+            {
+                if (attrib.AssemblyName == assemblyName || attrib.AssemblyName.StartsWith(assemblyName + ","))
+                {
+                    isTrusted = true;
+                    break;
+                }
+            }
+
+            if (isTrusted)
+            {
+                if (knownTrustedAssemblies == null) knownTrustedAssemblies = new BasicList();
+                knownTrustedAssemblies.Add(assembly);
+            }
+            else
+            {
+                if (knownUntrustedAssemblies == null) knownUntrustedAssemblies = new BasicList();
+                knownUntrustedAssemblies.Add(assembly);
+            }
+            return isTrusted;
+        }
+
+        internal void CheckAccessibility(ref MemberInfo member)
+        {
+            if (member == null)
+            {
+                throw new ArgumentNullException(nameof(member));
+            }
+#if !COREFX
+            Type type;
+#endif
+            if (!NonPublic)
+            {
+                if (member is FieldInfo && member.Name.StartsWith("<") & member.Name.EndsWith(">k__BackingField"))
+                {
+                    var propName = member.Name.Substring(1, member.Name.Length - 17);
+                    var prop = member.DeclaringType.GetProperty(propName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
+                    if (prop != null) member = prop;
+                }
+                bool isPublic;
+#if COREFX
+                if (member is TypeInfo)
+                {
+                    TypeInfo ti = (TypeInfo)member;
+                    do
+                    {
+                        isPublic = ti.IsNestedPublic || ti.IsPublic || ((ti.IsNested || ti.IsNestedAssembly || ti.IsNestedFamORAssem) && InternalsVisible(ti.Assembly));
+                    } while (isPublic && ti.IsNested && (ti = ti.DeclaringType.GetTypeInfo()) != null);
+                }
+                else if (member is FieldInfo)
+                {
+                    FieldInfo field = ((FieldInfo)member);
+                    isPublic = field.IsPublic || ((field.IsAssembly || field.IsFamilyOrAssembly) && InternalsVisible(Helpers.GetAssembly(field.DeclaringType)));
+                }
+                else if (member is PropertyInfo)
+                {
+                    isPublic = true; // defer to get/set
+                }
+                else if (member is ConstructorInfo)
+                {
+                    ConstructorInfo ctor = ((ConstructorInfo)member);
+                    isPublic = ctor.IsPublic || ((ctor.IsAssembly || ctor.IsFamilyOrAssembly) && InternalsVisible(Helpers.GetAssembly(ctor.DeclaringType)));
+                }
+                else if (member is MethodInfo)
+                {
+                    MethodInfo method = ((MethodInfo)member);
+                    isPublic = method.IsPublic || ((method.IsAssembly || method.IsFamilyOrAssembly) && InternalsVisible(Helpers.GetAssembly(method.DeclaringType)));
+                    if (!isPublic)
+                    {
+                        // allow calls to TypeModel protected methods, and methods we are in the process of creating
+                        if (
+                                member is MethodBuilder ||
+                                member.DeclaringType == MapType(typeof(TypeModel)))
+                            isPublic = true;
+                    }
+                }
+                else
+                {
+                    throw new NotSupportedException(member.GetType().Name);
+                }
+#else
+                MemberTypes memberType = member.MemberType;
+                switch (memberType)
+                {
+                    case MemberTypes.TypeInfo:
+                        // top-level type
+                        type = (Type)member;
+                        isPublic = type.IsPublic || InternalsVisible(type.Assembly);
+                        break;
+                    case MemberTypes.NestedType:
+                        type = (Type)member;
+                        do
+                        {
+                            isPublic = type.IsNestedPublic || type.IsPublic || ((type.DeclaringType == null || type.IsNestedAssembly || type.IsNestedFamORAssem) && InternalsVisible(type.Assembly));
+                        } while (isPublic && (type = type.DeclaringType) != null); // ^^^ !type.IsNested, but not all runtimes have that
+                        break;
+                    case MemberTypes.Field:
+                        FieldInfo field = ((FieldInfo)member);
+                        isPublic = field.IsPublic || ((field.IsAssembly || field.IsFamilyOrAssembly) && InternalsVisible(field.DeclaringType.Assembly));
+                        break;
+                    case MemberTypes.Constructor:
+                        ConstructorInfo ctor = ((ConstructorInfo)member);
+                        isPublic = ctor.IsPublic || ((ctor.IsAssembly || ctor.IsFamilyOrAssembly) && InternalsVisible(ctor.DeclaringType.Assembly));
+                        break;
+                    case MemberTypes.Method:
+                        MethodInfo method = ((MethodInfo)member);
+                        isPublic = method.IsPublic || ((method.IsAssembly || method.IsFamilyOrAssembly) && InternalsVisible(method.DeclaringType.Assembly));
+                        if (!isPublic)
+                        {
+                            // allow calls to TypeModel protected methods, and methods we are in the process of creating
+                            if (
+                                member is MethodBuilder ||
+                                member.DeclaringType == MapType(typeof(TypeModel))) isPublic = true;
+                        }
+                        break;
+                    case MemberTypes.Property:
+                        isPublic = true; // defer to get/set
+                        break;
+                    default:
+                        throw new NotSupportedException(memberType.ToString());
+                }
+#endif
+                if (!isPublic)
+                {
+#if COREFX
+                    if (member is TypeInfo)
+                    {
+                        throw new InvalidOperationException("Non-public type cannot be used with full dll compilation: " +
+                                ((TypeInfo)member).FullName);
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException("Non-public member cannot be used with full dll compilation: " +
+                                member.DeclaringType.FullName + "." + member.Name);
+                    }
+
+#else
+                    switch (memberType)
+                    {
+                        case MemberTypes.TypeInfo:
+                        case MemberTypes.NestedType:
+                            throw new InvalidOperationException("Non-public type cannot be used with full dll compilation: " +
+                                ((Type)member).FullName);
+                        default:
+                            throw new InvalidOperationException("Non-public member cannot be used with full dll compilation: " +
+                                member.DeclaringType.FullName + "." + member.Name);
+                    }
+#endif
+
+                }
+            }
+        }
+
+        public void LoadValue(FieldInfo field)
+        {
+            MemberInfo member = field;
+            CheckAccessibility(ref member);
+            if (member is PropertyInfo)
+            {
+                LoadValue((PropertyInfo)member);
+            }
+            else
+            {
+                OpCode code = field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld;
+                il.Emit(code, field);
+                TraceCompile(code + ": " + field + " on " + field.DeclaringType);
+            }
+        }
+
+        public void StoreValue(FieldInfo field)
+        {
+            MemberInfo member = field;
+            CheckAccessibility(ref member);
+            if (member is PropertyInfo)
+            {
+                StoreValue((PropertyInfo)member);
+            }
+            else
+            {
+                OpCode code = field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld;
+                il.Emit(code, field);
+                TraceCompile(code + ": " + field + " on " + field.DeclaringType);
+            }
+        }
+
+        public void LoadValue(PropertyInfo property)
+        {
+            MemberInfo member = property;
+            CheckAccessibility(ref member);
+            EmitCall(Helpers.GetGetMethod(property, true, true));
+        }
+
+        public void StoreValue(PropertyInfo property)
+        {
+            MemberInfo member = property;
+            CheckAccessibility(ref member);
+            EmitCall(Helpers.GetSetMethod(property, true, true));
+        }
+
+        //internal void EmitInstance()
+        //{
+        //    if (isStatic) throw new InvalidOperationException();
+        //    Emit(OpCodes.Ldarg_0);
+        //}
+
+        internal static void LoadValue(ILGenerator il, int value)
+        {
+            switch (value)
+            {
+                case 0: il.Emit(OpCodes.Ldc_I4_0); break;
+                case 1: il.Emit(OpCodes.Ldc_I4_1); break;
+                case 2: il.Emit(OpCodes.Ldc_I4_2); break;
+                case 3: il.Emit(OpCodes.Ldc_I4_3); break;
+                case 4: il.Emit(OpCodes.Ldc_I4_4); break;
+                case 5: il.Emit(OpCodes.Ldc_I4_5); break;
+                case 6: il.Emit(OpCodes.Ldc_I4_6); break;
+                case 7: il.Emit(OpCodes.Ldc_I4_7); break;
+                case 8: il.Emit(OpCodes.Ldc_I4_8); break;
+                case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
+                default: il.Emit(OpCodes.Ldc_I4, value); break;
+            }
+        }
+
+        private bool UseShortForm(Local local)
+        {
+            return local.Value.LocalIndex < 256;
+        }
+
+        internal void LoadAddress(Local local, Type type, bool evenIfClass = false)
+        {
+            if (evenIfClass || Helpers.IsValueType(type))
+            {
+                if (local == null)
+                {
+                    throw new InvalidOperationException("Cannot load the address of the head of the stack");
+                }
+
+                if (local == this.InputValue)
+                {
+                    il.Emit(OpCodes.Ldarga_S, (isStatic ? (byte)0 : (byte)1));
+                    TraceCompile(OpCodes.Ldarga_S + ": $" + (isStatic ? 0 : 1));
+                }
+                else
+                {
+                    OpCode code = UseShortForm(local) ? OpCodes.Ldloca_S : OpCodes.Ldloca;
+                    il.Emit(code, local.Value);
+                    TraceCompile(code + ": $" + local.Value);
+                }
+
+            }
+            else
+            {   // reference-type; already *is* the address; just load it
+                LoadValue(local);
+            }
+        }
+
+        internal void Branch(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Br_S : OpCodes.Br;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal void BranchIfFalse(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Brfalse_S : OpCodes.Brfalse;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal void BranchIfTrue(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Brtrue_S : OpCodes.Brtrue;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal void BranchIfEqual(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Beq_S : OpCodes.Beq;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        //internal void TestEqual()
+        //{
+        //    Emit(OpCodes.Ceq);
+        //}
+
+        internal void CopyValue()
+        {
+            Emit(OpCodes.Dup);
+        }
+
+        internal void BranchIfGreater(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Bgt_S : OpCodes.Bgt;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal void BranchIfLess(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Blt_S : OpCodes.Blt;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal void DiscardValue()
+        {
+            Emit(OpCodes.Pop);
+        }
+
+        public void Subtract()
+        {
+            Emit(OpCodes.Sub);
+        }
+
+        public void Switch(CodeLabel[] jumpTable)
+        {
+            const int MAX_JUMPS = 128;
+
+            if (jumpTable.Length <= MAX_JUMPS)
+            {
+                // simple case
+                Label[] labels = new Label[jumpTable.Length];
+                for (int i = 0; i < labels.Length; i++)
+                {
+                    labels[i] = jumpTable[i].Value;
+                }
+                TraceCompile(OpCodes.Switch.ToString());
+                il.Emit(OpCodes.Switch, labels);
+            }
+            else
+            {
+                // too many to jump easily (especially on Android) - need to split up (note: uses a local pulled from the stack)
+                using (Local val = GetLocalWithValue(MapType(typeof(int)), null))
+                {
+                    int count = jumpTable.Length, offset = 0;
+                    int blockCount = count / MAX_JUMPS;
+                    if ((count % MAX_JUMPS) != 0) blockCount++;
+
+                    Label[] blockLabels = new Label[blockCount];
+                    for (int i = 0; i < blockCount; i++)
+                    {
+                        blockLabels[i] = il.DefineLabel();
+                    }
+                    CodeLabel endOfSwitch = DefineLabel();
+
+                    LoadValue(val);
+                    LoadValue(MAX_JUMPS);
+                    Emit(OpCodes.Div);
+                    TraceCompile(OpCodes.Switch.ToString());
+                    il.Emit(OpCodes.Switch, blockLabels);
+                    Branch(endOfSwitch, false);
+
+                    Label[] innerLabels = new Label[MAX_JUMPS];
+                    for (int blockIndex = 0; blockIndex < blockCount; blockIndex++)
+                    {
+                        il.MarkLabel(blockLabels[blockIndex]);
+
+                        int itemsThisBlock = Math.Min(MAX_JUMPS, count);
+                        count -= itemsThisBlock;
+                        if (innerLabels.Length != itemsThisBlock) innerLabels = new Label[itemsThisBlock];
+
+                        int subtract = offset;
+                        for (int j = 0; j < itemsThisBlock; j++)
+                        {
+                            innerLabels[j] = jumpTable[offset++].Value;
+                        }
+                        LoadValue(val);
+                        if (subtract != 0) // switches are always zero-based
+                        {
+                            LoadValue(subtract);
+                            Emit(OpCodes.Sub);
+                        }
+                        TraceCompile(OpCodes.Switch.ToString());
+                        il.Emit(OpCodes.Switch, innerLabels);
+                        if (count != 0)
+                        { // force default to the very bottom
+                            Branch(endOfSwitch, false);
+                        }
+                    }
+                    Helpers.DebugAssert(count == 0, "Should use exactly all switch items");
+                    MarkLabel(endOfSwitch);
+                }
+            }
+        }
+
+        internal void EndFinally()
+        {
+            il.EndExceptionBlock();
+            TraceCompile("EndExceptionBlock");
+        }
+
+        internal void BeginFinally()
+        {
+            il.BeginFinallyBlock();
+            TraceCompile("BeginFinallyBlock");
+        }
+
+        internal void EndTry(CodeLabel label, bool @short)
+        {
+            OpCode code = @short ? OpCodes.Leave_S : OpCodes.Leave;
+            il.Emit(code, label.Value);
+            TraceCompile(code + ": " + label.Index);
+        }
+
+        internal CodeLabel BeginTry()
+        {
+            CodeLabel label = new CodeLabel(il.BeginExceptionBlock(), nextLabel++);
+            TraceCompile("BeginExceptionBlock: " + label.Index);
+            return label;
+        }
+
+        internal void Constrain(Type type)
+        {
+            il.Emit(OpCodes.Constrained, type);
+            TraceCompile(OpCodes.Constrained + ": " + type);
+        }
+
+        internal void TryCast(Type type)
+        {
+            il.Emit(OpCodes.Isinst, type);
+            TraceCompile(OpCodes.Isinst + ": " + type);
+        }
+
+        internal void Cast(Type type)
+        {
+            il.Emit(OpCodes.Castclass, type);
+            TraceCompile(OpCodes.Castclass + ": " + type);
+        }
+
+        public IDisposable Using(Local local)
+        {
+            return new UsingBlock(this, local);
+        }
+
+        private sealed class UsingBlock : IDisposable
+        {
+            private Local local;
+            CompilerContext ctx;
+            CodeLabel label;
+            /// <summary>
+            /// Creates a new "using" block (equivalent) around a variable;
+            /// the variable must exist, and note that (unlike in C#) it is
+            /// the variables *final* value that gets disposed. If you need
+            /// *original* disposal, copy your variable first.
+            /// 
+            /// It is the callers responsibility to ensure that the variable's
+            /// scope fully-encapsulates the "using"; if not, the variable
+            /// may be re-used (and thus re-assigned) unexpectedly.
+            /// </summary>
+            public UsingBlock(CompilerContext ctx, Local local)
+            {
+                if (ctx == null) throw new ArgumentNullException("ctx");
+                if (local == null) throw new ArgumentNullException("local");
+
+                Type type = local.Type;
+                // check if **never** disposable
+                if ((Helpers.IsValueType(type) || Helpers.IsSealed(type)) &&
+                    !ctx.MapType(typeof(IDisposable)).IsAssignableFrom(type))
+                {
+                    return; // nothing to do! easiest "using" block ever
+                    // (note that C# wouldn't allow this as a "using" block,
+                    // but we'll be generous and simply not do anything)
+                }
+                this.local = local;
+                this.ctx = ctx;
+                label = ctx.BeginTry();
+
+            }
+            public void Dispose()
+            {
+                if (local == null || ctx == null) return;
+
+                ctx.EndTry(label, false);
+                ctx.BeginFinally();
+                Type disposableType = ctx.MapType(typeof(IDisposable));
+                MethodInfo dispose = disposableType.GetMethod("Dispose");
+                Type type = local.Type;
+                // remember that we've already (in the .ctor) excluded the case
+                // where it *cannot* be disposable
+                if (Helpers.IsValueType(type))
+                {
+                    ctx.LoadAddress(local, type);
+                    switch (ctx.MetadataVersion)
+                    {
+                        case ILVersion.Net1:
+                            ctx.LoadValue(local);
+                            ctx.CastToObject(type);
+                            break;
+                        default:
+                            ctx.Constrain(type);
+                            break;
+                    }
+                    ctx.EmitCall(dispose);
+                }
+                else
+                {
+                    Compiler.CodeLabel @null = ctx.DefineLabel();
+                    if (disposableType.IsAssignableFrom(type))
+                    {   // *known* to be IDisposable; just needs a null-check                            
+                        ctx.LoadValue(local);
+                        ctx.BranchIfFalse(@null, true);
+                        ctx.LoadAddress(local, type);
+                    }
+                    else
+                    {   // *could* be IDisposable; test via "as"
+                        using (Compiler.Local disp = new Compiler.Local(ctx, disposableType))
+                        {
+                            ctx.LoadValue(local);
+                            ctx.TryCast(disposableType);
+                            ctx.CopyValue();
+                            ctx.StoreValue(disp);
+                            ctx.BranchIfFalse(@null, true);
+                            ctx.LoadAddress(disp, disposableType);
+                        }
+                    }
+                    ctx.EmitCall(dispose);
+                    ctx.MarkLabel(@null);
+                }
+                ctx.EndFinally();
+                this.local = null;
+                this.ctx = null;
+                label = new CodeLabel(); // default
+            }
+        }
+
+        internal void Add()
+        {
+            Emit(OpCodes.Add);
+        }
+
+        internal void LoadLength(Local arr, bool zeroIfNull)
+        {
+            Helpers.DebugAssert(arr.Type.IsArray && arr.Type.GetArrayRank() == 1);
+
+            if (zeroIfNull)
+            {
+                Compiler.CodeLabel notNull = DefineLabel(), done = DefineLabel();
+                LoadValue(arr);
+                CopyValue(); // optimised for non-null case
+                BranchIfTrue(notNull, true);
+                DiscardValue();
+                LoadValue(0);
+                Branch(done, true);
+                MarkLabel(notNull);
+                Emit(OpCodes.Ldlen);
+                Emit(OpCodes.Conv_I4);
+                MarkLabel(done);
+            }
+            else
+            {
+                LoadValue(arr);
+                Emit(OpCodes.Ldlen);
+                Emit(OpCodes.Conv_I4);
+            }
+        }
+
+        internal void CreateArray(Type elementType, Local length)
+        {
+            LoadValue(length);
+            il.Emit(OpCodes.Newarr, elementType);
+            TraceCompile(OpCodes.Newarr + ": " + elementType);
+        }
+
+        internal void LoadArrayValue(Local arr, Local i)
+        {
+            Type type = arr.Type;
+            Helpers.DebugAssert(type.IsArray && arr.Type.GetArrayRank() == 1);
+            type = type.GetElementType();
+            Helpers.DebugAssert(type != null, "Not an array: " + arr.Type.FullName);
+            LoadValue(arr);
+            LoadValue(i);
+            switch (Helpers.GetTypeCode(type))
+            {
+                case ProtoTypeCode.SByte: Emit(OpCodes.Ldelem_I1); break;
+                case ProtoTypeCode.Int16: Emit(OpCodes.Ldelem_I2); break;
+                case ProtoTypeCode.Int32: Emit(OpCodes.Ldelem_I4); break;
+                case ProtoTypeCode.Int64: Emit(OpCodes.Ldelem_I8); break;
+
+                case ProtoTypeCode.Byte: Emit(OpCodes.Ldelem_U1); break;
+                case ProtoTypeCode.UInt16: Emit(OpCodes.Ldelem_U2); break;
+                case ProtoTypeCode.UInt32: Emit(OpCodes.Ldelem_U4); break;
+                case ProtoTypeCode.UInt64: Emit(OpCodes.Ldelem_I8); break; // odd, but this is what C# does...
+
+                case ProtoTypeCode.Single: Emit(OpCodes.Ldelem_R4); break;
+                case ProtoTypeCode.Double: Emit(OpCodes.Ldelem_R8); break;
+                default:
+                    if (Helpers.IsValueType(type))
+                    {
+                        il.Emit(OpCodes.Ldelema, type);
+                        il.Emit(OpCodes.Ldobj, type);
+                        TraceCompile(OpCodes.Ldelema + ": " + type);
+                        TraceCompile(OpCodes.Ldobj + ": " + type);
+                    }
+                    else
+                    {
+                        Emit(OpCodes.Ldelem_Ref);
+                    }
+
+                    break;
+            }
+        }
+
+        internal void LoadValue(Type type)
+        {
+            il.Emit(OpCodes.Ldtoken, type);
+            TraceCompile(OpCodes.Ldtoken + ": " + type);
+            EmitCall(MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"));
+        }
+
+        internal void ConvertToInt32(ProtoTypeCode typeCode, bool uint32Overflow)
+        {
+            switch (typeCode)
+            {
+                case ProtoTypeCode.Byte:
+                case ProtoTypeCode.SByte:
+                case ProtoTypeCode.Int16:
+                case ProtoTypeCode.UInt16:
+                    Emit(OpCodes.Conv_I4);
+                    break;
+                case ProtoTypeCode.Int32:
+                    break;
+                case ProtoTypeCode.Int64:
+                    Emit(OpCodes.Conv_Ovf_I4);
+                    break;
+                case ProtoTypeCode.UInt32:
+                    Emit(uint32Overflow ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4);
+                    break;
+                case ProtoTypeCode.UInt64:
+                    Emit(OpCodes.Conv_Ovf_I4_Un);
+                    break;
+                default:
+                    throw new InvalidOperationException("ConvertToInt32 not implemented for: " + typeCode.ToString());
+            }
+        }
+
+        internal void ConvertFromInt32(ProtoTypeCode typeCode, bool uint32Overflow)
+        {
+            switch (typeCode)
+            {
+                case ProtoTypeCode.SByte: Emit(OpCodes.Conv_Ovf_I1); break;
+                case ProtoTypeCode.Byte: Emit(OpCodes.Conv_Ovf_U1); break;
+                case ProtoTypeCode.Int16: Emit(OpCodes.Conv_Ovf_I2); break;
+                case ProtoTypeCode.UInt16: Emit(OpCodes.Conv_Ovf_U2); break;
+                case ProtoTypeCode.Int32: break;
+                case ProtoTypeCode.UInt32: Emit(uint32Overflow ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4); break;
+                case ProtoTypeCode.Int64: Emit(OpCodes.Conv_I8); break;
+                case ProtoTypeCode.UInt64: Emit(OpCodes.Conv_U8); break;
+                default: throw new InvalidOperationException();
+            }
+        }
+
+        internal void LoadValue(decimal value)
+        {
+            if (value == 0M)
+            {
+                LoadValue(typeof(decimal).GetField("Zero"));
+            }
+            else
+            {
+                int[] bits = decimal.GetBits(value);
+                LoadValue(bits[0]); // lo
+                LoadValue(bits[1]); // mid
+                LoadValue(bits[2]); // hi
+                LoadValue((int)(((uint)bits[3]) >> 31)); // isNegative (bool, but int for CLI purposes)
+                LoadValue((bits[3] >> 16) & 0xFF); // scale (byte, but int for CLI purposes)
+
+                EmitCtor(MapType(typeof(decimal)), new Type[] { MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(bool)), MapType(typeof(byte)) });
+            }
+        }
+
+        internal void LoadValue(Guid value)
+        {
+            if (value == Guid.Empty)
+            {
+                LoadValue(typeof(Guid).GetField("Empty"));
+            }
+            else
+            { // note we're adding lots of shorts/bytes here - but at the IL level they are I4, not I1/I2 (which barely exist)
+                byte[] bytes = value.ToByteArray();
+                int i = (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
+                LoadValue(i);
+                short s = (short)((bytes[4]) | (bytes[5] << 8));
+                LoadValue(s);
+                s = (short)((bytes[6]) | (bytes[7] << 8));
+                LoadValue(s);
+                for (i = 8; i <= 15; i++)
+                {
+                    LoadValue(bytes[i]);
+                }
+                EmitCtor(MapType(typeof(Guid)), new Type[] { MapType(typeof(int)), MapType(typeof(short)), MapType(typeof(short)),
+                            MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)) });
+            }
+        }
+
+        //internal void LoadValue(bool value)
+        //{
+        //    Emit(value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
+        //}
+
+        internal void LoadSerializationContext()
+        {
+            LoadReaderWriter();
+            LoadValue((isWriter ? typeof(ProtoWriter) : typeof(ProtoReader)).GetProperty("Context"));
+        }
+
+        private readonly TypeModel model;
+
+        internal Type MapType(Type type)
+        {
+            return model.MapType(type);
+        }
+
+        private readonly ILVersion metadataVersion;
+        public ILVersion MetadataVersion { get { return metadataVersion; } }
+        public enum ILVersion
+        {
+            Net1, Net2
+        }
+
+        internal bool AllowInternal(PropertyInfo property)
+        {
+            return NonPublic ? true : InternalsVisible(Helpers.GetAssembly(property.DeclaringType));
+        }
+    }
+}
+#endif

+ 7 - 0
Server/ThirdParty/protobuf-net/Compiler/CompilerDelegates.cs

@@ -0,0 +1,7 @@
+#if FEAT_COMPILER
+namespace ProtoBuf.Compiler
+{
+    internal delegate void ProtoSerializer(object value, ProtoWriter dest);
+    internal delegate object ProtoDeserializer(object value, ProtoReader source);
+}
+#endif

+ 58 - 0
Server/ThirdParty/protobuf-net/Compiler/Local.cs

@@ -0,0 +1,58 @@
+#if FEAT_COMPILER
+using System;
+using System.Reflection.Emit;
+
+namespace ProtoBuf.Compiler
+{
+    internal sealed class Local : IDisposable
+    {
+        // public static readonly Local InputValue = new Local(null, null);
+        private LocalBuilder value;
+        private readonly Type type;
+        private CompilerContext ctx;
+
+        private Local(LocalBuilder value, Type type)
+        {
+            this.value = value;
+            this.type = type;
+        }
+
+        internal Local(CompilerContext ctx, Type type)
+        {
+            this.ctx = ctx;
+            if (ctx != null) { value = ctx.GetFromPool(type); }
+            this.type = type;
+        }
+
+        internal LocalBuilder Value => value ?? throw new ObjectDisposedException(GetType().Name);
+
+        public Type Type => type;
+
+        public Local AsCopy()
+        {
+            if (ctx == null) return this; // can re-use if context-free
+            return new Local(value, this.type);
+        }
+        
+        public void Dispose()
+        {
+            if (ctx != null)
+            {
+                // only *actually* dispose if this is context-bound; note that non-bound
+                // objects are cheekily re-used, and *must* be left intact agter a "using" etc
+                ctx.ReleaseToPool(value);
+                value = null; 
+                ctx = null;
+            }            
+        }
+
+        internal bool IsSame(Local other)
+        {
+            if((object)this == (object)other) return true;
+
+            object ourVal = value; // use prop to ensure obj-disposed etc
+            return other != null && ourVal == (object)(other.value); 
+        }
+    }
+}
+#endif

+ 49 - 0
Server/ThirdParty/protobuf-net/DataFormat.cs

@@ -0,0 +1,49 @@
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Sub-format to use when serializing/deserializing data
+    /// </summary>
+    public enum DataFormat
+    {
+        /// <summary>
+        /// Uses the default encoding for the data-type.
+        /// </summary>
+        Default,
+
+        /// <summary>
+        /// When applied to signed integer-based data (including Decimal), this
+        /// indicates that zigzag variant encoding will be used. This means that values
+        /// with small magnitude (regardless of sign) take a small amount
+        /// of space to encode.
+        /// </summary>
+        ZigZag,
+
+        /// <summary>
+        /// When applied to signed integer-based data (including Decimal), this
+        /// indicates that two's-complement variant encoding will be used.
+        /// This means that any -ve number will take 10 bytes (even for 32-bit),
+        /// so should only be used for compatibility.
+        /// </summary>
+        TwosComplement,
+
+        /// <summary>
+        /// When applied to signed integer-based data (including Decimal), this
+        /// indicates that a fixed amount of space will be used.
+        /// </summary>
+        FixedSize,
+
+        /// <summary>
+        /// When applied to a sub-message, indicates that the value should be treated
+        /// as group-delimited.
+        /// </summary>
+        Group,
+
+        /// <summary>
+        /// When applied to members of types such as DateTime or TimeSpan, specifies
+        /// that the "well known" standardized representation should be use; DateTime uses Timestamp,
+        /// 
+        /// </summary>
+        WellKnown
+    }
+}

+ 176 - 0
Server/ThirdParty/protobuf-net/DiscriminatedUnion.Serializable.cs

@@ -0,0 +1,176 @@
+#if PLAT_BINARYFORMATTER
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace ProtoBuf
+{
+    [Serializable]
+    public readonly partial struct DiscriminatedUnionObject : ISerializable
+    {
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (Discriminator != default) info.AddValue("d", Discriminator);
+            if (Object is object) info.AddValue("o", Object);
+        }
+        private DiscriminatedUnionObject(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": Discriminator = (int)field.Value; break;
+                    case "o": Object = field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion128Object : ISerializable
+    {
+        [FieldOffset(8)] private readonly long _lo;
+        [FieldOffset(16)] private readonly long _hi;
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (_lo != default) info.AddValue("l", _lo);
+            if (_hi != default) info.AddValue("h", _hi);
+            if (Object != null) info.AddValue("o", Object);
+        }
+        private DiscriminatedUnion128Object(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "l": _lo = (long)field.Value; break;
+                    case "h": _hi = (long)field.Value; break;
+                    case "o": Object = field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion128 : ISerializable
+    {
+        [FieldOffset(8)] private readonly long _lo;
+        [FieldOffset(16)] private readonly long _hi;
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (_lo != default) info.AddValue("l", _lo);
+            if (_hi != default) info.AddValue("h", _hi);
+        }
+        private DiscriminatedUnion128(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "l": _lo = (long)field.Value; break;
+                    case "h": _hi = (long)field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion64 : ISerializable
+    {
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (Int64 != default) info.AddValue("i", Int64);
+        }
+        private DiscriminatedUnion64(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "i": Int64 = (long)field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion64Object : ISerializable
+    {
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (Int64 != default) info.AddValue("i", Int64);
+            if (Object is object) info.AddValue("o", Object);
+        }
+        private DiscriminatedUnion64Object(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "i": Int64 = (long)field.Value; break;
+                    case "o": Object = field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion32 : ISerializable
+    {
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (Int32 != default) info.AddValue("i", Int32);
+        }
+        private DiscriminatedUnion32(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "i": Int32 = (int)field.Value; break;
+                }
+            }
+        }
+    }
+
+    [Serializable]
+    public readonly partial struct DiscriminatedUnion32Object : ISerializable
+    {
+        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (_discriminator != default) info.AddValue("d", _discriminator);
+            if (Int32 != default) info.AddValue("i", Int32);
+            if (Object is object) info.AddValue("o", Object);
+        }
+        private DiscriminatedUnion32Object(SerializationInfo info, StreamingContext context)
+        {
+            this = default;
+            foreach (var field in info)
+            {
+                switch (field.Name)
+                {
+                    case "d": _discriminator = (int)field.Value; break;
+                    case "i": Int32 = (int)field.Value; break;
+                    case "o": Object = field.Value; break;
+                }
+            }
+        }
+    }
+}
+#endif

+ 416 - 0
Server/ThirdParty/protobuf-net/DiscriminatedUnion.cs

@@ -0,0 +1,416 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ProtoBuf
+{
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    public readonly partial struct DiscriminatedUnionObject
+    {
+
+        /// <summary>The value typed as Object</summary>
+        public readonly object Object;
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => Discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnionObject(int discriminator, object value)
+        {
+            Discriminator = discriminator;
+            Object = value;
+        }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnionObject value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+
+        /// <summary>The discriminator value</summary>
+        public int Discriminator { get; }
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion64
+    {
+#if !FEAT_SAFE
+		unsafe static DiscriminatedUnion64()
+        {
+            if (sizeof(DateTime) > 8) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64));
+            if (sizeof(TimeSpan) > 8) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64));
+        }
+#endif
+		[FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64
+
+        /// <summary>The value typed as Int64</summary>
+        [FieldOffset(8)] public readonly long Int64;
+        /// <summary>The value typed as UInt64</summary>
+        [FieldOffset(8)] public readonly ulong UInt64;
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(8)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(8)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(8)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(8)] public readonly float Single;
+        /// <summary>The value typed as Double</summary>
+        [FieldOffset(8)] public readonly double Double;
+        /// <summary>The value typed as DateTime</summary>
+        [FieldOffset(8)] public readonly DateTime DateTime;
+        /// <summary>The value typed as TimeSpan</summary>
+        [FieldOffset(8)] public readonly TimeSpan TimeSpan;
+
+        private DiscriminatedUnion64(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, long value) : this(discriminator) { Int64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, ulong value) : this(discriminator) { UInt64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, double value) : this(discriminator) { Double = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion64 value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion128Object
+    {
+#if !FEAT_SAFE
+		unsafe static DiscriminatedUnion128Object()
+        {
+            if (sizeof(DateTime) > 16) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object));
+            if (sizeof(TimeSpan) > 16) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object));
+            if (sizeof(Guid) > 16) throw new InvalidOperationException(nameof(Guid) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object));
+        }
+#endif
+
+		[FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64
+
+        /// <summary>The value typed as Int64</summary>
+        [FieldOffset(8)] public readonly long Int64;
+        /// <summary>The value typed as UInt64</summary>
+        [FieldOffset(8)] public readonly ulong UInt64;
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(8)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(8)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(8)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(8)] public readonly float Single;
+        /// <summary>The value typed as Double</summary>
+        [FieldOffset(8)] public readonly double Double;
+        /// <summary>The value typed as DateTime</summary>
+        [FieldOffset(8)] public readonly DateTime DateTime;
+        /// <summary>The value typed as TimeSpan</summary>
+        [FieldOffset(8)] public readonly TimeSpan TimeSpan;
+        /// <summary>The value typed as Guid</summary>
+        [FieldOffset(8)] public readonly Guid Guid;
+        /// <summary>The value typed as Object</summary>
+        [FieldOffset(24)] public readonly object Object;
+
+        private DiscriminatedUnion128Object(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, long value) : this(discriminator) { Int64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, ulong value) : this(discriminator) { UInt64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, double value) : this(discriminator) { Double = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128Object(int discriminator, Guid? value) : this(value.HasValue ? discriminator : 0) { Guid = value.GetValueOrDefault(); }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion128Object value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion128
+    {
+#if !FEAT_SAFE
+        unsafe static DiscriminatedUnion128()
+        {
+            if (sizeof(DateTime) > 16) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128));
+            if (sizeof(TimeSpan) > 16) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128));
+            if (sizeof(Guid) > 16) throw new InvalidOperationException(nameof(Guid) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128));
+        }
+#endif
+        [FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64
+
+        /// <summary>The value typed as Int64</summary>
+        [FieldOffset(8)] public readonly long Int64;
+        /// <summary>The value typed as UInt64</summary>
+        [FieldOffset(8)] public readonly ulong UInt64;
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(8)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(8)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(8)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(8)] public readonly float Single;
+        /// <summary>The value typed as Double</summary>
+        [FieldOffset(8)] public readonly double Double;
+        /// <summary>The value typed as DateTime</summary>
+        [FieldOffset(8)] public readonly DateTime DateTime;
+        /// <summary>The value typed as TimeSpan</summary>
+        [FieldOffset(8)] public readonly TimeSpan TimeSpan;
+        /// <summary>The value typed as Guid</summary>
+        [FieldOffset(8)] public readonly Guid Guid;
+
+        private DiscriminatedUnion128(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, long value) : this(discriminator) { Int64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, ulong value) : this(discriminator) { UInt64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, double value) : this(discriminator) { Double = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion128(int discriminator, Guid? value) : this(value.HasValue ? discriminator : 0) { Guid = value.GetValueOrDefault(); }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion128 value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion64Object
+    {
+#if !FEAT_SAFE
+        unsafe static DiscriminatedUnion64Object()
+        {
+            if (sizeof(DateTime) > 8) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64Object));
+            if (sizeof(TimeSpan) > 8) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64Object));
+        }
+#endif
+        [FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64
+
+        /// <summary>The value typed as Int64</summary>
+        [FieldOffset(8)] public readonly long Int64;
+        /// <summary>The value typed as UInt64</summary>
+        [FieldOffset(8)] public readonly ulong UInt64;
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(8)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(8)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(8)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(8)] public readonly float Single;
+        /// <summary>The value typed as Double</summary>
+        [FieldOffset(8)] public readonly double Double;
+        /// <summary>The value typed as DateTime</summary>
+        [FieldOffset(8)] public readonly DateTime DateTime;
+        /// <summary>The value typed as TimeSpan</summary>
+        [FieldOffset(8)] public readonly TimeSpan TimeSpan;
+        /// <summary>The value typed as Object</summary>
+        [FieldOffset(16)] public readonly object Object;
+
+        private DiscriminatedUnion64Object(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, long value) : this(discriminator) { Int64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, ulong value) : this(discriminator) { UInt64 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, double value) : this(discriminator) { Double = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion64Object(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion64Object value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion32
+    {
+        [FieldOffset(0)] private readonly int _discriminator;
+
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(4)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(4)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(4)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(4)] public readonly float Single;
+
+        private DiscriminatedUnion32(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion32 value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+
+    /// <summary>Represent multiple types as a union; this is used as part of OneOf -
+    /// note that it is the caller's responsbility to only read/write the value as the same type</summary>
+    [StructLayout(LayoutKind.Explicit)]
+    public readonly partial struct DiscriminatedUnion32Object
+    {
+        [FieldOffset(0)] private readonly int _discriminator;
+
+        /// <summary>The value typed as Int32</summary>
+        [FieldOffset(4)] public readonly int Int32;
+        /// <summary>The value typed as UInt32</summary>
+        [FieldOffset(4)] public readonly uint UInt32;
+        /// <summary>The value typed as Boolean</summary>
+        [FieldOffset(4)] public readonly bool Boolean;
+        /// <summary>The value typed as Single</summary>
+        [FieldOffset(4)] public readonly float Single;
+        /// <summary>The value typed as Object</summary>
+        [FieldOffset(8)] public readonly object Object;
+
+        private DiscriminatedUnion32Object(int discriminator) : this()
+        {
+            _discriminator = discriminator;
+        }
+
+        /// <summary>Indicates whether the specified discriminator is assigned</summary>
+        public bool Is(int discriminator) => _discriminator == discriminator;
+
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32Object(int discriminator, int value) : this(discriminator) { Int32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32Object(int discriminator, float value) : this(discriminator) { Single = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32Object(int discriminator, bool value) : this(discriminator) { Boolean = value; }
+        /// <summary>Create a new discriminated union value</summary>
+        public DiscriminatedUnion32Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; }
+
+        /// <summary>Reset a value if the specified discriminator is assigned</summary>
+        public static void Reset(ref DiscriminatedUnion32Object value, int discriminator)
+        {
+            if (value.Discriminator == discriminator) value = default;
+        }
+        /// <summary>The discriminator value</summary>
+        public int Discriminator => _discriminator;
+    }
+}

+ 284 - 0
Server/ThirdParty/protobuf-net/Extensible.cs

@@ -0,0 +1,284 @@
+using System;
+using System.Collections.Generic;
+using ProtoBuf.Meta;
+using System.Collections;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Simple base class for supporting unexpected fields allowing
+    /// for loss-less round-tips/merge, even if the data is not understod.
+    /// The additional fields are (by default) stored in-memory in a buffer.
+    /// </summary>
+    /// <remarks>As an example of an alternative implementation, you might
+    /// choose to use the file system (temporary files) as the back-end, tracking
+    /// only the paths [such an object would ideally be IDisposable and use
+    /// a finalizer to ensure that the files are removed].</remarks>
+    /// <seealso cref="IExtensible"/>
+    public abstract class Extensible : IExtensible
+    {
+        // note: not marked ProtoContract - no local state, and can't 
+        // predict sub-classes
+
+        private IExtension extensionObject;
+
+        IExtension IExtensible.GetExtensionObject(bool createIfMissing)
+        {
+            return GetExtensionObject(createIfMissing);
+        }
+
+        /// <summary>
+        /// Retrieves the <see cref="IExtension">extension</see> object for the current
+        /// instance, optionally creating it if it does not already exist.
+        /// </summary>
+        /// <param name="createIfMissing">Should a new extension object be
+        /// created if it does not already exist?</param>
+        /// <returns>The extension object if it exists (or was created), or null
+        /// if the extension object does not exist or is not available.</returns>
+        /// <remarks>The <c>createIfMissing</c> argument is false during serialization,
+        /// and true during deserialization upon encountering unexpected fields.</remarks>
+        protected virtual IExtension GetExtensionObject(bool createIfMissing)
+        {
+            return GetExtensionObject(ref extensionObject, createIfMissing);
+        }
+
+        /// <summary>
+        /// Provides a simple, default implementation for <see cref="IExtension">extension</see> support,
+        /// optionally creating it if it does not already exist. Designed to be called by
+        /// classes implementing <see cref="IExtensible"/>.
+        /// </summary>
+        /// <param name="createIfMissing">Should a new extension object be
+        /// created if it does not already exist?</param>
+        /// <param name="extensionObject">The extension field to check (and possibly update).</param>
+        /// <returns>The extension object if it exists (or was created), or null
+        /// if the extension object does not exist or is not available.</returns>
+        /// <remarks>The <c>createIfMissing</c> argument is false during serialization,
+        /// and true during deserialization upon encountering unexpected fields.</remarks>
+        public static IExtension GetExtensionObject(ref IExtension extensionObject, bool createIfMissing)
+        {
+            if (createIfMissing && extensionObject == null)
+            {
+                extensionObject = new BufferExtension();
+            }
+            return extensionObject;
+        }
+
+#if !NO_RUNTIME
+        /// <summary>
+        /// Appends the value as an additional (unexpected) data-field for the instance.
+        /// Note that for non-repeated sub-objects, this equates to a merge operation;
+        /// for repeated sub-objects this adds a new instance to the set; for simple
+        /// values the new value supercedes the old value.
+        /// </summary>
+        /// <remarks>Note that appending a value does not remove the old value from
+        /// the stream; avoid repeatedly appending values for the same field.</remarks>
+        /// <typeparam name="TValue">The type of the value to append.</typeparam>
+        /// <param name="instance">The extensible object to append the value to.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="value">The value to append.</param>
+        public static void AppendValue<TValue>(IExtensible instance, int tag, TValue value)
+        {
+            AppendValue<TValue>(instance, tag, DataFormat.Default, value);
+        }
+
+        /// <summary>
+        /// Appends the value as an additional (unexpected) data-field for the instance.
+        /// Note that for non-repeated sub-objects, this equates to a merge operation;
+        /// for repeated sub-objects this adds a new instance to the set; for simple
+        /// values the new value supercedes the old value.
+        /// </summary>
+        /// <remarks>Note that appending a value does not remove the old value from
+        /// the stream; avoid repeatedly appending values for the same field.</remarks>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="format">The data-format to use when encoding the value.</param>
+        /// <param name="instance">The extensible object to append the value to.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="value">The value to append.</param>
+        public static void AppendValue<TValue>(IExtensible instance, int tag, DataFormat format, TValue value)
+        {
+            ExtensibleUtil.AppendExtendValue(RuntimeTypeModel.Default, instance, tag, format, value);
+        }
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned is the composed value after merging any duplicated content; if the
+        /// value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <returns>The effective value of the field, or the default value if not found.</returns>
+        public static TValue GetValue<TValue>(IExtensible instance, int tag)
+        {
+            return GetValue<TValue>(instance, tag, DataFormat.Default);
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned is the composed value after merging any duplicated content; if the
+        /// value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <returns>The effective value of the field, or the default value if not found.</returns>
+        public static TValue GetValue<TValue>(IExtensible instance, int tag, DataFormat format)
+        {
+            TryGetValue<TValue>(instance, tag, format, out TValue value);
+            return value;
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned (in "value") is the composed value after merging any duplicated content;
+        /// if the value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="value">The effective value of the field, or the default value if not found.</param>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <returns>True if data for the field was present, false otherwise.</returns>
+        public static bool TryGetValue<TValue>(IExtensible instance, int tag, out TValue value)
+        {
+            return TryGetValue<TValue>(instance, tag, DataFormat.Default, out value);
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned (in "value") is the composed value after merging any duplicated content;
+        /// if the value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="value">The effective value of the field, or the default value if not found.</param>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <returns>True if data for the field was present, false otherwise.</returns>
+        public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, out TValue value)
+        {
+            return TryGetValue<TValue>(instance, tag, format, false, out value);
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned (in "value") is the composed value after merging any duplicated content;
+        /// if the value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="value">The effective value of the field, or the default value if not found.</param>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <param name="allowDefinedTag">Allow tags that are present as part of the definition; for example, to query unknown enum values.</param>
+        /// <returns>True if data for the field was present, false otherwise.</returns>
+        public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out TValue value)
+        {
+            value = default;
+            bool set = false;
+            foreach (TValue val in ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, true, allowDefinedTag))
+            {
+                // expecting at most one yield...
+                // but don't break; need to read entire stream
+                value = val;
+                set = true;
+            }
+
+            return set;
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
+        /// (list) fields.
+        /// </summary>
+        /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <returns>An enumerator that yields each occurrence of the field.</returns>
+        public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag)
+        {
+            return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, DataFormat.Default, false, false);
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
+        /// (list) fields.
+        /// </summary>
+        /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks>
+        /// <typeparam name="TValue">The data-type of the field.</typeparam>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <returns>An enumerator that yields each occurrence of the field.</returns>
+        public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag, DataFormat format)
+        {
+            return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, false, false);
+        }
+#endif
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// The value returned (in "value") is the composed value after merging any duplicated content;
+        /// if the value is "repeated" (a list), then use GetValues instead.
+        /// </summary>
+        /// <param name="type">The data-type of the field.</param>
+        /// <param name="model">The model to use for configuration.</param>
+        /// <param name="value">The effective value of the field, or the default value if not found.</param>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <param name="allowDefinedTag">Allow tags that are present as part of the definition; for example, to query unknown enum values.</param>
+        /// <returns>True if data for the field was present, false otherwise.</returns>
+        public static bool TryGetValue(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out object value)
+        {
+            value = null;
+            bool set = false;
+            foreach (object val in ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, true, allowDefinedTag))
+            {
+                // expecting at most one yield...
+                // but don't break; need to read entire stream
+                value = val;
+                set = true;
+            }
+
+            return set;
+        }
+
+        /// <summary>
+        /// Queries an extensible object for an additional (unexpected) data-field for the instance.
+        /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated"
+        /// (list) fields.
+        /// </summary>
+        /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks>
+        /// <param name="model">The model to use for configuration.</param>
+        /// <param name="type">The data-type of the field.</param>
+        /// <param name="instance">The extensible object to obtain the value from.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="format">The data-format to use when decoding the value.</param>
+        /// <returns>An enumerator that yields each occurrence of the field.</returns>
+        public static IEnumerable GetValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format)
+        {
+            return ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, false, false);
+        }
+
+        /// <summary>
+        /// Appends the value as an additional (unexpected) data-field for the instance.
+        /// Note that for non-repeated sub-objects, this equates to a merge operation;
+        /// for repeated sub-objects this adds a new instance to the set; for simple
+        /// values the new value supercedes the old value.
+        /// </summary>
+        /// <remarks>Note that appending a value does not remove the old value from
+        /// the stream; avoid repeatedly appending values for the same field.</remarks>
+        /// <param name="model">The model to use for configuration.</param>
+        /// <param name="format">The data-format to use when encoding the value.</param>
+        /// <param name="instance">The extensible object to append the value to.</param>
+        /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param>
+        /// <param name="value">The value to append.</param>
+        public static void AppendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value)
+        {
+            ExtensibleUtil.AppendExtendValue(model, instance, tag, format, value);
+        }
+    }
+}

+ 118 - 0
Server/ThirdParty/protobuf-net/ExtensibleUtil.cs

@@ -0,0 +1,118 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using ProtoBuf.Meta;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// This class acts as an internal wrapper allowing us to do a dynamic
+    /// methodinfo invoke; an't put into Serializer as don't want on public
+    /// API; can't put into Serializer&lt;T&gt; since we need to invoke
+    /// across classes
+    /// </summary>
+    internal static class ExtensibleUtil
+    {
+
+#if !NO_RUNTIME
+        /// <summary>
+        /// All this does is call GetExtendedValuesTyped with the correct type for "instance";
+        /// this ensures that we don't get issues with subclasses declaring conflicting types -
+        /// the caller must respect the fields defined for the type they pass in.
+        /// </summary>
+        internal static IEnumerable<TValue> GetExtendedValues<TValue>(IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag)
+        {
+            foreach (TValue value in GetExtendedValues(RuntimeTypeModel.Default, typeof(TValue), instance, tag, format, singleton, allowDefinedTag))
+            {
+                yield return value;
+            }
+        }
+#endif
+        /// <summary>
+        /// All this does is call GetExtendedValuesTyped with the correct type for "instance";
+        /// this ensures that we don't get issues with subclasses declaring conflicting types -
+        /// the caller must respect the fields defined for the type they pass in.
+        /// </summary>
+        internal static IEnumerable GetExtendedValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag)
+        {
+            if (instance == null) throw new ArgumentNullException(nameof(instance));
+            if (tag <= 0) throw new ArgumentOutOfRangeException(nameof(tag));
+            IExtension extn = instance.GetExtensionObject(false);
+
+            if (extn == null)
+            {
+                yield break;
+            }
+
+            Stream stream = extn.BeginQuery();
+            object value = null;
+            ProtoReader reader = null;
+            try
+            {
+                SerializationContext ctx = new SerializationContext();
+                reader = ProtoReader.Create(stream, model, ctx, ProtoReader.TO_EOF);
+                while (model.TryDeserializeAuxiliaryType(reader, format, tag, type, ref value, true, true, false, false, null) && value != null)
+                {
+                    if (!singleton)
+                    {
+                        yield return value;
+
+                        value = null; // fresh item each time
+                    }
+                }
+                if (singleton && value != null)
+                {
+                    yield return value;
+                }
+            }
+            finally
+            {
+                ProtoReader.Recycle(reader);
+                extn.EndQuery(stream);
+            }
+        }
+
+        internal static void AppendExtendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value)
+        {
+            if (instance == null) throw new ArgumentNullException(nameof(instance));
+            if (value == null) throw new ArgumentNullException(nameof(value));
+
+            // TODO
+            //model.CheckTagNotInUse(tag);
+
+            // obtain the extension object and prepare to write
+            IExtension extn = instance.GetExtensionObject(true);
+            if (extn == null) throw new InvalidOperationException("No extension object available; appended data would be lost.");
+            bool commit = false;
+            Stream stream = extn.BeginAppend();
+            try
+            {
+                using (ProtoWriter writer = ProtoWriter.Create(stream, model, null))
+                {
+                    model.TrySerializeAuxiliaryType(writer, null, format, tag, value, false, null);
+                    writer.Close();
+                }
+                commit = true;
+            }
+            finally
+            {
+                extn.EndAppend(stream, commit);
+            }
+        }
+
+        //        /// <summary>
+        //        /// Stores the given value into the instance's stream; the serializer
+        //        /// is inferred from TValue and format.
+        //        /// </summary>
+        //        /// <remarks>Needs to be public to be callable thru reflection in Silverlight</remarks>
+        //        public static void AppendExtendValueTyped<TSource, TValue>(
+        //            TypeModel model, TSource instance, int tag, DataFormat format, TValue value)
+        //            where TSource : class, IExtensible
+        //        {
+        //            AppendExtendValue(model, instance, tag, format, value);
+        //        }
+
+    }
+
+}

BIN=BIN
Server/ThirdParty/protobuf-net/GlobalSuppressions.cs


+ 638 - 0
Server/ThirdParty/protobuf-net/Helpers.cs

@@ -0,0 +1,638 @@
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+#if COREFX
+using System.Linq;
+#endif
+#if PROFILE259
+using System.Reflection;
+using System.Linq;
+#else
+using System.Reflection;
+#endif
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Not all frameworks are created equal (fx1.1 vs fx2.0,
+    /// micro-framework, compact-framework,
+    /// silverlight, etc). This class simply wraps up a few things that would
+    /// otherwise make the real code unnecessarily messy, providing fallback
+    /// implementations if necessary.
+    /// </summary>
+    internal sealed class Helpers
+    {
+        private Helpers() { }
+
+        public static StringBuilder AppendLine(StringBuilder builder)
+        {
+            return builder.AppendLine();
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void DebugWriteLine(string message, object obj)
+        {
+#if DEBUG
+            string suffix;
+            try
+            {
+                suffix = obj == null ? "(null)" : obj.ToString();
+            }
+            catch
+            {
+                suffix = "(exception)";
+            }
+            DebugWriteLine(message + ": " + suffix);
+#endif
+        }
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void DebugWriteLine(string message)
+        {
+#if DEBUG
+            System.Diagnostics.Debug.WriteLine(message);
+#endif
+        }
+        [System.Diagnostics.Conditional("TRACE")]
+        public static void TraceWriteLine(string message)
+        {
+#if TRACE
+#if CF2 || PORTABLE || COREFX || PROFILE259
+			System.Diagnostics.Debug.WriteLine(message);
+#else
+            System.Diagnostics.Trace.WriteLine(message);
+#endif
+#endif
+        }
+
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void DebugAssert(bool condition, string message)
+        {
+#if DEBUG
+            if (!condition)
+            {
+                System.Diagnostics.Debug.Assert(false, message);
+            }
+#endif
+        }
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void DebugAssert(bool condition, string message, params object[] args)
+        {
+#if DEBUG
+            if (!condition) DebugAssert(false, string.Format(message, args));
+#endif
+        }
+        [System.Diagnostics.Conditional("DEBUG")]
+        public static void DebugAssert(bool condition)
+        {
+#if DEBUG   
+            if (!condition && System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
+            System.Diagnostics.Debug.Assert(condition);
+#endif
+        }
+#if !NO_RUNTIME
+        public static void Sort(int[] keys, object[] values)
+        {
+            // bubble-sort; it'll work on MF, has small code,
+            // and works well-enough for our sizes. This approach
+            // also allows us to do `int` compares without having
+            // to go via IComparable etc, so win:win
+            bool swapped;
+            do
+            {
+                swapped = false;
+                for (int i = 1; i < keys.Length; i++)
+                {
+                    if (keys[i - 1] > keys[i])
+                    {
+                        int tmpKey = keys[i];
+                        keys[i] = keys[i - 1];
+                        keys[i - 1] = tmpKey;
+                        object tmpValue = values[i];
+                        values[i] = values[i - 1];
+                        values[i - 1] = tmpValue;
+                        swapped = true;
+                    }
+                }
+            } while (swapped);
+        }
+#endif
+
+#if COREFX
+		internal static MemberInfo GetInstanceMember(TypeInfo declaringType, string name)
+        {
+            var members = declaringType.AsType().GetMember(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+            switch(members.Length)
+            {
+                case 0: return null;
+                case 1: return members[0];
+                default: throw new AmbiguousMatchException(name);
+            }
+        }
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name)
+        {
+            foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (method.Name == name) return method;
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name)
+        {
+            return GetInstanceMethod(declaringType.AsType(), name); ;
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name)
+        {
+            foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (method.Name == name) return method;
+            }
+            return null;
+        }
+
+        internal static MethodInfo GetStaticMethod(TypeInfo declaringType, string name)
+        {
+            return GetStaticMethod(declaringType.AsType(), name);
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes)
+        {
+            foreach(MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method;
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] parameterTypes)
+        {
+            foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method;
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name, Type[] types)
+        {
+            return GetInstanceMethod(declaringType.AsType(), name, types);
+        }
+#elif PROFILE259
+        internal static MemberInfo GetInstanceMember(TypeInfo declaringType, string name)
+        {
+            IEnumerable<MemberInfo> members = declaringType.DeclaredMembers;
+            IList<MemberInfo> found = new List<MemberInfo>();
+            foreach (MemberInfo member in members)
+            {
+                if (member.Name.Equals(name))
+                {
+                    found.Add(member);
+                }
+            }
+            switch (found.Count)
+            {
+                case 0: return null;
+                case 1: return found.First();
+                default: throw new AmbiguousMatchException(name);
+            }
+        }
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name)
+        {
+            var methods = declaringType.GetRuntimeMethods();
+            foreach (MethodInfo method in methods)
+            {
+                if (method.Name == name)
+                {
+                    return method;
+                }
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name)
+        {
+            return GetInstanceMethod(declaringType.AsType(), name); ;
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name)
+        {
+            var methods = declaringType.GetRuntimeMethods();
+            foreach (MethodInfo method in methods)
+            {
+                if (method.Name == name)
+                {
+                    return method;
+                }
+            }
+            return null;
+        }
+
+        internal static MethodInfo GetStaticMethod(TypeInfo declaringType, string name)
+        {
+            return GetStaticMethod(declaringType.AsType(), name);
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes)
+        {
+            var methods = declaringType.GetRuntimeMethods();
+            foreach (MethodInfo method in methods)
+            {
+                if (method.Name == name &&
+                    IsMatch(method.GetParameters(), parameterTypes))
+                {
+                    return method;
+                }
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] parameterTypes)
+        {
+            var methods = declaringType.GetRuntimeMethods();
+            foreach (MethodInfo method in methods)
+            {
+                if (method.Name == name &&
+                    IsMatch(method.GetParameters(), parameterTypes))
+                {
+                    return method;
+                }
+            }
+            return null;
+        }
+        internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name, Type[] types)
+        {
+            return GetInstanceMethod(declaringType.AsType(), name, types);
+        }
+#else
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name)
+        {
+            return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name)
+        {
+            return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
+        }
+        internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes)
+        {
+#if PORTABLE
+            foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method;
+            }
+            return null;
+#else
+            return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
+#endif
+        }
+        internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] types)
+        {
+            if (types == null) types = EmptyTypes;
+#if PORTABLE || COREFX
+            MethodInfo method = declaringType.GetMethod(name, types);
+            if (method != null && method.IsStatic) method = null;
+            return method;
+#else
+            return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
+                null, types, null);
+#endif
+        }
+#endif
+
+        internal static bool IsSubclassOf(Type type, Type baseClass)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().IsSubclassOf(baseClass);
+#else
+            return type.IsSubclassOf(baseClass);
+#endif
+        }
+
+        public readonly static Type[] EmptyTypes =
+#if PORTABLE || CF2 || CF35 || PROFILE259
+            new Type[0];
+#else
+            Type.EmptyTypes;
+#endif
+
+#if COREFX || PROFILE259
+        private static readonly Type[] knownTypes = new Type[] {
+                typeof(bool), typeof(char), typeof(sbyte), typeof(byte),
+                typeof(short), typeof(ushort), typeof(int), typeof(uint),
+                typeof(long), typeof(ulong), typeof(float), typeof(double),
+                typeof(decimal), typeof(string),
+                typeof(DateTime), typeof(TimeSpan), typeof(Guid), typeof(Uri),
+                typeof(byte[]), typeof(Type)};
+        private static readonly ProtoTypeCode[] knownCodes = new ProtoTypeCode[] {
+            ProtoTypeCode.Boolean, ProtoTypeCode.Char, ProtoTypeCode.SByte, ProtoTypeCode.Byte,
+            ProtoTypeCode.Int16, ProtoTypeCode.UInt16, ProtoTypeCode.Int32, ProtoTypeCode.UInt32,
+            ProtoTypeCode.Int64, ProtoTypeCode.UInt64, ProtoTypeCode.Single, ProtoTypeCode.Double,
+            ProtoTypeCode.Decimal, ProtoTypeCode.String,
+            ProtoTypeCode.DateTime, ProtoTypeCode.TimeSpan, ProtoTypeCode.Guid, ProtoTypeCode.Uri,
+            ProtoTypeCode.ByteArray, ProtoTypeCode.Type
+        };
+
+#endif
+
+        public static ProtoTypeCode GetTypeCode(Type type)
+        {
+#if COREFX || PROFILE259
+            if (IsEnum(type))
+            {
+                type = Enum.GetUnderlyingType(type);
+            }
+            int idx = Array.IndexOf<Type>(knownTypes, type);
+            if (idx >= 0) return knownCodes[idx];
+            return type == null ? ProtoTypeCode.Empty : ProtoTypeCode.Unknown;
+#else
+            TypeCode code = Type.GetTypeCode(type);
+            switch (code)
+            {
+                case TypeCode.Empty:
+                case TypeCode.Boolean:
+                case TypeCode.Char:
+                case TypeCode.SByte:
+                case TypeCode.Byte:
+                case TypeCode.Int16:
+                case TypeCode.UInt16:
+                case TypeCode.Int32:
+                case TypeCode.UInt32:
+                case TypeCode.Int64:
+                case TypeCode.UInt64:
+                case TypeCode.Single:
+                case TypeCode.Double:
+                case TypeCode.Decimal:
+                case TypeCode.DateTime:
+                case TypeCode.String:
+                    return (ProtoTypeCode)code;
+            }
+            if (type == typeof(TimeSpan)) return ProtoTypeCode.TimeSpan;
+            if (type == typeof(Guid)) return ProtoTypeCode.Guid;
+            if (type == typeof(Uri)) return ProtoTypeCode.Uri;
+#if PORTABLE
+            // In PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri), so match on the full name instead
+            if (type.FullName == typeof(Uri).FullName) return ProtoTypeCode.Uri;
+#endif
+            if (type == typeof(byte[])) return ProtoTypeCode.ByteArray;
+            if (type == typeof(Type)) return ProtoTypeCode.Type;
+
+            return ProtoTypeCode.Unknown;
+#endif
+        }
+
+        internal static Type GetUnderlyingType(Type type)
+        {
+            return Nullable.GetUnderlyingType(type);
+        }
+
+        internal static bool IsValueType(Type type)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().IsValueType;
+#else
+            return type.IsValueType;
+#endif
+        }
+        internal static bool IsSealed(Type type)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().IsSealed;
+#else
+            return type.IsSealed;
+#endif
+        }
+        internal static bool IsClass(Type type)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().IsClass;
+#else
+            return type.IsClass;
+#endif
+        }
+
+        internal static bool IsEnum(Type type)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().IsEnum;
+#else
+            return type.IsEnum;
+#endif
+        }
+
+        internal static MethodInfo GetGetMethod(PropertyInfo property, bool nonPublic, bool allowInternal)
+        {
+            if (property == null) return null;
+#if COREFX || PROFILE259
+            MethodInfo method = property.GetMethod;
+            if (!nonPublic && method != null && !method.IsPublic) method = null;
+            return method;
+#else
+            MethodInfo method = property.GetGetMethod(nonPublic);
+            if (method == null && !nonPublic && allowInternal)
+            { // could be "internal" or "protected internal"; look for a non-public, then back-check
+                method = property.GetGetMethod(true);
+                if (method == null && !(method.IsAssembly || method.IsFamilyOrAssembly))
+                {
+                    method = null;
+                }
+            }
+            return method;
+#endif
+        }
+        internal static MethodInfo GetSetMethod(PropertyInfo property, bool nonPublic, bool allowInternal)
+        {
+            if (property == null) return null;
+#if COREFX || PROFILE259
+            MethodInfo method = property.SetMethod;
+            if (!nonPublic && method != null && !method.IsPublic) method = null;
+            return method;
+#else
+            MethodInfo method = property.GetSetMethod(nonPublic);
+            if (method == null && !nonPublic && allowInternal)
+            { // could be "internal" or "protected internal"; look for a non-public, then back-check
+                method = property.GetGetMethod(true);
+                if (method == null && !(method.IsAssembly || method.IsFamilyOrAssembly))
+                {
+                    method = null;
+                }
+            }
+            return method;
+#endif
+        }
+
+#if COREFX || PORTABLE || PROFILE259
+        private static bool IsMatch(ParameterInfo[] parameters, Type[] parameterTypes)
+        {
+            if (parameterTypes == null) parameterTypes = EmptyTypes;
+            if (parameters.Length != parameterTypes.Length) return false;
+            for (int i = 0; i < parameters.Length; i++)
+            {
+                if (parameters[i].ParameterType != parameterTypes[i]) return false;
+            }
+            return true;
+        }
+#endif
+#if COREFX || PROFILE259
+        internal static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes, bool nonPublic)
+        {
+            return GetConstructor(type.GetTypeInfo(), parameterTypes, nonPublic);
+        }
+        internal static ConstructorInfo GetConstructor(TypeInfo type, Type[] parameterTypes, bool nonPublic)
+        {
+            return GetConstructors(type, nonPublic).SingleOrDefault(ctor => IsMatch(ctor.GetParameters(), parameterTypes));
+        }
+        internal static ConstructorInfo[] GetConstructors(TypeInfo typeInfo, bool nonPublic)
+        {
+            return typeInfo.DeclaredConstructors.Where(c => !c.IsStatic && ((!nonPublic && c.IsPublic) || nonPublic)).ToArray();
+        }
+        internal static PropertyInfo GetProperty(Type type, string name, bool nonPublic)
+        {
+            return GetProperty(type.GetTypeInfo(), name, nonPublic);
+        }
+        internal static PropertyInfo GetProperty(TypeInfo type, string name, bool nonPublic)
+        {
+            return type.GetDeclaredProperty(name);
+        }
+#else
+
+        internal static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes, bool nonPublic)
+        {
+#if PORTABLE || COREFX
+            // pretty sure this will only ever return public, but...
+            ConstructorInfo ctor = type.GetConstructor(parameterTypes);
+            return (ctor != null && (nonPublic || ctor.IsPublic)) ? ctor : null;
+#else
+            return type.GetConstructor(
+                nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
+                          : BindingFlags.Instance | BindingFlags.Public,
+                    null, parameterTypes, null);
+#endif
+
+        }
+        internal static ConstructorInfo[] GetConstructors(Type type, bool nonPublic)
+        {
+            return type.GetConstructors(
+                nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
+                          : BindingFlags.Instance | BindingFlags.Public);
+        }
+        internal static PropertyInfo GetProperty(Type type, string name, bool nonPublic)
+        {
+            return type.GetProperty(name,
+                nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
+                          : BindingFlags.Instance | BindingFlags.Public);
+        }
+#endif
+
+
+        internal static object ParseEnum(Type type, string value)
+        {
+            return Enum.Parse(type, value, true);
+        }
+
+
+        internal static MemberInfo[] GetInstanceFieldsAndProperties(Type type, bool publicOnly)
+        {
+#if PROFILE259
+            var members = new List<MemberInfo>();
+            foreach (FieldInfo field in type.GetRuntimeFields())
+            {
+                if (field.IsStatic) continue;
+                if (field.IsPublic || !publicOnly) members.Add(field);
+            }
+            foreach (PropertyInfo prop in type.GetRuntimeProperties())
+            {
+                MethodInfo getter = Helpers.GetGetMethod(prop, true, true);
+                if (getter == null || getter.IsStatic) continue;
+                if (getter.IsPublic || !publicOnly) members.Add(prop);
+            }
+            return members.ToArray();
+#else
+            BindingFlags flags = publicOnly ? BindingFlags.Public | BindingFlags.Instance : BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
+            PropertyInfo[] props = type.GetProperties(flags);
+            FieldInfo[] fields = type.GetFields(flags);
+            MemberInfo[] members = new MemberInfo[fields.Length + props.Length];
+            props.CopyTo(members, 0);
+            fields.CopyTo(members, props.Length);
+            return members;
+#endif
+        }
+
+        internal static Type GetMemberType(MemberInfo member)
+        {
+#if PORTABLE || COREFX || PROFILE259
+            if (member is PropertyInfo prop) return prop.PropertyType;
+            FieldInfo fld = member as FieldInfo;
+            return fld?.FieldType;
+#else
+            switch (member.MemberType)
+            {
+                case MemberTypes.Field: return ((FieldInfo)member).FieldType;
+                case MemberTypes.Property: return ((PropertyInfo)member).PropertyType;
+                default: return null;
+            }
+#endif
+        }
+
+        internal static bool IsAssignableFrom(Type target, Type type)
+        {
+#if PROFILE259
+            return target.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo());
+#else
+            return target.IsAssignableFrom(type);
+#endif
+        }
+        internal static Assembly GetAssembly(Type type)
+        {
+#if COREFX || PROFILE259
+            return type.GetTypeInfo().Assembly;
+#else
+            return type.Assembly;
+#endif
+        }
+        internal static byte[] GetBuffer(MemoryStream ms)
+        {
+#if COREFX
+            if(!ms.TryGetBuffer(out var segment))
+            {
+                throw new InvalidOperationException("Unable to obtain underlying MemoryStream buffer");
+            } else if(segment.Offset != 0)
+            {
+                throw new InvalidOperationException("Underlying MemoryStream buffer was not zero-offset");
+            } else
+            {
+                return segment.Array;
+            }
+#elif PORTABLE || PROFILE259
+            return ms.ToArray();
+#else
+            return ms.GetBuffer();
+#endif
+        }
+    }
+    /// <summary>
+    /// Intended to be a direct map to regular TypeCode, but:
+    /// - with missing types
+    /// - existing on WinRT
+    /// </summary>
+    internal enum ProtoTypeCode
+    {
+        Empty = 0,
+        Unknown = 1, // maps to TypeCode.Object
+        Boolean = 3,
+        Char = 4,
+        SByte = 5,
+        Byte = 6,
+        Int16 = 7,
+        UInt16 = 8,
+        Int32 = 9,
+        UInt32 = 10,
+        Int64 = 11,
+        UInt64 = 12,
+        Single = 13,
+        Double = 14,
+        Decimal = 15,
+        DateTime = 16,
+        String = 18,
+
+        // additions
+        TimeSpan = 100,
+        ByteArray = 101,
+        Guid = 102,
+        Uri = 103,
+        Type = 104
+    }
+}

+ 23 - 0
Server/ThirdParty/protobuf-net/IExtensible.cs

@@ -0,0 +1,23 @@
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates that the implementing type has support for protocol-buffer
+    /// <see cref="IExtension">extensions</see>.
+    /// </summary>
+    /// <remarks>Can be implemented by deriving from Extensible.</remarks>
+    public interface IExtensible
+    {
+        /// <summary>
+        /// Retrieves the <see cref="IExtension">extension</see> object for the current
+        /// instance, optionally creating it if it does not already exist.
+        /// </summary>
+        /// <param name="createIfMissing">Should a new extension object be
+        /// created if it does not already exist?</param>
+        /// <returns>The extension object if it exists (or was created), or null
+        /// if the extension object does not exist or is not available.</returns>
+        /// <remarks>The <c>createIfMissing</c> argument is false during serialization,
+        /// and true during deserialization upon encountering unexpected fields.</remarks>
+        IExtension GetExtensionObject(bool createIfMissing);
+    }
+}

+ 58 - 0
Server/ThirdParty/protobuf-net/IExtension.cs

@@ -0,0 +1,58 @@
+
+using System.IO;
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Provides addition capability for supporting unexpected fields during
+    /// protocol-buffer serialization/deserialization. This allows for loss-less
+    /// round-trip/merge, even when the data is not fully understood.
+    /// </summary>
+    public interface IExtension
+    {
+        /// <summary>
+        /// Requests a stream into which any unexpected fields can be persisted.
+        /// </summary>
+        /// <returns>A new stream suitable for storing data.</returns>
+        Stream BeginAppend();
+
+        /// <summary>
+        /// Indicates that all unexpected fields have now been stored. The
+        /// implementing class is responsible for closing the stream. If
+        /// "commit" is not true the data may be discarded.
+        /// </summary>
+        /// <param name="stream">The stream originally obtained by BeginAppend.</param>
+        /// <param name="commit">True if the append operation completed successfully.</param>
+        void EndAppend(Stream stream, bool commit);
+
+        /// <summary>
+        /// Requests a stream of the unexpected fields previously stored.
+        /// </summary>
+        /// <returns>A prepared stream of the unexpected fields.</returns>
+        Stream BeginQuery();
+
+        /// <summary>
+        /// Indicates that all unexpected fields have now been read. The
+        /// implementing class is responsible for closing the stream.
+        /// </summary>
+        /// <param name="stream">The stream originally obtained by BeginQuery.</param>
+        void EndQuery(Stream stream);
+
+        /// <summary>
+        /// Requests the length of the raw binary stream; this is used
+        /// when serializing sub-entities to indicate the expected size.
+        /// </summary>
+        /// <returns>The length of the binary stream representing unexpected data.</returns>
+        int GetLength();
+    }
+
+    /// <summary>
+    /// Provides the ability to remove all existing extension data
+    /// </summary>
+    public interface IExtensionResettable : IExtension
+    {
+        /// <summary>
+        /// Remove all existing extension data
+        /// </summary>
+        void Reset();
+    }
+}

+ 13 - 0
Server/ThirdParty/protobuf-net/IProtoInputT.cs

@@ -0,0 +1,13 @@
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Represents the ability to deserialize values from an input of type <typeparamref name="TInput"/>
+    /// </summary>
+    public interface IProtoInput<TInput>
+    {
+        /// <summary>
+        /// Deserialize a value from the input
+        /// </summary>
+        T Deserialize<T>(TInput source, T value = default, object userState = null);
+    }
+}

+ 55 - 0
Server/ThirdParty/protobuf-net/IProtoOutputT.cs

@@ -0,0 +1,55 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Represents the ability to serialize values to an output of type <typeparamref name="TOutput"/>
+    /// </summary>
+    public interface IProtoOutput<TOutput>
+    {
+        /// <summary>
+        /// Serialize the provided value
+        /// </summary>
+        void Serialize<T>(TOutput destination, T value, object userState = null);
+    }
+
+    /// <summary>
+    /// Represents the ability to serialize values to an output of type <typeparamref name="TOutput"/>
+    /// with pre-computation of the length
+    /// </summary>
+    public interface IMeasuredProtoOutput<TOutput> : IProtoOutput<TOutput>
+    {
+        /// <summary>
+        /// Measure the length of a value in advance of serialization
+        /// </summary>
+        MeasureState<T> Measure<T>(T value, object userState = null);
+
+        /// <summary>
+        /// Serialize the previously measured value
+        /// </summary>
+        void Serialize<T>(MeasureState<T> measured, TOutput destination);
+    }
+
+    /// <summary>
+    /// Represents the outcome of computing the length of an object; since this may have required computing lengths
+    /// for multiple objects, some metadata is retained so that a subsequent serialize operation using
+    /// this instance can re-use the previously calculated lengths. If the object state changes between the
+    /// measure and serialize operations, the behavior is undefined.
+    /// </summary>
+    public struct MeasureState<T> : IDisposable
+        // note: 2.4.* does not actually implement this API;
+        // it only advertises it for 3.* capability/feature-testing, i.e.
+        // callers can check whether a model implements
+        // IMeasuredProtoOutput<Foo>, and *work from that*
+    {
+        /// <summary>
+        /// Releases all resources associated with this value
+        /// </summary>
+        public void Dispose() => throw new NotImplementedException();
+
+        /// <summary>
+        /// Gets the calculated length of this serialize operation, in bytes
+        /// </summary>
+        public long Length => throw new NotImplementedException();
+    }
+}

+ 29 - 0
Server/ThirdParty/protobuf-net/ImplicitFields.cs

@@ -0,0 +1,29 @@
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Specifies the method used to infer field tags for members of the type
+    /// under consideration. Tags are deduced using the invariant alphabetic
+    /// sequence of the members' names; this makes implicit field tags very brittle,
+    /// and susceptible to changes such as field names (normally an isolated
+    /// change).
+    /// </summary>
+    public enum ImplicitFields
+    {
+        /// <summary>
+        /// No members are serialized implicitly; all members require a suitable
+        /// attribute such as [ProtoMember]. This is the recmomended mode for
+        /// most scenarios.
+        /// </summary>
+        None = 0,
+        /// <summary>
+        /// Public properties and fields are eligible for implicit serialization;
+        /// this treats the public API as a contract. Ordering beings from ImplicitFirstTag.
+        /// </summary>
+        AllPublic = 1,
+        /// <summary>
+        /// Public and non-public fields are eligible for implicit serialization;
+        /// this acts as a state/implementation serializer. Ordering beings from ImplicitFirstTag.
+        /// </summary>
+        AllFields = 2
+    }
+}

+ 44 - 0
Server/ThirdParty/protobuf-net/KeyValuePairProxy.cs

@@ -0,0 +1,44 @@
+//using System.Collections.Generic;
+
+//namespace ProtoBuf
+//{
+//    /// <summary>
+//    /// Mutable version of the common key/value pair struct; used during serialization. This type is intended for internal use only and should not
+//    /// be used by calling code; it is required to be public for implementation reasons.
+//    /// </summary>
+//    [ProtoContract]
+//    public struct KeyValuePairSurrogate<TKey,TValue>
+//    {
+//        private TKey key;
+//        private TValue value;
+//        /// <summary>
+//        /// The key of the pair.
+//        /// </summary>
+//        [ProtoMember(1, IsRequired = true)]
+//        public TKey Key { get { return key; } set { key = value; } }
+//        /// <summary>
+//        /// The value of the pair.
+//        /// </summary>
+//        [ProtoMember(2)]
+//        public TValue Value{ get { return value; } set { this.value = value; } }
+//        private KeyValuePairSurrogate(TKey key, TValue value)
+//        {
+//            this.key = key;
+//            this.value = value;
+//        }
+//        /// <summary>
+//        /// Convert a surrogate instance to a standard pair instance.
+//        /// </summary>
+//        public static implicit operator KeyValuePair<TKey, TValue> (KeyValuePairSurrogate<TKey, TValue> value)
+//        {
+//            return new KeyValuePair<TKey,TValue>(value.key, value.value);
+//        }
+//        /// <summary>
+//        /// Convert a standard pair instance to a surrogate instance.
+//        /// </summary>
+//        public static implicit operator KeyValuePairSurrogate<TKey, TValue>(KeyValuePair<TKey, TValue> value)
+//        {
+//            return new KeyValuePairSurrogate<TKey, TValue>(value.Key, value.Value);
+//        }
+//    }
+//}

+ 108 - 0
Server/ThirdParty/protobuf-net/Meta/AttributeMap.cs

@@ -0,0 +1,108 @@
+#if !NO_RUNTIME
+using System;
+using System.Reflection;
+
+namespace ProtoBuf.Meta
+{
+    internal abstract class AttributeMap
+    {
+#if DEBUG
+        [Obsolete("Please use AttributeType instead")]
+        new public Type GetType() => AttributeType;
+#endif
+        public override string ToString() => AttributeType?.FullName ?? "";
+        public abstract bool TryGet(string key, bool publicOnly, out object value);
+        public bool TryGet(string key, out object value)
+        {
+            return TryGet(key, true, out value);
+        }
+        public abstract Type AttributeType { get; }
+        public static AttributeMap[] Create(TypeModel model, Type type, bool inherit)
+        {
+
+#if COREFX || PROFILE259
+			Attribute[] all = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.OfType<Attribute>(type.GetTypeInfo().GetCustomAttributes(inherit)));
+#else
+            object[] all = type.GetCustomAttributes(inherit);
+#endif
+            AttributeMap[] result = new AttributeMap[all.Length];
+            for(int i = 0 ; i < all.Length ; i++)
+            {
+                result[i] = new ReflectionAttributeMap((Attribute)all[i]);
+            }
+            return result;
+        }
+
+        public static AttributeMap[] Create(TypeModel model, MemberInfo member, bool inherit)
+        {
+
+#if COREFX || PROFILE259
+			Attribute[] all = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.OfType<Attribute>(member.GetCustomAttributes(inherit)));
+#else
+            object[] all = member.GetCustomAttributes(inherit);
+#endif
+            AttributeMap[] result = new AttributeMap[all.Length];
+            for(int i = 0 ; i < all.Length ; i++)
+            {
+                result[i] = new ReflectionAttributeMap((Attribute)all[i]);
+            }
+            return result;
+        }
+        public static AttributeMap[] Create(TypeModel model, Assembly assembly)
+        {
+#if COREFX || PROFILE259
+			Attribute[] all = System.Linq.Enumerable.ToArray(assembly.GetCustomAttributes());
+#else
+            const bool inherit = false;
+            object[] all = assembly.GetCustomAttributes(inherit);
+#endif
+            AttributeMap[] result = new AttributeMap[all.Length];
+            for(int i = 0 ; i < all.Length ; i++)
+            {
+                result[i] = new ReflectionAttributeMap((Attribute)all[i]);
+            }
+            return result;
+
+        }
+
+        public abstract object Target { get; }
+
+        private sealed class ReflectionAttributeMap : AttributeMap
+        {
+            private readonly Attribute attribute;
+
+            public ReflectionAttributeMap(Attribute attribute)
+            {
+                this.attribute = attribute;
+            }
+
+            public override object Target => attribute;
+
+            public override Type AttributeType => attribute.GetType();
+
+            public override bool TryGet(string key, bool publicOnly, out object value)
+            {
+                MemberInfo[] members = Helpers.GetInstanceFieldsAndProperties(attribute.GetType(), publicOnly);
+                foreach (MemberInfo member in members)
+                {
+                    if (string.Equals(member.Name, key, StringComparison.OrdinalIgnoreCase))
+                    {
+                        if (member is PropertyInfo prop) {
+                            value = prop.GetValue(attribute, null);
+                            return true;
+                        }
+                        if (member is FieldInfo field) {
+                            value = field.GetValue(attribute);
+                            return true;
+                        }
+
+                        throw new NotSupportedException(member.GetType().Name);
+                    }
+                }
+                value = null;
+                return false;
+            }
+        }
+    }
+}
+#endif

+ 267 - 0
Server/ThirdParty/protobuf-net/Meta/BasicList.cs

@@ -0,0 +1,267 @@
+using System;
+using System.Collections;
+
+namespace ProtoBuf.Meta
+{
+    internal sealed class MutableList : BasicList
+    {
+        /*  Like BasicList, but allows existing values to be changed
+         */
+        public new object this[int index]
+        {
+            get { return head[index]; }
+            set { head[index] = value; }
+        }
+        public void RemoveLast()
+        {
+            head.RemoveLastWithMutate();
+        }
+
+        public void Clear()
+        {
+            head.Clear();
+        }
+    }
+
+    internal class BasicList : IEnumerable
+    {
+        /* Requirements:
+         *   - Fast access by index
+         *   - Immutable in the tail, so a node can be read (iterated) without locking
+         *   - Lock-free tail handling must match the memory mode; struct for Node
+         *     wouldn't work as "read" would not be atomic
+         *   - Only operation required is append, but this shouldn't go out of its
+         *     way to be inefficient
+         *   - Assume that the caller is handling thread-safety (to co-ordinate with
+         *     other code); no attempt to be thread-safe
+         *   - Assume that the data is private; internal data structure is allowed to
+         *     be mutable (i.e. array is fine as long as we don't screw it up)
+         */
+        private static readonly Node nil = new Node(null, 0);
+
+        public void CopyTo(Array array, int offset)
+        {
+            head.CopyTo(array, offset);
+        }
+
+        protected Node head = nil;
+
+        public int Add(object value)
+        {
+            return (head = head.Append(value)).Length - 1;
+        }
+
+        public object this[int index] => head[index];
+
+        //public object TryGet(int index)
+        //{
+        //    return head.TryGet(index);
+        //}
+
+        public void Trim() { head = head.Trim(); }
+
+        public int Count => head.Length;
+
+        IEnumerator IEnumerable.GetEnumerator() => new NodeEnumerator(head);
+
+        public NodeEnumerator GetEnumerator() => new NodeEnumerator(head);
+
+        public struct NodeEnumerator : IEnumerator
+        {
+            private int position;
+            private readonly Node node;
+            internal NodeEnumerator(Node node)
+            {
+                this.position = -1;
+                this.node = node;
+            }
+            void IEnumerator.Reset() { position = -1; }
+            public object Current { get { return node[position]; } }
+            public bool MoveNext()
+            {
+                int len = node.Length;
+                return (position <= len) && (++position < len);
+            }
+        }
+
+        internal sealed class Node
+        {
+            public object this[int index]
+            {
+                get
+                {
+                    if (index >= 0 && index < length)
+                    {
+                        return data[index];
+                    }
+                    throw new ArgumentOutOfRangeException(nameof(index));
+                }
+                set
+                {
+                    if (index >= 0 && index < length)
+                    {
+                        data[index] = value;
+                    }
+                    else
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(index));
+                    }
+                }
+            }
+            //public object TryGet(int index)
+            //{
+            //    return (index >= 0 && index < length) ? data[index] : null;
+            //}
+            private readonly object[] data;
+
+            private int length;
+            public int Length => length;
+
+            internal Node(object[] data, int length)
+            {
+                Helpers.DebugAssert((data == null && length == 0) ||
+                    (data != null && length > 0 && length <= data.Length));
+                this.data = data;
+
+                this.length = length;
+            }
+
+            public void RemoveLastWithMutate()
+            {
+                if (length == 0) throw new InvalidOperationException();
+                length -= 1;
+            }
+
+            public Node Append(object value)
+            {
+                object[] newData;
+                int newLength = length + 1;
+                if (data == null)
+                {
+                    newData = new object[10];
+                }
+                else if (length == data.Length)
+                {
+                    newData = new object[data.Length * 2];
+                    Array.Copy(data, newData, length);
+                }
+                else
+                {
+                    newData = data;
+                }
+                newData[length] = value;
+                return new Node(newData, newLength);
+            }
+
+            public Node Trim()
+            {
+                if (length == 0 || length == data.Length) return this;
+                object[] newData = new object[length];
+                Array.Copy(data, newData, length);
+                return new Node(newData, length);
+            }
+
+            internal int IndexOfString(string value)
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    if ((string)value == (string)data[i]) return i;
+                }
+                return -1;
+            }
+
+            internal int IndexOfReference(object instance)
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    if ((object)instance == (object)data[i]) return i;
+                } // ^^^ (object) above should be preserved, even if this was typed; needs
+                  // to be a reference check
+                return -1;
+            }
+
+            internal int IndexOf(MatchPredicate predicate, object ctx)
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    if (predicate(data[i], ctx)) return i;
+                }
+                return -1;
+            }
+
+            internal void CopyTo(Array array, int offset)
+            {
+                if (length > 0)
+                {
+                    Array.Copy(data, 0, array, offset, length);
+                }
+            }
+
+            internal void Clear()
+            {
+                if (data != null)
+                {
+                    Array.Clear(data, 0, data.Length);
+                }
+                length = 0;
+            }
+        }
+
+        internal int IndexOf(MatchPredicate predicate, object ctx)
+        {
+            return head.IndexOf(predicate, ctx);
+        }
+
+        internal int IndexOfString(string value)
+        {
+            return head.IndexOfString(value);
+        }
+
+        internal int IndexOfReference(object instance)
+        {
+            return head.IndexOfReference(instance);
+        }
+
+        internal delegate bool MatchPredicate(object value, object ctx);
+
+        internal bool Contains(object value)
+        {
+            foreach (object obj in this)
+            {
+                if (object.Equals(obj, value)) return true;
+            }
+            return false;
+        }
+
+        internal sealed class Group
+        {
+            public readonly int First;
+            public readonly BasicList Items;
+            public Group(int first)
+            {
+                this.First = first;
+                this.Items = new BasicList();
+            }
+        }
+
+        internal static BasicList GetContiguousGroups(int[] keys, object[] values)
+        {
+            if (keys == null) throw new ArgumentNullException(nameof(keys));
+            if (values == null) throw new ArgumentNullException(nameof(values));
+            if (values.Length < keys.Length) throw new ArgumentException("Not all keys are covered by values", nameof(values));
+            BasicList outer = new BasicList();
+            Group group = null;
+            for (int i = 0; i < keys.Length; i++)
+            {
+                if (i == 0 || keys[i] != keys[i - 1]) { group = null; }
+                if (group == null)
+                {
+                    group = new Group(keys[i]);
+                    outer.Add(group);
+                }
+                group.Items.Add(values[i]);
+            }
+            return outer;
+        }
+    }
+}

+ 110 - 0
Server/ThirdParty/protobuf-net/Meta/CallbackSet.cs

@@ -0,0 +1,110 @@
+#if !NO_RUNTIME
+using System;
+using System.Reflection;
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Represents the set of serialization callbacks to be used when serializing/deserializing a type.
+    /// </summary>
+    public class CallbackSet
+    {
+        private readonly MetaType metaType;
+        internal CallbackSet(MetaType metaType)
+        {
+            this.metaType = metaType ?? throw new ArgumentNullException(nameof(metaType));
+        }
+
+        internal MethodInfo this[TypeModel.CallbackType callbackType]
+        {
+            get
+            {
+                switch (callbackType)
+                {
+                    case TypeModel.CallbackType.BeforeSerialize: return beforeSerialize;
+                    case TypeModel.CallbackType.AfterSerialize: return afterSerialize;
+                    case TypeModel.CallbackType.BeforeDeserialize: return beforeDeserialize;
+                    case TypeModel.CallbackType.AfterDeserialize: return afterDeserialize;
+                    default: throw new ArgumentException("Callback type not supported: " + callbackType.ToString(), "callbackType");
+                }
+            }
+        }
+
+        internal static bool CheckCallbackParameters(TypeModel model, MethodInfo method)
+        {
+            ParameterInfo[] args = method.GetParameters();
+            for (int i = 0; i < args.Length; i++)
+            {
+                Type paramType = args[i].ParameterType;
+                if (paramType == model.MapType(typeof(SerializationContext))) { }
+                else if (paramType == model.MapType(typeof(System.Type))) { }
+#if PLAT_BINARYFORMATTER
+                else if (paramType == model.MapType(typeof(System.Runtime.Serialization.StreamingContext))) { }
+#endif
+                else return false;
+            }
+            return true;
+        }
+
+        private MethodInfo SanityCheckCallback(TypeModel model, MethodInfo callback)
+        {
+            metaType.ThrowIfFrozen();
+            if (callback == null) return callback; // fine
+            if (callback.IsStatic) throw new ArgumentException("Callbacks cannot be static", nameof(callback));
+            if (callback.ReturnType != model.MapType(typeof(void))
+                || !CheckCallbackParameters(model, callback))
+            {
+                throw CreateInvalidCallbackSignature(callback);
+            }
+            return callback;
+        }
+
+        internal static Exception CreateInvalidCallbackSignature(MethodInfo method)
+        {
+            return new NotSupportedException("Invalid callback signature in " + method.DeclaringType.FullName + "." + method.Name);
+        }
+
+        private MethodInfo beforeSerialize, afterSerialize, beforeDeserialize, afterDeserialize;
+
+        /// <summary>Called before serializing an instance</summary>
+        public MethodInfo BeforeSerialize
+        {
+            get { return beforeSerialize; }
+            set { beforeSerialize = SanityCheckCallback(metaType.Model, value); }
+        }
+
+        /// <summary>Called before deserializing an instance</summary>
+        public MethodInfo BeforeDeserialize
+        {
+            get { return beforeDeserialize; }
+            set { beforeDeserialize = SanityCheckCallback(metaType.Model, value); }
+        }
+
+        /// <summary>Called after serializing an instance</summary>
+        public MethodInfo AfterSerialize
+        {
+            get { return afterSerialize; }
+            set { afterSerialize = SanityCheckCallback(metaType.Model, value); }
+        }
+
+        /// <summary>Called after deserializing an instance</summary>
+        public MethodInfo AfterDeserialize
+        {
+            get { return afterDeserialize; }
+            set { afterDeserialize = SanityCheckCallback(metaType.Model, value); }
+        }
+
+        /// <summary>
+        /// True if any callback is set, else False
+        /// </summary>
+        public bool NonTrivial
+        {
+            get
+            {
+                return beforeSerialize != null || beforeDeserialize != null
+                    || afterSerialize != null || afterDeserialize != null;
+            }
+        }
+    }
+}
+#endif

+ 2160 - 0
Server/ThirdParty/protobuf-net/Meta/MetaType.cs

@@ -0,0 +1,2160 @@
+#if !NO_RUNTIME
+using System;
+using System.Collections;
+using System.Text;
+using ProtoBuf.Serializers;
+using System.Reflection;
+using System.Collections.Generic;
+
+#if PROFILE259
+using System.Linq;
+#endif
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Represents a type at runtime for use with protobuf, allowing the field mappings (etc) to be defined
+    /// </summary>
+    public class MetaType : ISerializerProxy
+    {
+        internal sealed class Comparer : IComparer, IComparer<MetaType>
+        {
+            public static readonly Comparer Default = new Comparer();
+            public int Compare(object x, object y)
+            {
+                return Compare(x as MetaType, y as MetaType);
+            }
+            public int Compare(MetaType x, MetaType y)
+            {
+                if (ReferenceEquals(x, y)) return 0;
+                if (x == null) return -1;
+                if (y == null) return 1;
+
+                return string.Compare(x.GetSchemaTypeName(), y.GetSchemaTypeName(), StringComparison.Ordinal);
+            }
+        }
+        /// <summary>
+        /// Get the name of the type being represented
+        /// </summary>
+        public override string ToString()
+        {
+            return type.ToString();
+        }
+
+        IProtoSerializer ISerializerProxy.Serializer => Serializer;
+        private MetaType baseType;
+
+        /// <summary>
+        /// Gets the base-type for this type
+        /// </summary>
+        public MetaType BaseType => baseType;
+
+        internal TypeModel Model => model;
+
+        /// <summary>
+        /// When used to compile a model, should public serialization/deserialzation methods
+        /// be included for this type?
+        /// </summary>
+        public bool IncludeSerializerMethod
+        {   // negated to minimize common-case / initializer
+            get { return !HasFlag(OPTIONS_PrivateOnApi); }
+            set { SetFlag(OPTIONS_PrivateOnApi, !value, true); }
+        }
+
+        /// <summary>
+        /// Should this type be treated as a reference by default?
+        /// </summary>
+        public bool AsReferenceDefault
+        {
+            get { return HasFlag(OPTIONS_AsReferenceDefault); }
+            set { SetFlag(OPTIONS_AsReferenceDefault, value, true); }
+        }
+
+        private BasicList subTypes;
+        private bool IsValidSubType(Type subType)
+        {
+#if COREFX || PROFILE259
+            return typeInfo.IsAssignableFrom(subType.GetTypeInfo());
+#else
+            return type.IsAssignableFrom(subType);
+#endif
+        }
+        /// <summary>
+        /// Adds a known sub-type to the inheritance model
+        /// </summary>
+        public MetaType AddSubType(int fieldNumber, Type derivedType)
+        {
+            return AddSubType(fieldNumber, derivedType, DataFormat.Default);
+        }
+        /// <summary>
+        /// Adds a known sub-type to the inheritance model
+        /// </summary>
+        public MetaType AddSubType(int fieldNumber, Type derivedType, DataFormat dataFormat)
+        {
+            if (derivedType == null) throw new ArgumentNullException("derivedType");
+            if (fieldNumber < 1) throw new ArgumentOutOfRangeException("fieldNumber");
+#if COREFX || COREFX || PROFILE259
+			if (!(typeInfo.IsClass || typeInfo.IsInterface) || typeInfo.IsSealed) {
+#else
+            if (!(type.IsClass || type.IsInterface) || type.IsSealed)
+            {
+#endif
+                throw new InvalidOperationException("Sub-types can only be added to non-sealed classes");
+            }
+            if (!IsValidSubType(derivedType))
+            {
+                throw new ArgumentException(derivedType.Name + " is not a valid sub-type of " + type.Name, "derivedType");
+            }
+            MetaType derivedMeta = model[derivedType];
+            ThrowIfFrozen();
+            derivedMeta.ThrowIfFrozen();
+            SubType subType = new SubType(fieldNumber, derivedMeta, dataFormat);
+            ThrowIfFrozen();
+
+            derivedMeta.SetBaseType(this); // includes ThrowIfFrozen
+            if (subTypes == null) subTypes = new BasicList();
+            subTypes.Add(subType);
+            model.ResetKeyCache();
+            return this;
+        }
+#if COREFX || PROFILE259
+		internal static readonly TypeInfo ienumerable = typeof(IEnumerable).GetTypeInfo();
+#else
+        internal static readonly Type ienumerable = typeof(IEnumerable);
+#endif
+        private void SetBaseType(MetaType baseType)
+        {
+            if (baseType == null) throw new ArgumentNullException("baseType");
+            if (this.baseType == baseType) return;
+            if (this.baseType != null) throw new InvalidOperationException($"Type '{this.baseType.Type.FullName}' can only participate in one inheritance hierarchy");
+
+            MetaType type = baseType;
+            while (type != null)
+            {
+                if (ReferenceEquals(type, this)) throw new InvalidOperationException($"Cyclic inheritance of '{this.baseType.Type.FullName}' is not allowed");
+                type = type.baseType;
+            }
+            this.baseType = baseType;
+        }
+
+        private CallbackSet callbacks;
+
+        /// <summary>
+        /// Indicates whether the current type has defined callbacks 
+        /// </summary>
+        public bool HasCallbacks => callbacks != null && callbacks.NonTrivial;
+
+        /// <summary>
+        /// Indicates whether the current type has defined subtypes
+        /// </summary>
+        public bool HasSubtypes => subTypes != null && subTypes.Count != 0;
+
+        /// <summary>
+        /// Returns the set of callbacks defined for this type
+        /// </summary>
+        public CallbackSet Callbacks
+        {
+            get
+            {
+                if (callbacks == null) callbacks = new CallbackSet(this);
+                return callbacks;
+            }
+        }
+
+        private bool IsValueType
+        {
+            get
+            {
+#if COREFX || PROFILE259
+				return typeInfo.IsValueType;
+#else
+                return type.IsValueType;
+#endif
+            }
+        }
+        /// <summary>
+        /// Assigns the callbacks to use during serialiation/deserialization.
+        /// </summary>
+        /// <param name="beforeSerialize">The method (or null) called before serialization begins.</param>
+        /// <param name="afterSerialize">The method (or null) called when serialization is complete.</param>
+        /// <param name="beforeDeserialize">The method (or null) called before deserialization begins (or when a new instance is created during deserialization).</param>
+        /// <param name="afterDeserialize">The method (or null) called when deserialization is complete.</param>
+        /// <returns>The set of callbacks.</returns>
+        public MetaType SetCallbacks(MethodInfo beforeSerialize, MethodInfo afterSerialize, MethodInfo beforeDeserialize, MethodInfo afterDeserialize)
+        {
+            CallbackSet callbacks = Callbacks;
+            callbacks.BeforeSerialize = beforeSerialize;
+            callbacks.AfterSerialize = afterSerialize;
+            callbacks.BeforeDeserialize = beforeDeserialize;
+            callbacks.AfterDeserialize = afterDeserialize;
+            return this;
+        }
+        /// <summary>
+        /// Assigns the callbacks to use during serialiation/deserialization.
+        /// </summary>
+        /// <param name="beforeSerialize">The name of the method (or null) called before serialization begins.</param>
+        /// <param name="afterSerialize">The name of the method (or null) called when serialization is complete.</param>
+        /// <param name="beforeDeserialize">The name of the method (or null) called before deserialization begins (or when a new instance is created during deserialization).</param>
+        /// <param name="afterDeserialize">The name of the method (or null) called when deserialization is complete.</param>
+        /// <returns>The set of callbacks.</returns>
+        public MetaType SetCallbacks(string beforeSerialize, string afterSerialize, string beforeDeserialize, string afterDeserialize)
+        {
+            if (IsValueType) throw new InvalidOperationException();
+            CallbackSet callbacks = Callbacks;
+            callbacks.BeforeSerialize = ResolveMethod(beforeSerialize, true);
+            callbacks.AfterSerialize = ResolveMethod(afterSerialize, true);
+            callbacks.BeforeDeserialize = ResolveMethod(beforeDeserialize, true);
+            callbacks.AfterDeserialize = ResolveMethod(afterDeserialize, true);
+            return this;
+        }
+
+        internal string GetSchemaTypeName()
+        {
+            if (surrogate != null) return model[surrogate].GetSchemaTypeName();
+
+            if (!string.IsNullOrEmpty(name)) return name;
+
+            string typeName = type.Name;
+            if (type
+#if COREFX || PROFILE259
+				.GetTypeInfo()
+#endif
+                .IsGenericType)
+            {
+                var sb = new StringBuilder(typeName);
+                int split = typeName.IndexOf('`');
+                if (split >= 0) sb.Length = split;
+                foreach (Type arg in type
+#if COREFX || PROFILE259
+					.GetTypeInfo().GenericTypeArguments
+#else
+                    .GetGenericArguments()
+#endif
+                    )
+                {
+                    sb.Append('_');
+                    Type tmp = arg;
+                    int key = model.GetKey(ref tmp);
+                    MetaType mt;
+                    if (key >= 0 && (mt = model[tmp]) != null && mt.surrogate == null) // <=== need to exclude surrogate to avoid chance of infinite loop
+                    {
+
+                        sb.Append(mt.GetSchemaTypeName());
+                    }
+                    else
+                    {
+                        sb.Append(tmp.Name);
+                    }
+                }
+                return sb.ToString();
+            }
+
+            return typeName;
+        }
+
+        private string name;
+
+        /// <summary>
+        /// Gets or sets the name of this contract.
+        /// </summary>
+        public string Name
+        {
+            get
+            {
+                return name;
+            }
+            set
+            {
+                ThrowIfFrozen();
+                name = value;
+            }
+        }
+
+        private MethodInfo factory;
+        /// <summary>
+        /// Designate a factory-method to use to create instances of this type
+        /// </summary>
+        public MetaType SetFactory(MethodInfo factory)
+        {
+            model.VerifyFactory(factory, type);
+            ThrowIfFrozen();
+            this.factory = factory;
+            return this;
+        }
+
+        /// <summary>
+        /// Designate a factory-method to use to create instances of this type
+        /// </summary>
+        public MetaType SetFactory(string factory)
+        {
+            return SetFactory(ResolveMethod(factory, false));
+        }
+
+        private MethodInfo ResolveMethod(string name, bool instance)
+        {
+            if (string.IsNullOrEmpty(name)) return null;
+#if COREFX
+            return instance ? Helpers.GetInstanceMethod(typeInfo, name) : Helpers.GetStaticMethod(typeInfo, name);
+#else
+            return instance ? Helpers.GetInstanceMethod(type, name) : Helpers.GetStaticMethod(type, name);
+#endif
+        }
+
+        private readonly RuntimeTypeModel model;
+
+        internal static Exception InbuiltType(Type type)
+        {
+            return new ArgumentException("Data of this type has inbuilt behaviour, and cannot be added to a model in this way: " + type.FullName);
+        }
+
+        internal MetaType(RuntimeTypeModel model, Type type, MethodInfo factory)
+        {
+            this.factory = factory;
+            if (model == null) throw new ArgumentNullException("model");
+            if (type == null) throw new ArgumentNullException("type");
+
+            if (type.IsArray) throw InbuiltType(type);
+            IProtoSerializer coreSerializer = model.TryGetBasicTypeSerializer(type);
+            if (coreSerializer != null)
+            {
+                throw InbuiltType(type);
+            }
+
+            this.type = type;
+#if COREFX || PROFILE259
+			this.typeInfo = type.GetTypeInfo();
+#endif
+            this.model = model;
+
+            if (Helpers.IsEnum(type))
+            {
+#if COREFX || PROFILE259
+				EnumPassthru = typeInfo.IsDefined(typeof(FlagsAttribute), false);
+#else
+                EnumPassthru = type.IsDefined(model.MapType(typeof(FlagsAttribute)), false);
+#endif
+            }
+        }
+#if COREFX || PROFILE259
+		private readonly TypeInfo typeInfo;
+#endif
+        /// <summary>
+        /// Throws an exception if the type has been made immutable
+        /// </summary>
+        protected internal void ThrowIfFrozen()
+        {
+            if ((flags & OPTIONS_Frozen) != 0) throw new InvalidOperationException("The type cannot be changed once a serializer has been generated for " + type.FullName);
+        }
+
+        // internal void Freeze() { flags |= OPTIONS_Frozen; }
+
+        private readonly Type type;
+        /// <summary>
+        /// The runtime type that the meta-type represents
+        /// </summary>
+        public Type Type => type;
+
+        private IProtoTypeSerializer serializer;
+        internal IProtoTypeSerializer Serializer
+        {
+            get
+            {
+                if (serializer == null)
+                {
+                    int opaqueToken = 0;
+                    try
+                    {
+                        model.TakeLock(ref opaqueToken);
+                        if (serializer == null)
+                        { // double-check, but our main purpse with this lock is to ensure thread-safety with
+                            // serializers needing to wait until another thread has finished adding the properties
+                            SetFlag(OPTIONS_Frozen, true, false);
+                            serializer = BuildSerializer();
+#if FEAT_COMPILER
+                            if (model.AutoCompile) CompileInPlace();
+#endif
+                        }
+                    }
+                    finally
+                    {
+                        model.ReleaseLock(opaqueToken);
+                    }
+                }
+                return serializer;
+            }
+        }
+        internal bool IsList
+        {
+            get
+            {
+                Type itemType = IgnoreListHandling ? null : TypeModel.GetListItemType(model, type);
+                return itemType != null;
+            }
+        }
+        private IProtoTypeSerializer BuildSerializer()
+        {
+            if (Helpers.IsEnum(type))
+            {
+                return new TagDecorator(ProtoBuf.Serializer.ListItemTag, WireType.Variant, false, new EnumSerializer(type, GetEnumMap()));
+            }
+            Type itemType = IgnoreListHandling ? null : TypeModel.GetListItemType(model, type);
+            if (itemType != null)
+            {
+                if (surrogate != null)
+                {
+                    throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot use a surrogate");
+                }
+                if (subTypes != null && subTypes.Count != 0)
+                {
+                    throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be subclassed");
+                }
+                Type defaultType = null;
+                ResolveListTypes(model, type, ref itemType, ref defaultType);
+                ValueMember fakeMember = new ValueMember(model, ProtoBuf.Serializer.ListItemTag, type, itemType, defaultType, DataFormat.Default);
+                return new TypeSerializer(model, type, new int[] { ProtoBuf.Serializer.ListItemTag }, new IProtoSerializer[] { fakeMember.Serializer }, null, true, true, null, constructType, factory);
+            }
+            if (surrogate != null)
+            {
+                MetaType mt = model[surrogate], mtBase;
+                while ((mtBase = mt.baseType) != null) { mt = mtBase; }
+                return new SurrogateSerializer(model, type, surrogate, mt.Serializer);
+            }
+            if (IsAutoTuple)
+            {
+                ConstructorInfo ctor = ResolveTupleConstructor(type, out MemberInfo[] mapping);
+                if (ctor == null) throw new InvalidOperationException();
+                return new TupleSerializer(model, ctor, mapping);
+            }
+
+            fields.Trim();
+            int fieldCount = fields.Count;
+            int subTypeCount = subTypes == null ? 0 : subTypes.Count;
+            int[] fieldNumbers = new int[fieldCount + subTypeCount];
+            IProtoSerializer[] serializers = new IProtoSerializer[fieldCount + subTypeCount];
+            int i = 0;
+            if (subTypeCount != 0)
+            {
+                foreach (SubType subType in subTypes)
+                {
+#if COREFX || PROFILE259
+					if (!subType.DerivedType.IgnoreListHandling && ienumerable.IsAssignableFrom(subType.DerivedType.Type.GetTypeInfo()))
+#else
+                    if (!subType.DerivedType.IgnoreListHandling && model.MapType(ienumerable).IsAssignableFrom(subType.DerivedType.Type))
+#endif
+                    {
+                        throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass");
+                    }
+                    fieldNumbers[i] = subType.FieldNumber;
+                    serializers[i++] = subType.Serializer;
+                }
+            }
+            if (fieldCount != 0)
+            {
+                foreach (ValueMember member in fields)
+                {
+                    fieldNumbers[i] = member.FieldNumber;
+                    serializers[i++] = member.Serializer;
+                }
+            }
+
+            BasicList baseCtorCallbacks = null;
+            MetaType tmp = BaseType;
+
+            while (tmp != null)
+            {
+                MethodInfo method = tmp.HasCallbacks ? tmp.Callbacks.BeforeDeserialize : null;
+                if (method != null)
+                {
+                    if (baseCtorCallbacks == null) baseCtorCallbacks = new BasicList();
+                    baseCtorCallbacks.Add(method);
+                }
+                tmp = tmp.BaseType;
+            }
+            MethodInfo[] arr = null;
+            if (baseCtorCallbacks != null)
+            {
+                arr = new MethodInfo[baseCtorCallbacks.Count];
+                baseCtorCallbacks.CopyTo(arr, 0);
+                Array.Reverse(arr);
+            }
+            return new TypeSerializer(model, type, fieldNumbers, serializers, arr, baseType == null, UseConstructor, callbacks, constructType, factory);
+        }
+
+        [Flags]
+        internal enum AttributeFamily
+        {
+            None = 0, ProtoBuf = 1, DataContractSerialier = 2, XmlSerializer = 4, AutoTuple = 8
+        }
+        static Type GetBaseType(MetaType type)
+        {
+#if COREFX || PROFILE259
+			return type.typeInfo.BaseType;
+#else
+            return type.type.BaseType;
+#endif
+        }
+        internal static bool GetAsReferenceDefault(RuntimeTypeModel model, Type type)
+        {
+            if (type == null) throw new ArgumentNullException(nameof(type));
+            if (Helpers.IsEnum(type)) return false; // never as-ref
+            AttributeMap[] typeAttribs = AttributeMap.Create(model, type, false);
+            for (int i = 0; i < typeAttribs.Length; i++)
+            {
+                if (typeAttribs[i].AttributeType.FullName == "ProtoBuf.ProtoContractAttribute")
+                {
+                    if (typeAttribs[i].TryGet("AsReferenceDefault", out object tmp)) return (bool)tmp;
+                }
+            }
+            return false;
+        }
+
+        internal void ApplyDefaultBehaviour()
+        {
+            Type baseType = GetBaseType(this);
+            if (baseType != null && model.FindWithoutAdd(baseType) == null
+                && GetContractFamily(model, baseType, null) != MetaType.AttributeFamily.None)
+            {
+                model.FindOrAddAuto(baseType, true, false, false);
+            }
+
+            AttributeMap[] typeAttribs = AttributeMap.Create(model, type, false);
+            AttributeFamily family = GetContractFamily(model, type, typeAttribs);
+            if (family == AttributeFamily.AutoTuple)
+            {
+                SetFlag(OPTIONS_AutoTuple, true, true);
+            }
+            bool isEnum = !EnumPassthru && Helpers.IsEnum(type);
+            if (family == AttributeFamily.None && !isEnum) return; // and you'd like me to do what, exactly?
+
+            bool enumShouldUseImplicitPassThru = isEnum;
+            BasicList partialIgnores = null, partialMembers = null;
+            int dataMemberOffset = 0, implicitFirstTag = 1;
+            bool inferTagByName = model.InferTagFromNameDefault;
+            ImplicitFields implicitMode = ImplicitFields.None;
+            string name = null;
+            for (int i = 0; i < typeAttribs.Length; i++)
+            {
+                AttributeMap item = (AttributeMap)typeAttribs[i];
+                object tmp;
+                string fullAttributeTypeName = item.AttributeType.FullName;
+                if (!isEnum && fullAttributeTypeName == "ProtoBuf.ProtoIncludeAttribute")
+                {
+                    int tag = 0;
+                    if (item.TryGet("tag", out tmp)) tag = (int)tmp;
+                    DataFormat dataFormat = DataFormat.Default;
+                    if (item.TryGet("DataFormat", out tmp))
+                    {
+                        dataFormat = (DataFormat)(int)tmp;
+                    }
+                    Type knownType = null;
+                    try
+                    {
+                        if (item.TryGet("knownTypeName", out tmp)) knownType = model.GetType((string)tmp, type
+#if COREFX || PROFILE259
+							.GetTypeInfo()
+#endif
+                            .Assembly);
+                        else if (item.TryGet("knownType", out tmp)) knownType = (Type)tmp;
+                    }
+                    catch (Exception ex)
+                    {
+                        throw new InvalidOperationException("Unable to resolve sub-type of: " + type.FullName, ex);
+                    }
+                    if (knownType == null)
+                    {
+                        throw new InvalidOperationException("Unable to resolve sub-type of: " + type.FullName);
+                    }
+                    if (IsValidSubType(knownType)) AddSubType(tag, knownType, dataFormat);
+                }
+
+                if (fullAttributeTypeName == "ProtoBuf.ProtoPartialIgnoreAttribute")
+                {
+                    if (item.TryGet(nameof(ProtoPartialIgnoreAttribute.MemberName), out tmp) && tmp != null)
+                    {
+                        if (partialIgnores == null) partialIgnores = new BasicList();
+                        partialIgnores.Add((string)tmp);
+                    }
+                }
+                if (!isEnum && fullAttributeTypeName == "ProtoBuf.ProtoPartialMemberAttribute")
+                {
+                    if (partialMembers == null) partialMembers = new BasicList();
+                    partialMembers.Add(item);
+                }
+
+                if (fullAttributeTypeName == "ProtoBuf.ProtoContractAttribute")
+                {
+                    if (item.TryGet(nameof(ProtoContractAttribute.Name), out tmp)) name = (string)tmp;
+                    if (Helpers.IsEnum(type)) // note this is subtly different to isEnum; want to do this even if [Flags]
+                    {
+                        if (item.TryGet(nameof(ProtoContractAttribute.EnumPassthruHasValue), false, out tmp) && (bool)tmp)
+                        {
+                            if (item.TryGet(nameof(ProtoContractAttribute.EnumPassthru), out tmp))
+                            {
+                                EnumPassthru = (bool)tmp;
+                                enumShouldUseImplicitPassThru = false;
+                                if (EnumPassthru) isEnum = false; // no longer treated as an enum
+                            }
+                        }
+                    }
+                    else
+                    {
+                        if (item.TryGet(nameof(ProtoContractAttribute.DataMemberOffset), out tmp)) dataMemberOffset = (int)tmp;
+
+                        if (item.TryGet(nameof(ProtoContractAttribute.InferTagFromNameHasValue), false, out tmp) && (bool)tmp)
+                        {
+                            if (item.TryGet(nameof(ProtoContractAttribute.InferTagFromName), out tmp)) inferTagByName = (bool)tmp;
+                        }
+
+                        if (item.TryGet(nameof(ProtoContractAttribute.ImplicitFields), out tmp) && tmp != null)
+                        {
+                            implicitMode = (ImplicitFields)(int)tmp; // note that this uses the bizarre unboxing rules of enums/underlying-types
+                        }
+
+                        if (item.TryGet(nameof(ProtoContractAttribute.SkipConstructor), out tmp)) UseConstructor = !(bool)tmp;
+                        if (item.TryGet(nameof(ProtoContractAttribute.IgnoreListHandling), out tmp)) IgnoreListHandling = (bool)tmp;
+                        if (item.TryGet(nameof(ProtoContractAttribute.AsReferenceDefault), out tmp)) AsReferenceDefault = (bool)tmp;
+                        if (item.TryGet(nameof(ProtoContractAttribute.ImplicitFirstTag), out tmp) && (int)tmp > 0) implicitFirstTag = (int)tmp;
+                        if (item.TryGet(nameof(ProtoContractAttribute.IsGroup), out tmp)) IsGroup = (bool)tmp;
+
+                        if (item.TryGet(nameof(ProtoContractAttribute.Surrogate), out tmp))
+                        {
+                            SetSurrogate((Type)tmp);
+                        }
+                    }
+                }
+
+                if (fullAttributeTypeName == "System.Runtime.Serialization.DataContractAttribute")
+                {
+                    if (name == null && item.TryGet("Name", out tmp)) name = (string)tmp;
+                }
+                if (fullAttributeTypeName == "System.Xml.Serialization.XmlTypeAttribute")
+                {
+                    if (name == null && item.TryGet("TypeName", out tmp)) name = (string)tmp;
+                }
+            }
+            if (!string.IsNullOrEmpty(name)) Name = name;
+            if (implicitMode != ImplicitFields.None)
+            {
+                family &= AttributeFamily.ProtoBuf; // with implicit fields, **only** proto attributes are important
+            }
+            MethodInfo[] callbacks = null;
+
+            BasicList members = new BasicList();
+
+#if PROFILE259
+			IEnumerable<MemberInfo> foundList;
+            if(isEnum) {
+                foundList = type.GetRuntimeFields();
+            }
+            else
+            {
+                List<MemberInfo> list = new List<MemberInfo>();
+                foreach(PropertyInfo prop in type.GetRuntimeProperties()) {
+                    MethodInfo getter = Helpers.GetGetMethod(prop, false, false);
+                    if(getter != null && !getter.IsStatic) list.Add(prop);
+                }
+                foreach(FieldInfo fld in type.GetRuntimeFields()) if(fld.IsPublic && !fld.IsStatic) list.Add(fld);
+                foreach(MethodInfo mthd in type.GetRuntimeMethods()) if(mthd.IsPublic && !mthd.IsStatic) list.Add(mthd);
+                foundList = list;
+            }
+#else
+            MemberInfo[] foundList = type.GetMembers(isEnum ? BindingFlags.Public | BindingFlags.Static
+                : BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+#endif
+            bool hasConflictingEnumValue = false;
+            foreach (MemberInfo member in foundList)
+            {
+                if (member.DeclaringType != type) continue;
+                if (member.IsDefined(model.MapType(typeof(ProtoIgnoreAttribute)), true)) continue;
+                if (partialIgnores != null && partialIgnores.Contains(member.Name)) continue;
+
+                bool forced = false, isPublic, isField;
+                Type effectiveType;
+
+                if (member is PropertyInfo property)
+                {
+                    if (isEnum) continue; // wasn't expecting any props!
+                    MemberInfo backingField = null;
+                    if (!property.CanWrite)
+                    {
+                        // roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
+                        var backingFieldName = $"<{property.Name}>k__BackingField";
+                        foreach (var fieldMemeber in foundList)
+                        {
+                            if ((fieldMemeber as FieldInfo != null) && fieldMemeber.Name == backingFieldName)
+                            {
+                                backingField = fieldMemeber;
+                                break;
+                            }
+                        }
+                    }
+                    effectiveType = property.PropertyType;
+                    isPublic = Helpers.GetGetMethod(property, false, false) != null;
+                    isField = false;
+                    ApplyDefaultBehaviour_AddMembers(model, family, isEnum, partialMembers, dataMemberOffset, inferTagByName, implicitMode, members, member, ref forced, isPublic, isField, ref effectiveType, ref hasConflictingEnumValue, backingField);
+                }
+                else if (member is FieldInfo field)
+                {
+                    effectiveType = field.FieldType;
+                    isPublic = field.IsPublic;
+                    isField = true;
+                    if (isEnum && !field.IsStatic)
+                    { // only care about static things on enums; WinRT has a __value instance field!
+                        continue;
+                    }
+                    ApplyDefaultBehaviour_AddMembers(model, family, isEnum, partialMembers, dataMemberOffset, inferTagByName, implicitMode, members, member, ref forced, isPublic, isField, ref effectiveType, ref hasConflictingEnumValue);
+                }
+                else if (member is MethodInfo method)
+                {
+                    if (isEnum) continue;
+                    AttributeMap[] memberAttribs = AttributeMap.Create(model, method, false);
+                    if (memberAttribs != null && memberAttribs.Length > 0)
+                    {
+                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeSerializationAttribute", ref callbacks, 0);
+                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterSerializationAttribute", ref callbacks, 1);
+                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeDeserializationAttribute", ref callbacks, 2);
+                        CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterDeserializationAttribute", ref callbacks, 3);
+                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializingAttribute", ref callbacks, 4);
+                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializedAttribute", ref callbacks, 5);
+                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializingAttribute", ref callbacks, 6);
+                        CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializedAttribute", ref callbacks, 7);
+                    }
+                }
+            }
+
+            if (isEnum && enumShouldUseImplicitPassThru && !hasConflictingEnumValue)
+            {
+                EnumPassthru = true;
+                // but leave isEnum alone
+            }
+            var arr = new ProtoMemberAttribute[members.Count];
+            members.CopyTo(arr, 0);
+
+            if (inferTagByName || implicitMode != ImplicitFields.None)
+            {
+                Array.Sort(arr);
+                int nextTag = implicitFirstTag;
+                foreach (ProtoMemberAttribute normalizedAttribute in arr)
+                {
+                    if (!normalizedAttribute.TagIsPinned) // if ProtoMember etc sets a tag, we'll trust it
+                    {
+                        normalizedAttribute.Rebase(nextTag++);
+                    }
+                }
+            }
+
+            foreach (ProtoMemberAttribute normalizedAttribute in arr)
+            {
+                ValueMember vm = ApplyDefaultBehaviour(isEnum, normalizedAttribute);
+                if (vm != null)
+                {
+                    Add(vm);
+                }
+            }
+
+            if (callbacks != null)
+            {
+                SetCallbacks(Coalesce(callbacks, 0, 4), Coalesce(callbacks, 1, 5),
+                    Coalesce(callbacks, 2, 6), Coalesce(callbacks, 3, 7));
+            }
+        }
+
+        private static void ApplyDefaultBehaviour_AddMembers(TypeModel model, AttributeFamily family, bool isEnum, BasicList partialMembers, int dataMemberOffset, bool inferTagByName, ImplicitFields implicitMode, BasicList members, MemberInfo member, ref bool forced, bool isPublic, bool isField, ref Type effectiveType, ref bool hasConflictingEnumValue, MemberInfo backingMember = null)
+        {
+            switch (implicitMode)
+            {
+                case ImplicitFields.AllFields:
+                    if (isField) forced = true;
+                    break;
+                case ImplicitFields.AllPublic:
+                    if (isPublic) forced = true;
+                    break;
+            }
+
+            // we just don't like delegate types ;p
+#if COREFX || PROFILE259
+			if (effectiveType.GetTypeInfo().IsSubclassOf(typeof(Delegate))) effectiveType = null;
+#else
+            if (effectiveType.IsSubclassOf(model.MapType(typeof(Delegate)))) effectiveType = null;
+#endif
+            if (effectiveType != null)
+            {
+                ProtoMemberAttribute normalizedAttribute = NormalizeProtoMember(model, member, family, forced, isEnum, partialMembers, dataMemberOffset, inferTagByName, ref hasConflictingEnumValue, backingMember);
+                if (normalizedAttribute != null) members.Add(normalizedAttribute);
+            }
+        }
+
+        static MethodInfo Coalesce(MethodInfo[] arr, int x, int y)
+        {
+            MethodInfo mi = arr[x];
+            if (mi == null) mi = arr[y];
+            return mi;
+        }
+
+        internal static AttributeFamily GetContractFamily(RuntimeTypeModel model, Type type, AttributeMap[] attributes)
+        {
+            AttributeFamily family = AttributeFamily.None;
+
+            if (attributes == null) attributes = AttributeMap.Create(model, type, false);
+
+            for (int i = 0; i < attributes.Length; i++)
+            {
+                switch (attributes[i].AttributeType.FullName)
+                {
+                    case "ProtoBuf.ProtoContractAttribute":
+                        bool tmp = false;
+                        GetFieldBoolean(ref tmp, attributes[i], "UseProtoMembersOnly");
+                        if (tmp) return AttributeFamily.ProtoBuf;
+                        family |= AttributeFamily.ProtoBuf;
+                        break;
+                    case "System.Xml.Serialization.XmlTypeAttribute":
+                        if (!model.AutoAddProtoContractTypesOnly)
+                        {
+                            family |= AttributeFamily.XmlSerializer;
+                        }
+                        break;
+                    case "System.Runtime.Serialization.DataContractAttribute":
+                        if (!model.AutoAddProtoContractTypesOnly)
+                        {
+                            family |= AttributeFamily.DataContractSerialier;
+                        }
+                        break;
+                }
+            }
+            if (family == AttributeFamily.None)
+            { // check for obvious tuples
+                if (ResolveTupleConstructor(type, out MemberInfo[] mapping) != null)
+                {
+                    family |= AttributeFamily.AutoTuple;
+                }
+            }
+            return family;
+        }
+        internal static ConstructorInfo ResolveTupleConstructor(Type type, out MemberInfo[] mappedMembers)
+        {
+            mappedMembers = null;
+            if (type == null) throw new ArgumentNullException(nameof(type));
+#if COREFX || PROFILE259
+			TypeInfo typeInfo = type.GetTypeInfo();
+            if (typeInfo.IsAbstract) return null; // as if!
+            ConstructorInfo[] ctors = Helpers.GetConstructors(typeInfo, false);
+#else
+            if (type.IsAbstract) return null; // as if!
+            ConstructorInfo[] ctors = Helpers.GetConstructors(type, false);
+#endif
+            // need to have an interesting constructor to bother even checking this stuff
+            if (ctors.Length == 0 || (ctors.Length == 1 && ctors[0].GetParameters().Length == 0)) return null;
+
+            MemberInfo[] fieldsPropsUnfiltered = Helpers.GetInstanceFieldsAndProperties(type, true);
+            BasicList memberList = new BasicList();
+            // for most types we'll enforce that you need readonly, because that is what protobuf-net
+            // always did historically; but: if you smell so much like a Tuple that it is *in your name*,
+            // we'll let you past that
+            bool demandReadOnly = type.Name.IndexOf("Tuple", StringComparison.OrdinalIgnoreCase) < 0;
+            for (int i = 0; i < fieldsPropsUnfiltered.Length; i++)
+            {
+                if (fieldsPropsUnfiltered[i] is PropertyInfo prop)
+                {
+                    if (!prop.CanRead) return null; // no use if can't read
+                    if (demandReadOnly && prop.CanWrite && Helpers.GetSetMethod(prop, false, false) != null) return null; // don't allow a public set (need to allow non-public to handle Mono's KeyValuePair<,>)
+                    memberList.Add(prop);
+                }
+                else
+                {
+                    if (fieldsPropsUnfiltered[i] is FieldInfo field)
+                    {
+                        if (demandReadOnly && !field.IsInitOnly) return null; // all public fields must be readonly to be counted a tuple
+                        memberList.Add(field);
+                    }
+                }
+            }
+            if (memberList.Count == 0)
+            {
+                return null;
+            }
+
+            MemberInfo[] members = new MemberInfo[memberList.Count];
+            memberList.CopyTo(members, 0);
+
+            int[] mapping = new int[members.Length];
+            int found = 0;
+            ConstructorInfo result = null;
+            mappedMembers = new MemberInfo[mapping.Length];
+            for (int i = 0; i < ctors.Length; i++)
+            {
+                ParameterInfo[] parameters = ctors[i].GetParameters();
+
+                if (parameters.Length != members.Length) continue;
+
+                // reset the mappings to test
+                for (int j = 0; j < mapping.Length; j++) mapping[j] = -1;
+
+                for (int j = 0; j < parameters.Length; j++)
+                {
+                    for (int k = 0; k < members.Length; k++)
+                    {
+                        if (string.Compare(parameters[j].Name, members[k].Name, StringComparison.OrdinalIgnoreCase) != 0) continue;
+                        Type memberType = Helpers.GetMemberType(members[k]);
+                        if (memberType != parameters[j].ParameterType) continue;
+
+                        mapping[j] = k;
+                    }
+                }
+                // did we map all?
+                bool notMapped = false;
+                for (int j = 0; j < mapping.Length; j++)
+                {
+                    if (mapping[j] < 0)
+                    {
+                        notMapped = true;
+                        break;
+                    }
+                    mappedMembers[j] = members[mapping[j]];
+                }
+
+                if (notMapped) continue;
+                found++;
+                result = ctors[i];
+
+            }
+            return found == 1 ? result : null;
+        }
+
+        private static void CheckForCallback(MethodInfo method, AttributeMap[] attributes, string callbackTypeName, ref MethodInfo[] callbacks, int index)
+        {
+            for (int i = 0; i < attributes.Length; i++)
+            {
+                if (attributes[i].AttributeType.FullName == callbackTypeName)
+                {
+                    if (callbacks == null) { callbacks = new MethodInfo[8]; }
+                    else if (callbacks[index] != null)
+                    {
+#if COREFX || PROFILE259
+						Type reflected = method.DeclaringType;
+#else
+                        Type reflected = method.ReflectedType;
+#endif
+                        throw new ProtoException("Duplicate " + callbackTypeName + " callbacks on " + reflected.FullName);
+                    }
+                    callbacks[index] = method;
+                }
+            }
+        }
+        private static bool HasFamily(AttributeFamily value, AttributeFamily required)
+        {
+            return (value & required) == required;
+        }
+
+        private static ProtoMemberAttribute NormalizeProtoMember(TypeModel model, MemberInfo member, AttributeFamily family, bool forced, bool isEnum, BasicList partialMembers, int dataMemberOffset, bool inferByTagName, ref bool hasConflictingEnumValue, MemberInfo backingMember = null)
+        {
+            if (member == null || (family == AttributeFamily.None && !isEnum)) return null; // nix
+            int fieldNumber = int.MinValue, minAcceptFieldNumber = inferByTagName ? -1 : 1;
+            string name = null;
+            bool isPacked = false, ignore = false, done = false, isRequired = false, asReference = false, asReferenceHasValue = false, dynamicType = false, tagIsPinned = false, overwriteList = false;
+            DataFormat dataFormat = DataFormat.Default;
+            if (isEnum) forced = true;
+            AttributeMap[] attribs = AttributeMap.Create(model, member, true);
+            AttributeMap attrib;
+
+            if (isEnum)
+            {
+                attrib = GetAttribute(attribs, "ProtoBuf.ProtoIgnoreAttribute");
+                if (attrib != null)
+                {
+                    ignore = true;
+                }
+                else
+                {
+                    attrib = GetAttribute(attribs, "ProtoBuf.ProtoEnumAttribute");
+#if PORTABLE || CF || COREFX || PROFILE259
+					fieldNumber = Convert.ToInt32(((FieldInfo)member).GetValue(null));
+#else
+                    fieldNumber = Convert.ToInt32(((FieldInfo)member).GetRawConstantValue());
+#endif
+                    if (attrib != null)
+                    {
+                        GetFieldName(ref name, attrib, nameof(ProtoEnumAttribute.Name));
+
+                        if ((bool)Helpers.GetInstanceMethod(attrib.AttributeType
+#if COREFX || PROFILE259
+							 .GetTypeInfo()
+#endif
+                            , nameof(ProtoEnumAttribute.HasValue)).Invoke(attrib.Target, null))
+                        {
+                            if (attrib.TryGet(nameof(ProtoEnumAttribute.Value), out object tmp))
+                            {
+                                if (fieldNumber != (int)tmp)
+                                {
+                                    hasConflictingEnumValue = true;
+                                }
+                                fieldNumber = (int)tmp;
+                            }
+                        }
+                    }
+
+                }
+                done = true;
+            }
+
+            if (!ignore && !done) // always consider ProtoMember 
+            {
+                attrib = GetAttribute(attribs, "ProtoBuf.ProtoMemberAttribute");
+                GetIgnore(ref ignore, attrib, attribs, "ProtoBuf.ProtoIgnoreAttribute");
+
+                if (!ignore && attrib != null)
+                {
+                    GetFieldNumber(ref fieldNumber, attrib, "Tag");
+                    GetFieldName(ref name, attrib, "Name");
+                    GetFieldBoolean(ref isRequired, attrib, "IsRequired");
+                    GetFieldBoolean(ref isPacked, attrib, "IsPacked");
+                    GetFieldBoolean(ref overwriteList, attrib, "OverwriteList");
+                    GetDataFormat(ref dataFormat, attrib, "DataFormat");
+                    GetFieldBoolean(ref asReferenceHasValue, attrib, "AsReferenceHasValue", false);
+
+                    if (asReferenceHasValue)
+                    {
+                        asReferenceHasValue = GetFieldBoolean(ref asReference, attrib, "AsReference", true);
+                    }
+                    GetFieldBoolean(ref dynamicType, attrib, "DynamicType");
+                    done = tagIsPinned = fieldNumber > 0; // note minAcceptFieldNumber only applies to non-proto
+                }
+
+                if (!done && partialMembers != null)
+                {
+                    foreach (AttributeMap ppma in partialMembers)
+                    {
+                        if (ppma.TryGet("MemberName", out object tmp) && (string)tmp == member.Name)
+                        {
+                            GetFieldNumber(ref fieldNumber, ppma, "Tag");
+                            GetFieldName(ref name, ppma, "Name");
+                            GetFieldBoolean(ref isRequired, ppma, "IsRequired");
+                            GetFieldBoolean(ref isPacked, ppma, "IsPacked");
+                            GetFieldBoolean(ref overwriteList, attrib, "OverwriteList");
+                            GetDataFormat(ref dataFormat, ppma, "DataFormat");
+                            GetFieldBoolean(ref asReferenceHasValue, attrib, "AsReferenceHasValue", false);
+
+                            if (asReferenceHasValue)
+                            {
+                                asReferenceHasValue = GetFieldBoolean(ref asReference, ppma, "AsReference", true);
+                            }
+                            GetFieldBoolean(ref dynamicType, ppma, "DynamicType");
+                            if (done = tagIsPinned = fieldNumber > 0) break; // note minAcceptFieldNumber only applies to non-proto
+                        }
+                    }
+                }
+            }
+
+            if (!ignore && !done && HasFamily(family, AttributeFamily.DataContractSerialier))
+            {
+                attrib = GetAttribute(attribs, "System.Runtime.Serialization.DataMemberAttribute");
+                if (attrib != null)
+                {
+                    GetFieldNumber(ref fieldNumber, attrib, "Order");
+                    GetFieldName(ref name, attrib, "Name");
+                    GetFieldBoolean(ref isRequired, attrib, "IsRequired");
+                    done = fieldNumber >= minAcceptFieldNumber;
+                    if (done) fieldNumber += dataMemberOffset; // dataMemberOffset only applies to DCS flags, to allow us to "bump" WCF by a notch
+                }
+            }
+            if (!ignore && !done && HasFamily(family, AttributeFamily.XmlSerializer))
+            {
+                attrib = GetAttribute(attribs, "System.Xml.Serialization.XmlElementAttribute");
+                if (attrib == null) attrib = GetAttribute(attribs, "System.Xml.Serialization.XmlArrayAttribute");
+                GetIgnore(ref ignore, attrib, attribs, "System.Xml.Serialization.XmlIgnoreAttribute");
+                if (attrib != null && !ignore)
+                {
+                    GetFieldNumber(ref fieldNumber, attrib, "Order");
+                    GetFieldName(ref name, attrib, "ElementName");
+                    done = fieldNumber >= minAcceptFieldNumber;
+                }
+            }
+            if (!ignore && !done)
+            {
+                if (GetAttribute(attribs, "System.NonSerializedAttribute") != null) ignore = true;
+            }
+            if (ignore || (fieldNumber < minAcceptFieldNumber && !forced)) return null;
+            ProtoMemberAttribute result = new ProtoMemberAttribute(fieldNumber, forced || inferByTagName)
+            {
+                AsReference = asReference,
+                AsReferenceHasValue = asReferenceHasValue,
+                DataFormat = dataFormat,
+                DynamicType = dynamicType,
+                IsPacked = isPacked,
+                OverwriteList = overwriteList,
+                IsRequired = isRequired,
+                Name = string.IsNullOrEmpty(name) ? member.Name : name,
+                Member = member,
+                BackingMember = backingMember,
+                TagIsPinned = tagIsPinned
+            };
+            return result;
+        }
+
+        private ValueMember ApplyDefaultBehaviour(bool isEnum, ProtoMemberAttribute normalizedAttribute)
+        {
+            MemberInfo member;
+            if (normalizedAttribute == null || (member = normalizedAttribute.Member) == null) return null; // nix
+
+            Type effectiveType = Helpers.GetMemberType(member);
+
+
+            Type itemType = null;
+            Type defaultType = null;
+
+            // check for list types
+            ResolveListTypes(model, effectiveType, ref itemType, ref defaultType);
+            bool ignoreListHandling = false;
+            // but take it back if it is explicitly excluded
+            if (itemType != null)
+            { // looks like a list, but double check for IgnoreListHandling
+                int idx = model.FindOrAddAuto(effectiveType, false, true, false);
+                if (idx >= 0 && (ignoreListHandling = model[effectiveType].IgnoreListHandling))
+                {
+                    itemType = null;
+                    defaultType = null;
+                }
+            }
+            AttributeMap[] attribs = AttributeMap.Create(model, member, true);
+            AttributeMap attrib;
+
+            object defaultValue = null;
+            // implicit zero default
+            if (model.UseImplicitZeroDefaults)
+            {
+                switch (Helpers.GetTypeCode(effectiveType))
+                {
+                    case ProtoTypeCode.Boolean: defaultValue = false; break;
+                    case ProtoTypeCode.Decimal: defaultValue = (decimal)0; break;
+                    case ProtoTypeCode.Single: defaultValue = (float)0; break;
+                    case ProtoTypeCode.Double: defaultValue = (double)0; break;
+                    case ProtoTypeCode.Byte: defaultValue = (byte)0; break;
+                    case ProtoTypeCode.Char: defaultValue = (char)0; break;
+                    case ProtoTypeCode.Int16: defaultValue = (short)0; break;
+                    case ProtoTypeCode.Int32: defaultValue = (int)0; break;
+                    case ProtoTypeCode.Int64: defaultValue = (long)0; break;
+                    case ProtoTypeCode.SByte: defaultValue = (sbyte)0; break;
+                    case ProtoTypeCode.UInt16: defaultValue = (ushort)0; break;
+                    case ProtoTypeCode.UInt32: defaultValue = (uint)0; break;
+                    case ProtoTypeCode.UInt64: defaultValue = (ulong)0; break;
+                    case ProtoTypeCode.TimeSpan: defaultValue = TimeSpan.Zero; break;
+                    case ProtoTypeCode.Guid: defaultValue = Guid.Empty; break;
+                }
+            }
+            if ((attrib = GetAttribute(attribs, "System.ComponentModel.DefaultValueAttribute")) != null)
+            {
+                if (attrib.TryGet("Value", out object tmp)) defaultValue = tmp;
+            }
+            ValueMember vm = ((isEnum || normalizedAttribute.Tag > 0))
+                ? new ValueMember(model, type, normalizedAttribute.Tag, member, effectiveType, itemType, defaultType, normalizedAttribute.DataFormat, defaultValue)
+                    : null;
+            if (vm != null)
+            {
+                vm.BackingMember = normalizedAttribute.BackingMember;
+#if COREFX || PROFILE259
+				TypeInfo finalType = typeInfo;
+#else
+                Type finalType = type;
+#endif
+                PropertyInfo prop = Helpers.GetProperty(finalType, member.Name + "Specified", true);
+                MethodInfo getMethod = Helpers.GetGetMethod(prop, true, true);
+                if (getMethod == null || getMethod.IsStatic) prop = null;
+                if (prop != null)
+                {
+                    vm.SetSpecified(getMethod, Helpers.GetSetMethod(prop, true, true));
+                }
+                else
+                {
+                    MethodInfo method = Helpers.GetInstanceMethod(finalType, "ShouldSerialize" + member.Name, Helpers.EmptyTypes);
+                    if (method != null && method.ReturnType == model.MapType(typeof(bool)))
+                    {
+                        vm.SetSpecified(method, null);
+                    }
+                }
+                if (!string.IsNullOrEmpty(normalizedAttribute.Name)) vm.SetName(normalizedAttribute.Name);
+                vm.IsPacked = normalizedAttribute.IsPacked;
+                vm.IsRequired = normalizedAttribute.IsRequired;
+                vm.OverwriteList = normalizedAttribute.OverwriteList;
+                if (normalizedAttribute.AsReferenceHasValue)
+                {
+                    vm.AsReference = normalizedAttribute.AsReference;
+                }
+                vm.DynamicType = normalizedAttribute.DynamicType;
+
+                vm.IsMap = ignoreListHandling ? false : vm.ResolveMapTypes(out var _, out var _, out var _);
+                if (vm.IsMap) // is it even *allowed* to be a map?
+                {
+                    if ((attrib = GetAttribute(attribs, "ProtoBuf.ProtoMapAttribute")) != null)
+                    {
+                        if (attrib.TryGet(nameof(ProtoMapAttribute.DisableMap), out object tmp) && (bool)tmp)
+                        {
+                            vm.IsMap = false;
+                        }
+                        else
+                        {
+                            if (attrib.TryGet(nameof(ProtoMapAttribute.KeyFormat), out tmp)) vm.MapKeyFormat = (DataFormat)tmp;
+                            if (attrib.TryGet(nameof(ProtoMapAttribute.ValueFormat), out tmp)) vm.MapValueFormat = (DataFormat)tmp;
+                        }
+                    }
+                }
+
+            }
+            return vm;
+        }
+
+        private static void GetDataFormat(ref DataFormat value, AttributeMap attrib, string memberName)
+        {
+            if ((attrib == null) || (value != DataFormat.Default)) return;
+            if (attrib.TryGet(memberName, out object obj) && obj != null) value = (DataFormat)obj;
+        }
+
+        private static void GetIgnore(ref bool ignore, AttributeMap attrib, AttributeMap[] attribs, string fullName)
+        {
+            if (ignore || attrib == null) return;
+            ignore = GetAttribute(attribs, fullName) != null;
+            return;
+        }
+
+        private static void GetFieldBoolean(ref bool value, AttributeMap attrib, string memberName)
+        {
+            GetFieldBoolean(ref value, attrib, memberName, true);
+        }
+        private static bool GetFieldBoolean(ref bool value, AttributeMap attrib, string memberName, bool publicOnly)
+        {
+            if (attrib == null) return false;
+            if (value) return true;
+            if (attrib.TryGet(memberName, publicOnly, out object obj) && obj != null)
+            {
+                value = (bool)obj;
+                return true;
+            }
+            return false;
+        }
+
+        private static void GetFieldNumber(ref int value, AttributeMap attrib, string memberName)
+        {
+            if (attrib == null || value > 0) return;
+            if (attrib.TryGet(memberName, out object obj) && obj != null) value = (int)obj;
+        }
+
+        private static void GetFieldName(ref string name, AttributeMap attrib, string memberName)
+        {
+            if (attrib == null || !string.IsNullOrEmpty(name)) return;
+            if (attrib.TryGet(memberName, out object obj) && obj != null) name = (string)obj;
+        }
+
+        private static AttributeMap GetAttribute(AttributeMap[] attribs, string fullName)
+        {
+            for (int i = 0; i < attribs.Length; i++)
+            {
+                AttributeMap attrib = attribs[i];
+                if (attrib != null && attrib.AttributeType.FullName == fullName) return attrib;
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Adds a member (by name) to the MetaType
+        /// </summary>        
+        public MetaType Add(int fieldNumber, string memberName)
+        {
+            AddField(fieldNumber, memberName, null, null, null);
+            return this;
+        }
+
+        /// <summary>
+        /// Adds a member (by name) to the MetaType, returning the ValueMember rather than the fluent API.
+        /// This is otherwise identical to Add.
+        /// </summary>
+        public ValueMember AddField(int fieldNumber, string memberName)
+        {
+            return AddField(fieldNumber, memberName, null, null, null);
+        }
+
+        /// <summary>
+        /// Gets or sets whether the type should use a parameterless constructor (the default),
+        /// or whether the type should skip the constructor completely. This option is not supported
+        /// on compact-framework.
+        /// </summary>
+        public bool UseConstructor
+        { // negated to have defaults as flat zero
+            get { return !HasFlag(OPTIONS_SkipConstructor); }
+            set { SetFlag(OPTIONS_SkipConstructor, !value, true); }
+        }
+
+        /// <summary>
+        /// The concrete type to create when a new instance of this type is needed; this may be useful when dealing
+        /// with dynamic proxies, or with interface-based APIs
+        /// </summary>
+        public Type ConstructType
+        {
+            get { return constructType; }
+            set
+            {
+                ThrowIfFrozen();
+                constructType = value;
+            }
+        }
+
+        private Type constructType;
+        /// <summary>
+        /// Adds a member (by name) to the MetaType
+        /// </summary>     
+        public MetaType Add(string memberName)
+        {
+            Add(GetNextFieldNumber(), memberName);
+            return this;
+        }
+
+        Type surrogate;
+        /// <summary>
+        /// Performs serialization of this type via a surrogate; all
+        /// other serialization options are ignored and handled
+        /// by the surrogate's configuration.
+        /// </summary>
+        public void SetSurrogate(Type surrogateType)
+        {
+            if (surrogateType == type) surrogateType = null;
+            if (surrogateType != null)
+            {
+                // note that BuildSerializer checks the **CURRENT TYPE** is OK to be surrogated
+                if (surrogateType != null && Helpers.IsAssignableFrom(model.MapType(typeof(IEnumerable)), surrogateType))
+                {
+                    throw new ArgumentException("Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a surrogate");
+                }
+            }
+            ThrowIfFrozen();
+            this.surrogate = surrogateType;
+            // no point in offering chaining; no options are respected
+        }
+
+        internal MetaType GetSurrogateOrSelf()
+        {
+            if (surrogate != null) return model[surrogate];
+            return this;
+        }
+
+        internal MetaType GetSurrogateOrBaseOrSelf(bool deep)
+        {
+            if (surrogate != null) return model[surrogate];
+            MetaType snapshot = this.baseType;
+            if (snapshot != null)
+            {
+                if (deep)
+                {
+                    MetaType tmp;
+                    do
+                    {
+                        tmp = snapshot;
+                        snapshot = snapshot.baseType;
+                    } while (snapshot != null);
+                    return tmp;
+                }
+                return snapshot;
+            }
+            return this;
+        }
+
+        private int GetNextFieldNumber()
+        {
+            int maxField = 0;
+            foreach (ValueMember member in fields)
+            {
+                if (member.FieldNumber > maxField) maxField = member.FieldNumber;
+            }
+            if (subTypes != null)
+            {
+                foreach (SubType subType in subTypes)
+                {
+                    if (subType.FieldNumber > maxField) maxField = subType.FieldNumber;
+                }
+            }
+            return maxField + 1;
+        }
+
+        /// <summary>
+        /// Adds a set of members (by name) to the MetaType
+        /// </summary>     
+        public MetaType Add(params string[] memberNames)
+        {
+            if (memberNames == null) throw new ArgumentNullException("memberNames");
+            int next = GetNextFieldNumber();
+            for (int i = 0; i < memberNames.Length; i++)
+            {
+                Add(next++, memberNames[i]);
+            }
+            return this;
+        }
+
+        /// <summary>
+        /// Adds a member (by name) to the MetaType
+        /// </summary>        
+        public MetaType Add(int fieldNumber, string memberName, object defaultValue)
+        {
+            AddField(fieldNumber, memberName, null, null, defaultValue);
+            return this;
+        }
+
+        /// <summary>
+        /// Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists
+        /// </summary>
+        public MetaType Add(int fieldNumber, string memberName, Type itemType, Type defaultType)
+        {
+            AddField(fieldNumber, memberName, itemType, defaultType, null);
+            return this;
+        }
+
+        /// <summary>
+        /// Adds a member (by name) to the MetaType, including an itemType and defaultType for representing lists, returning the ValueMember rather than the fluent API.
+        /// This is otherwise identical to Add.
+        /// </summary>
+        public ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType)
+        {
+            return AddField(fieldNumber, memberName, itemType, defaultType, null);
+        }
+
+        private ValueMember AddField(int fieldNumber, string memberName, Type itemType, Type defaultType, object defaultValue)
+        {
+            MemberInfo mi = null;
+#if PROFILE259
+			mi = Helpers.IsEnum(type) ? type.GetTypeInfo().GetDeclaredField(memberName) : Helpers.GetInstanceMember(type.GetTypeInfo(), memberName);
+
+#else
+            MemberInfo[] members = type.GetMember(memberName, Helpers.IsEnum(type) ? BindingFlags.Static | BindingFlags.Public : BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+            if (members != null && members.Length == 1) mi = members[0];
+#endif
+            if (mi == null) throw new ArgumentException("Unable to determine member: " + memberName, "memberName");
+
+            Type miType;
+            PropertyInfo pi = null;
+            FieldInfo fi = null;
+#if PORTABLE || COREFX || PROFILE259
+			pi = mi as PropertyInfo;
+            if (pi == null)
+            {
+                fi = mi as FieldInfo;
+                if (fi == null)
+                {
+                    throw new NotSupportedException(mi.GetType().Name);
+                }
+                else
+                {
+                    miType = fi.FieldType;
+                }
+            }
+            else
+            {
+                miType = pi.PropertyType;
+            }
+#else
+            switch (mi.MemberType)
+            {
+                case MemberTypes.Field:
+                    fi = (FieldInfo)mi;
+                    miType = fi.FieldType; break;
+                case MemberTypes.Property:
+                    pi = (PropertyInfo)mi;
+                    miType = pi.PropertyType; break;
+                default:
+                    throw new NotSupportedException(mi.MemberType.ToString());
+            }
+#endif
+            ResolveListTypes(model, miType, ref itemType, ref defaultType);
+
+            MemberInfo backingField = null;
+            if (pi?.CanWrite == false)
+            {
+                string name = $"<{((PropertyInfo)mi).Name}>k__BackingField";
+#if PROFILE259
+				var backingMembers = type.GetTypeInfo().DeclaredMembers;
+	            var memberInfos = backingMembers as MemberInfo[] ?? backingMembers.ToArray();
+	            if (memberInfos.Count() == 1)
+	            {
+		            MemberInfo first = memberInfos.FirstOrDefault();
+		            if (first is FieldInfo)
+		            {
+			            backingField = first;
+		            }
+	            }
+#else
+                var backingMembers = type.GetMember($"<{((PropertyInfo)mi).Name}>k__BackingField", Helpers.IsEnum(type) ? BindingFlags.Static | BindingFlags.Public : BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+                if (backingMembers != null && backingMembers.Length == 1 && (backingMembers[0] as FieldInfo) != null)
+                    backingField = backingMembers[0];
+#endif
+            }
+            ValueMember newField = new ValueMember(model, type, fieldNumber, backingField ?? mi, miType, itemType, defaultType, DataFormat.Default, defaultValue);
+            if (backingField != null)
+                newField.SetName(mi.Name);
+            Add(newField);
+            return newField;
+        }
+
+        internal static void ResolveListTypes(TypeModel model, Type type, ref Type itemType, ref Type defaultType)
+        {
+            if (type == null) return;
+            // handle arrays
+            if (type.IsArray)
+            {
+                if (type.GetArrayRank() != 1)
+                {
+                    throw new NotSupportedException("Multi-dimensional arrays are not supported");
+                }
+                itemType = type.GetElementType();
+                if (itemType == model.MapType(typeof(byte)))
+                {
+                    defaultType = itemType = null;
+                }
+                else
+                {
+                    defaultType = type;
+                }
+            }
+            // handle lists
+            if (itemType == null) { itemType = TypeModel.GetListItemType(model, type); }
+
+            // check for nested data (not allowed)
+            if (itemType != null)
+            {
+                Type nestedItemType = null, nestedDefaultType = null;
+                ResolveListTypes(model, itemType, ref nestedItemType, ref nestedDefaultType);
+                if (nestedItemType != null)
+                {
+                    throw TypeModel.CreateNestedListsNotSupported(type);
+                }
+            }
+
+            if (itemType != null && defaultType == null)
+            {
+#if COREFX || PROFILE259
+				TypeInfo typeInfo = type.GetTypeInfo();
+                if (typeInfo.IsClass && !typeInfo.IsAbstract && Helpers.GetConstructor(typeInfo, Helpers.EmptyTypes, true) != null)
+#else
+                if (type.IsClass && !type.IsAbstract && Helpers.GetConstructor(type, Helpers.EmptyTypes, true) != null)
+#endif
+                {
+                    defaultType = type;
+                }
+                if (defaultType == null)
+                {
+#if COREFX || PROFILE259
+					if (typeInfo.IsInterface)
+#else
+                    if (type.IsInterface)
+#endif
+                    {
+
+                        Type[] genArgs;
+#if COREFX || PROFILE259
+						if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>)
+                            && itemType == typeof(System.Collections.Generic.KeyValuePair<,>).MakeGenericType(genArgs = typeInfo.GenericTypeArguments))
+#else
+                        if (type.IsGenericType && type.GetGenericTypeDefinition() == model.MapType(typeof(System.Collections.Generic.IDictionary<,>))
+                            && itemType == model.MapType(typeof(System.Collections.Generic.KeyValuePair<,>)).MakeGenericType(genArgs = type.GetGenericArguments()))
+#endif
+                        {
+                            defaultType = model.MapType(typeof(System.Collections.Generic.Dictionary<,>)).MakeGenericType(genArgs);
+                        }
+                        else
+                        {
+                            defaultType = model.MapType(typeof(System.Collections.Generic.List<>)).MakeGenericType(itemType);
+                        }
+                    }
+                }
+                // verify that the default type is appropriate
+                if (defaultType != null && !Helpers.IsAssignableFrom(type, defaultType)) { defaultType = null; }
+            }
+        }
+
+        private void Add(ValueMember member)
+        {
+            int opaqueToken = 0;
+            try
+            {
+                model.TakeLock(ref opaqueToken);
+                ThrowIfFrozen();
+                fields.Add(member);
+            }
+            finally
+            {
+                model.ReleaseLock(opaqueToken);
+            }
+        }
+
+        /// <summary>
+        /// Returns the ValueMember that matchs a given field number, or null if not found
+        /// </summary>
+        public ValueMember this[int fieldNumber]
+        {
+            get
+            {
+                foreach (ValueMember member in fields)
+                {
+                    if (member.FieldNumber == fieldNumber) return member;
+                }
+                return null;
+            }
+        }
+        /// <summary>
+        /// Returns the ValueMember that matchs a given member (property/field), or null if not found
+        /// </summary>
+        public ValueMember this[MemberInfo member]
+        {
+            get
+            {
+                if (member == null) return null;
+                foreach (ValueMember x in fields)
+                {
+                    if (x.Member == member || x.BackingMember == member) return x;
+                }
+                return null;
+            }
+        }
+        private readonly BasicList fields = new BasicList();
+
+        /// <summary>
+        /// Returns the ValueMember instances associated with this type
+        /// </summary>
+        public ValueMember[] GetFields()
+        {
+            ValueMember[] arr = new ValueMember[fields.Count];
+            fields.CopyTo(arr, 0);
+            Array.Sort(arr, ValueMember.Comparer.Default);
+            return arr;
+        }
+
+        /// <summary>
+        /// Returns the SubType instances associated with this type
+        /// </summary>
+        public SubType[] GetSubtypes()
+        {
+            if (subTypes == null || subTypes.Count == 0) return new SubType[0];
+            SubType[] arr = new SubType[subTypes.Count];
+            subTypes.CopyTo(arr, 0);
+            Array.Sort(arr, SubType.Comparer.Default);
+            return arr;
+        }
+
+        internal IEnumerable<Type> GetAllGenericArguments()
+        {
+            return GetAllGenericArguments(type);
+        }
+
+        private static IEnumerable<Type> GetAllGenericArguments(Type type)
+        {
+
+#if PROFILE259
+	        var genericArguments = type.GetGenericTypeDefinition().GenericTypeArguments;
+#else
+            var genericArguments = type.GetGenericArguments();
+#endif
+            foreach (var arg in genericArguments)
+            {
+                yield return arg;
+                foreach (var inner in GetAllGenericArguments(arg))
+                {
+                    yield return inner;
+                }
+            }
+        }
+
+#if FEAT_COMPILER
+        /// <summary>
+        /// Compiles the serializer for this type; this is *not* a full
+        /// standalone compile, but can significantly boost performance
+        /// while allowing additional types to be added.
+        /// </summary>
+        /// <remarks>An in-place compile can access non-public types / members</remarks>
+        public void CompileInPlace()
+        {
+            serializer = CompiledSerializer.Wrap(Serializer, model);
+        }
+#endif
+
+        internal bool IsDefined(int fieldNumber)
+        {
+            foreach (ValueMember field in fields)
+            {
+                if (field.FieldNumber == fieldNumber) return true;
+            }
+            return false;
+        }
+
+        internal int GetKey(bool demand, bool getBaseKey)
+        {
+            return model.GetKey(type, demand, getBaseKey);
+        }
+
+        internal EnumSerializer.EnumPair[] GetEnumMap()
+        {
+            if (HasFlag(OPTIONS_EnumPassThru)) return null;
+            EnumSerializer.EnumPair[] result = new EnumSerializer.EnumPair[fields.Count];
+            for (int i = 0; i < result.Length; i++)
+            {
+                ValueMember member = (ValueMember)fields[i];
+                int wireValue = member.FieldNumber;
+                object value = member.GetRawEnumValue();
+                result[i] = new EnumSerializer.EnumPair(wireValue, value, member.MemberType);
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather
+        /// than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums.
+        /// </summary>
+        public bool EnumPassthru
+        {
+            get { return HasFlag(OPTIONS_EnumPassThru); }
+            set { SetFlag(OPTIONS_EnumPassThru, value, true); }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating that this type should NOT be treated as a list, even if it has
+        /// familiar list-like characteristics (enumerable, add, etc)
+        /// </summary>
+        public bool IgnoreListHandling
+        {
+            get { return HasFlag(OPTIONS_IgnoreListHandling); }
+            set { SetFlag(OPTIONS_IgnoreListHandling, value, true); }
+        }
+
+        internal bool Pending
+        {
+            get { return HasFlag(OPTIONS_Pending); }
+            set { SetFlag(OPTIONS_Pending, value, false); }
+        }
+
+        private const ushort
+            OPTIONS_Pending = 1,
+            OPTIONS_EnumPassThru = 2,
+            OPTIONS_Frozen = 4,
+            OPTIONS_PrivateOnApi = 8,
+            OPTIONS_SkipConstructor = 16,
+            OPTIONS_AsReferenceDefault = 32,
+            OPTIONS_AutoTuple = 64,
+            OPTIONS_IgnoreListHandling = 128,
+            OPTIONS_IsGroup = 256;
+
+        private volatile ushort flags;
+        private bool HasFlag(ushort flag) { return (flags & flag) == flag; }
+        private void SetFlag(ushort flag, bool value, bool throwIfFrozen)
+        {
+            if (throwIfFrozen && HasFlag(flag) != value)
+            {
+                ThrowIfFrozen();
+            }
+            if (value)
+                flags |= flag;
+            else
+                flags = (ushort)(flags & ~flag);
+        }
+
+        internal static MetaType GetRootType(MetaType source)
+        {
+            while (source.serializer != null)
+            {
+                MetaType tmp = source.baseType;
+                if (tmp == null) return source;
+                source = tmp; // else loop until we reach something that isn't generated, or is the root
+            }
+
+            // now we get into uncertain territory
+            RuntimeTypeModel model = source.model;
+            int opaqueToken = 0;
+            try
+            {
+                model.TakeLock(ref opaqueToken);
+
+                MetaType tmp;
+                while ((tmp = source.baseType) != null) source = tmp;
+                return source;
+
+            }
+            finally
+            {
+                model.ReleaseLock(opaqueToken);
+            }
+        }
+
+        internal bool IsPrepared()
+        {
+#if FEAT_COMPILER
+            return serializer is CompiledSerializer;
+#else
+            return false;
+#endif
+        }
+
+        internal IEnumerable Fields => this.fields;
+
+        internal static StringBuilder NewLine(StringBuilder builder, int indent)
+        {
+            return Helpers.AppendLine(builder).Append(' ', indent * 3);
+        }
+
+        internal bool IsAutoTuple => HasFlag(OPTIONS_AutoTuple);
+
+        /// <summary>
+        /// Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message)
+        /// </summary>
+        public bool IsGroup
+        {
+            get { return HasFlag(OPTIONS_IsGroup); }
+            set { SetFlag(OPTIONS_IsGroup, value, true); }
+        }
+
+        internal void WriteSchema(StringBuilder builder, int indent, ref RuntimeTypeModel.CommonImports imports, ProtoSyntax syntax)
+        {
+            if (surrogate != null) return; // nothing to write
+
+            ValueMember[] fieldsArr = new ValueMember[fields.Count];
+            fields.CopyTo(fieldsArr, 0);
+            Array.Sort(fieldsArr, ValueMember.Comparer.Default);
+
+            if (IsList)
+            {
+                string itemTypeName = model.GetSchemaTypeName(TypeModel.GetListItemType(model, type), DataFormat.Default, false, false, ref imports);
+                NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
+                NewLine(builder, indent + 1).Append("repeated ").Append(itemTypeName).Append(" items = 1;");
+                NewLine(builder, indent).Append('}');
+            }
+            else if (IsAutoTuple)
+            { // key-value-pair etc
+
+                if (ResolveTupleConstructor(type, out MemberInfo[] mapping) != null)
+                {
+                    NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
+                    for (int i = 0; i < mapping.Length; i++)
+                    {
+                        Type effectiveType;
+                        if (mapping[i] is PropertyInfo property)
+                        {
+                            effectiveType = property.PropertyType;
+                        }
+                        else if (mapping[i] is FieldInfo field)
+                        {
+                            effectiveType = field.FieldType;
+                        }
+                        else
+                        {
+                            throw new NotSupportedException("Unknown member type: " + mapping[i].GetType().Name);
+                        }
+                        NewLine(builder, indent + 1).Append(syntax == ProtoSyntax.Proto2 ? "optional " : "").Append(model.GetSchemaTypeName(effectiveType, DataFormat.Default, false, false, ref imports).Replace('.', '_'))
+                            .Append(' ').Append(mapping[i].Name).Append(" = ").Append(i + 1).Append(';');
+                    }
+                    NewLine(builder, indent).Append('}');
+                }
+            }
+            else if (Helpers.IsEnum(type))
+            {
+                NewLine(builder, indent).Append("enum ").Append(GetSchemaTypeName()).Append(" {");
+                if (fieldsArr.Length == 0 && EnumPassthru)
+                {
+                    if (type
+#if COREFX || PROFILE259
+					.GetTypeInfo()
+#endif
+.IsDefined(model.MapType(typeof(FlagsAttribute)), false))
+                    {
+                        NewLine(builder, indent + 1).Append("// this is a composite/flags enumeration");
+                    }
+                    else
+                    {
+                        NewLine(builder, indent + 1).Append("// this enumeration will be passed as a raw value");
+                    }
+                    foreach (FieldInfo field in
+#if PROFILE259
+						type.GetRuntimeFields()
+#else
+                        type.GetFields()
+#endif
+
+                        )
+                    {
+                        if (field.IsStatic && field.IsLiteral)
+                        {
+                            object enumVal;
+#if PORTABLE || CF || NETSTANDARD1_3 || NETSTANDARD1_4 || PROFILE259 || UAP
+							enumVal = Convert.ChangeType(field.GetValue(null), Enum.GetUnderlyingType(field.FieldType), System.Globalization.CultureInfo.InvariantCulture);
+#else
+                            enumVal = field.GetRawConstantValue();
+#endif
+                            NewLine(builder, indent + 1).Append(field.Name).Append(" = ").Append(enumVal).Append(";");
+                        }
+                    }
+
+                }
+                else
+                {
+                    Dictionary<int, int> countByField = new Dictionary<int, int>(fieldsArr.Length);
+                    bool needsAlias = false;
+                    foreach (var field in fieldsArr)
+                    {
+                        if (countByField.ContainsKey(field.FieldNumber))
+                        {  // no point actually counting; that's enough to know we have a problem
+                            needsAlias = true;
+                            break;
+                        }
+                        countByField.Add(field.FieldNumber, 1);
+                    }
+                    if (needsAlias)
+                    {   // duplicated value requires allow_alias
+                        NewLine(builder, indent + 1).Append("option allow_alias = true;");
+                    }
+
+                    bool haveWrittenZero = false;
+                    // write zero values **first**
+                    foreach (ValueMember member in fieldsArr)
+                    {
+                        if (member.FieldNumber == 0)
+                        {
+                            NewLine(builder, indent + 1).Append(member.Name).Append(" = ").Append(member.FieldNumber).Append(';');
+                            haveWrittenZero = true;
+                        }
+                    }
+                    if (syntax == ProtoSyntax.Proto3 && !haveWrittenZero)
+                    {
+                        NewLine(builder, indent + 1).Append("ZERO = 0; // proto3 requires a zero value as the first item (it can be named anything)");
+                    }
+                    // note array is already sorted, so zero would already be first
+                    foreach (ValueMember member in fieldsArr)
+                    {
+                        if (member.FieldNumber == 0) continue;
+                        NewLine(builder, indent + 1).Append(member.Name).Append(" = ").Append(member.FieldNumber).Append(';');
+                    }
+                }
+                NewLine(builder, indent).Append('}');
+            }
+            else
+            {
+                NewLine(builder, indent).Append("message ").Append(GetSchemaTypeName()).Append(" {");
+                foreach (ValueMember member in fieldsArr)
+                {
+                    string schemaTypeName;
+                    bool hasOption = false;
+                    if (member.IsMap)
+                    {
+                        member.ResolveMapTypes(out var _, out var keyType, out var valueType);
+
+                        var keyTypeName = model.GetSchemaTypeName(keyType, member.MapKeyFormat, false, false, ref imports);
+                        schemaTypeName = model.GetSchemaTypeName(valueType, member.MapKeyFormat, member.AsReference, member.DynamicType, ref imports);
+                        NewLine(builder, indent + 1).Append("map<").Append(keyTypeName).Append(",").Append(schemaTypeName).Append("> ")
+                            .Append(member.Name).Append(" = ").Append(member.FieldNumber).Append(";");
+                    }
+                    else
+                    {
+                        string ordinality = member.ItemType != null ? "repeated " : (syntax == ProtoSyntax.Proto2 ? (member.IsRequired ? "required " : "optional ") : "");
+                        NewLine(builder, indent + 1).Append(ordinality);
+                        if (member.DataFormat == DataFormat.Group) builder.Append("group ");
+                        schemaTypeName = member.GetSchemaTypeName(true, ref imports);
+                        builder.Append(schemaTypeName).Append(" ")
+                             .Append(member.Name).Append(" = ").Append(member.FieldNumber);
+
+                        if (syntax == ProtoSyntax.Proto2 && member.DefaultValue != null && member.IsRequired == false)
+                        {
+                            if (member.DefaultValue is string)
+                            {
+                                AddOption(builder, ref hasOption).Append("default = \"").Append(member.DefaultValue).Append("\"");
+                            }
+                            else if (member.DefaultValue is TimeSpan)
+                            {
+                                // ignore
+                            }
+                            else if (member.DefaultValue is bool)
+                            {   // need to be lower case (issue 304)
+                                AddOption(builder, ref hasOption).Append((bool)member.DefaultValue ? "default = true" : "default = false");
+                            }
+                            else
+                            {
+                                AddOption(builder, ref hasOption).Append("default = ").Append(member.DefaultValue);
+                            }
+                        }
+                        if (CanPack(member.ItemType))
+                        {
+                            if (syntax == ProtoSyntax.Proto2)
+                            {
+                                if (member.IsPacked) AddOption(builder, ref hasOption).Append("packed = true"); // disabled by default
+                            }
+                            else
+                            {
+                                if (!member.IsPacked) AddOption(builder, ref hasOption).Append("packed = false"); // enabled by default
+                            }
+                        }
+                        if (member.AsReference)
+                        {
+                            imports |= RuntimeTypeModel.CommonImports.Protogen;
+                            AddOption(builder, ref hasOption).Append("(.protobuf_net.fieldopt).asRef = true");
+                        }
+                        if (member.DynamicType)
+                        {
+                            imports |= RuntimeTypeModel.CommonImports.Protogen;
+                            AddOption(builder, ref hasOption).Append("(.protobuf_net.fieldopt).dynamicType = true");
+                        }
+                        CloseOption(builder, ref hasOption).Append(';');
+                        if (syntax != ProtoSyntax.Proto2 && member.DefaultValue != null && !member.IsRequired)
+                        {
+                            if (IsImplicitDefault(member.DefaultValue))
+                            {
+                                // don't emit; we're good
+                            }
+                            else
+                            {
+                                builder.Append(" // default value could not be applied: ").Append(member.DefaultValue);
+                            }
+                        }
+                    }
+                    if (schemaTypeName == ".bcl.NetObjectProxy" && member.AsReference && !member.DynamicType) // we know what it is; tell the user
+                    {
+                        builder.Append(" // reference-tracked ").Append(member.GetSchemaTypeName(false, ref imports));
+                    }
+                }
+                if (subTypes != null && subTypes.Count != 0)
+                {
+                    SubType[] subTypeArr = new SubType[subTypes.Count];
+                    subTypes.CopyTo(subTypeArr, 0);
+                    Array.Sort(subTypeArr, SubType.Comparer.Default);
+                    string[] fieldNames = new string[subTypeArr.Length];
+                    for(int i = 0; i < subTypeArr.Length;i++)
+                        fieldNames[i] = subTypeArr[i].DerivedType.GetSchemaTypeName();
+
+                    string fieldName = "subtype";
+                    while (Array.IndexOf(fieldNames, fieldName) >= 0)
+                        fieldName = "_" + fieldName;
+
+                    NewLine(builder, indent + 1).Append("oneof ").Append(fieldName).Append(" {");
+                    for(int i = 0; i < subTypeArr.Length; i++)
+                    {
+                        var subTypeName = fieldNames[i];
+                        NewLine(builder, indent + 2).Append(subTypeName)
+                               .Append(" ").Append(subTypeName).Append(" = ").Append(subTypeArr[i].FieldNumber).Append(';');
+                    }
+                    NewLine(builder, indent + 1).Append("}");
+                }
+                NewLine(builder, indent).Append('}');
+            }
+        }
+
+        private static StringBuilder AddOption(StringBuilder builder, ref bool hasOption)
+        {
+            if (hasOption)
+                return builder.Append(", ");
+            hasOption = true;
+            return builder.Append(" [");
+        }
+
+        private static StringBuilder CloseOption(StringBuilder builder, ref bool hasOption)
+        {
+            if (hasOption)
+            {
+                hasOption = false;
+                return builder.Append("]");
+            }
+            return builder;
+        }
+
+        private static bool IsImplicitDefault(object value)
+        {
+            try
+            {
+                if (value == null) return false;
+                switch (Helpers.GetTypeCode(value.GetType()))
+                {
+                    case ProtoTypeCode.Boolean: return ((bool)value) == false;
+                    case ProtoTypeCode.Byte: return ((byte)value) == (byte)0;
+                    case ProtoTypeCode.Char: return ((char)value) == (char)0;
+                    case ProtoTypeCode.DateTime: return ((DateTime)value) == default;
+                    case ProtoTypeCode.Decimal: return ((decimal)value) == 0M;
+                    case ProtoTypeCode.Double: return ((double)value) == (double)0;
+                    case ProtoTypeCode.Int16: return ((short)value) == (short)0;
+                    case ProtoTypeCode.Int32: return ((int)value) == (int)0;
+                    case ProtoTypeCode.Int64: return ((long)value) == (long)0;
+                    case ProtoTypeCode.SByte: return ((sbyte)value) == (sbyte)0;
+                    case ProtoTypeCode.Single: return ((float)value) == (float)0;
+                    case ProtoTypeCode.String: return ((string)value) == "";
+                    case ProtoTypeCode.TimeSpan: return ((TimeSpan)value) == TimeSpan.Zero;
+                    case ProtoTypeCode.UInt16: return ((ushort)value) == (ushort)0;
+                    case ProtoTypeCode.UInt32: return ((uint)value) == (uint)0;
+                    case ProtoTypeCode.UInt64: return ((ulong)value) == (ulong)0;
+                }
+            }
+            catch { }
+            return false;
+        }
+
+        private static bool CanPack(Type type)
+        {
+            if (type == null) return false;
+            switch (Helpers.GetTypeCode(type))
+            {
+                case ProtoTypeCode.Boolean:
+                case ProtoTypeCode.Byte:
+                case ProtoTypeCode.Char:
+                case ProtoTypeCode.Double:
+                case ProtoTypeCode.Int16:
+                case ProtoTypeCode.Int32:
+                case ProtoTypeCode.Int64:
+                case ProtoTypeCode.SByte:
+                case ProtoTypeCode.Single:
+                case ProtoTypeCode.UInt16:
+                case ProtoTypeCode.UInt32:
+                case ProtoTypeCode.UInt64:
+                    return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Apply a shift to all fields (and sub-types) on this type
+        /// </summary>
+        /// <param name="offset">The change in field number to apply</param>
+        /// <remarks>The resultant field numbers must still all be considered valid</remarks>
+#if !(NETSTANDARD1_0 || NETSTANDARD1_3 || UAP)
+        [System.ComponentModel.Browsable(false)]
+#endif
+        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)]
+        public void ApplyFieldOffset(int offset)
+        {
+            if (Helpers.IsEnum(type)) throw new InvalidOperationException("Cannot apply field-offset to an enum");
+            if (offset == 0) return; // nothing to do
+            int opaqueToken = 0;
+            try
+            {
+                model.TakeLock(ref opaqueToken);
+                ThrowIfFrozen();
+
+                if (fields != null)
+                {
+                    foreach(ValueMember field in fields)
+                        AssertValidFieldNumber(field.FieldNumber + offset);
+                }
+                if (subTypes != null)
+                {
+                    foreach (SubType subType in subTypes)
+                        AssertValidFieldNumber(subType.FieldNumber + offset);
+                }
+
+                // we've checked the ranges are all OK; since we're moving everything, we can't overlap ourselves
+                // so: we can just move
+                if (fields != null)
+                {
+                    foreach (ValueMember field in fields)
+                        field.FieldNumber += offset;
+                }
+                if (subTypes != null)
+                {
+                    foreach (SubType subType in subTypes)
+                        subType.FieldNumber += offset;
+                }
+            }
+            finally
+            {
+                model.ReleaseLock(opaqueToken);
+            }
+        }
+
+        internal static void AssertValidFieldNumber(int fieldNumber)
+        {
+            if (fieldNumber < 1) throw new ArgumentOutOfRangeException(nameof(fieldNumber));
+        }
+    }
+}
+#endif

+ 17 - 0
Server/ThirdParty/protobuf-net/Meta/ProtoSyntax.cs

@@ -0,0 +1,17 @@
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Indiate the variant of the protobuf .proto DSL syntax to use
+    /// </summary>
+    public enum ProtoSyntax
+    {
+        /// <summary>
+        /// https://developers.google.com/protocol-buffers/docs/proto
+        /// </summary>
+        Proto2 = 0,
+        /// <summary>
+        /// https://developers.google.com/protocol-buffers/docs/proto3
+        /// </summary>
+        Proto3 = 1,
+    }
+}

+ 2006 - 0
Server/ThirdParty/protobuf-net/Meta/RuntimeTypeModel.cs

@@ -0,0 +1,2006 @@
+#if !NO_RUNTIME
+using System;
+using System.Collections;
+using System.Text;
+using System.Reflection;
+#if FEAT_COMPILER
+using System.Reflection.Emit;
+#endif
+
+using ProtoBuf.Serializers;
+using System.Threading;
+using System.IO;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Provides protobuf serialization support for a number of types that can be defined at runtime
+    /// </summary>
+    public sealed class RuntimeTypeModel : TypeModel
+    {
+        private ushort options;
+        private const ushort
+           OPTIONS_InferTagFromNameDefault = 1,
+           OPTIONS_IsDefaultModel = 2,
+           OPTIONS_Frozen = 4,
+           OPTIONS_AutoAddMissingTypes = 8,
+#if FEAT_COMPILER
+           OPTIONS_AutoCompile = 16,
+#endif
+           OPTIONS_UseImplicitZeroDefaults = 32,
+           OPTIONS_AllowParseableTypes = 64,
+           OPTIONS_AutoAddProtoContractTypesOnly = 128,
+           OPTIONS_IncludeDateTimeKind = 256,
+           OPTIONS_DoNotInternStrings = 512;
+
+        private bool GetOption(ushort option)
+        {
+            return (options & option) == option;
+        }
+
+        private void SetOption(ushort option, bool value)
+        {
+            if (value) options |= option;
+            else options &= (ushort)~option;
+        }
+
+        /// <summary>
+        /// Global default that
+        /// enables/disables automatic tag generation based on the existing name / order
+        /// of the defined members. See <seealso cref="ProtoContractAttribute.InferTagFromName"/>
+        /// for usage and <b>important warning</b> / explanation.
+        /// You must set the global default before attempting to serialize/deserialize any
+        /// impacted type.
+        /// </summary>
+        public bool InferTagFromNameDefault
+        {
+            get { return GetOption(OPTIONS_InferTagFromNameDefault); }
+            set { SetOption(OPTIONS_InferTagFromNameDefault, value); }
+        }
+
+        /// <summary>
+        /// Global default that determines whether types are considered serializable
+        /// if they have [DataContract] / [XmlType]. With this enabled, <b>ONLY</b>
+        /// types marked as [ProtoContract] are added automatically.
+        /// </summary>
+        public bool AutoAddProtoContractTypesOnly
+        {
+            get { return GetOption(OPTIONS_AutoAddProtoContractTypesOnly); }
+            set { SetOption(OPTIONS_AutoAddProtoContractTypesOnly, value); }
+        }
+
+        /// <summary>
+        /// Global switch that enables or disables the implicit
+        /// handling of "zero defaults"; meanning: if no other default is specified,
+        /// it assumes bools always default to false, integers to zero, etc.
+        /// 
+        /// If this is disabled, no such assumptions are made and only *explicit*
+        /// default values are processed. This is enabled by default to 
+        /// preserve similar logic to v1.
+        /// </summary>
+        public bool UseImplicitZeroDefaults
+        {
+            get { return GetOption(OPTIONS_UseImplicitZeroDefaults); }
+            set
+            {
+                if (!value && GetOption(OPTIONS_IsDefaultModel))
+                {
+                    throw new InvalidOperationException("UseImplicitZeroDefaults cannot be disabled on the default model");
+                }
+                SetOption(OPTIONS_UseImplicitZeroDefaults, value);
+            }
+        }
+
+        /// <summary>
+        /// Global switch that determines whether types with a <c>.ToString()</c> and a <c>Parse(string)</c>
+        /// should be serialized as strings.
+        /// </summary>
+        public bool AllowParseableTypes
+        {
+            get { return GetOption(OPTIONS_AllowParseableTypes); }
+            set { SetOption(OPTIONS_AllowParseableTypes, value); }
+        }
+
+        /// <summary>
+        /// Global switch that determines whether DateTime serialization should include the <c>Kind</c> of the date/time.
+        /// </summary>
+        public bool IncludeDateTimeKind
+        {
+            get { return GetOption(OPTIONS_IncludeDateTimeKind); }
+            set { SetOption(OPTIONS_IncludeDateTimeKind, value); }
+        }
+
+        /// <summary>
+        /// Global switch that determines whether a single instance of the same string should be used during deserialization.
+        /// </summary>
+        /// <remarks>Note this does not use the global .NET string interner</remarks>
+        public bool InternStrings
+        {
+            get { return !GetOption(OPTIONS_DoNotInternStrings); }
+            set { SetOption(OPTIONS_DoNotInternStrings, !value); }
+        }
+
+        /// <summary>
+        /// Should the <c>Kind</c> be included on date/time values?
+        /// </summary>
+        protected internal override bool SerializeDateTimeKind()
+        {
+            return GetOption(OPTIONS_IncludeDateTimeKind);
+        }
+
+        private sealed class Singleton
+        {
+            private Singleton() { }
+            internal static readonly RuntimeTypeModel Value = new RuntimeTypeModel(true);
+        }
+
+        /// <summary>
+        /// The default model, used to support ProtoBuf.Serializer
+        /// </summary>
+        public static RuntimeTypeModel Default => Singleton.Value;
+
+        /// <summary>
+        /// Returns a sequence of the Type instances that can be
+        /// processed by this model.
+        /// </summary>
+        public IEnumerable GetTypes() => types;
+
+        /// <summary>
+        /// Suggest a .proto definition for the given type
+        /// </summary>
+        /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param>
+        /// <returns>The .proto definition as a string</returns>
+        /// <param name="syntax">The .proto syntax to use</param>
+        public override string GetSchema(Type type, ProtoSyntax syntax)
+        {
+            BasicList requiredTypes = new BasicList();
+            MetaType primaryType = null;
+            bool isInbuiltType = false;
+            if (type == null)
+            { // generate for the entire model
+                foreach (MetaType meta in types)
+                {
+                    MetaType tmp = meta.GetSurrogateOrBaseOrSelf(false);
+                    if (!requiredTypes.Contains(tmp))
+                    { // ^^^ note that the type might have been added as a descendent
+                        requiredTypes.Add(tmp);
+                        CascadeDependents(requiredTypes, tmp);
+                    }
+                }
+            }
+            else
+            {
+                Type tmp = Helpers.GetUnderlyingType(type);
+                if (tmp != null) type = tmp;
+
+                WireType defaultWireType;
+                isInbuiltType = (ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, false, false, false, false) != null);
+                if (!isInbuiltType)
+                {
+                    //Agenerate just relative to the supplied type
+                    int index = FindOrAddAuto(type, false, false, false);
+                    if (index < 0) throw new ArgumentException("The type specified is not a contract-type", "type");
+
+                    // get the required types
+                    primaryType = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
+                    requiredTypes.Add(primaryType);
+                    CascadeDependents(requiredTypes, primaryType);
+                }
+            }
+
+            // use the provided type's namespace for the "package"
+            StringBuilder headerBuilder = new StringBuilder();
+            string package = null;
+
+            if (!isInbuiltType)
+            {
+                IEnumerable typesForNamespace = primaryType == null ? types : requiredTypes;
+                foreach (MetaType meta in typesForNamespace)
+                {
+                    if (meta.IsList) continue;
+                    string tmp = meta.Type.Namespace;
+                    if (!string.IsNullOrEmpty(tmp))
+                    {
+                        if (tmp.StartsWith("System.")) continue;
+                        if (package == null)
+                        { // haven't seen any suggestions yet
+                            package = tmp;
+                        }
+                        else if (package == tmp)
+                        { // that's fine; a repeat of the one we already saw
+                        }
+                        else
+                        { // something else; have confliucting suggestions; abort
+                            package = null;
+                            break;
+                        }
+                    }
+                }
+            }
+            switch (syntax)
+            {
+                case ProtoSyntax.Proto2:
+                    headerBuilder.AppendLine(@"syntax = ""proto2"";");
+                    break;
+                case ProtoSyntax.Proto3:
+                    headerBuilder.AppendLine(@"syntax = ""proto3"";");
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(syntax));
+            }
+
+            if (!string.IsNullOrEmpty(package))
+            {
+                headerBuilder.Append("package ").Append(package).Append(';');
+                Helpers.AppendLine(headerBuilder);
+            }
+
+            var imports = CommonImports.None;
+            StringBuilder bodyBuilder = new StringBuilder();
+            // sort them by schema-name
+            MetaType[] metaTypesArr = new MetaType[requiredTypes.Count];
+            requiredTypes.CopyTo(metaTypesArr, 0);
+            Array.Sort(metaTypesArr, MetaType.Comparer.Default);
+
+            // write the messages
+            if (isInbuiltType)
+            {
+                Helpers.AppendLine(bodyBuilder).Append("message ").Append(type.Name).Append(" {");
+                MetaType.NewLine(bodyBuilder, 1).Append(syntax == ProtoSyntax.Proto2 ? "optional " : "").Append(GetSchemaTypeName(type, DataFormat.Default, false, false, ref imports))
+                    .Append(" value = 1;");
+                Helpers.AppendLine(bodyBuilder).Append('}');
+            }
+            else
+            {
+                for (int i = 0; i < metaTypesArr.Length; i++)
+                {
+                    MetaType tmp = metaTypesArr[i];
+                    if (tmp.IsList && tmp != primaryType) continue;
+                    tmp.WriteSchema(bodyBuilder, 0, ref imports, syntax);
+                }
+            }
+            if ((imports & CommonImports.Bcl) != 0)
+            {
+                headerBuilder.Append("import \"protobuf-net/bcl.proto\"; // schema for protobuf-net's handling of core .NET types");
+                Helpers.AppendLine(headerBuilder);
+            }
+            if ((imports & CommonImports.Protogen) != 0)
+            {
+                headerBuilder.Append("import \"protobuf-net/protogen.proto\"; // custom protobuf-net options");
+                Helpers.AppendLine(headerBuilder);
+            }
+            if ((imports & CommonImports.Timestamp) != 0)
+            {
+                headerBuilder.Append("import \"google/protobuf/timestamp.proto\";");
+                Helpers.AppendLine(headerBuilder);
+            }
+            if ((imports & CommonImports.Duration) != 0)
+            {
+                headerBuilder.Append("import \"google/protobuf/duration.proto\";");
+                Helpers.AppendLine(headerBuilder);
+            }
+            return Helpers.AppendLine(headerBuilder.Append(bodyBuilder)).ToString();
+        }
+        [Flags]
+        internal enum CommonImports
+        {
+            None = 0,
+            Bcl = 1,
+            Timestamp = 2,
+            Duration = 4,
+            Protogen = 8
+        }
+        private void CascadeDependents(BasicList list, MetaType metaType)
+        {
+            MetaType tmp;
+            if (metaType.IsList)
+            {
+                Type itemType = TypeModel.GetListItemType(this, metaType.Type);
+                TryGetCoreSerializer(list, itemType);
+            }
+            else
+            {
+                if (metaType.IsAutoTuple)
+                {
+                    MemberInfo[] mapping;
+                    if (MetaType.ResolveTupleConstructor(metaType.Type, out mapping) != null)
+                    {
+                        for (int i = 0; i < mapping.Length; i++)
+                        {
+                            Type type = null;
+                            if (mapping[i] is PropertyInfo) type = ((PropertyInfo)mapping[i]).PropertyType;
+                            else if (mapping[i] is FieldInfo) type = ((FieldInfo)mapping[i]).FieldType;
+                            TryGetCoreSerializer(list, type);
+                        }
+                    }
+                }
+                else
+                {
+                    foreach (ValueMember member in metaType.Fields)
+                    {
+                        Type type = member.ItemType;
+                        if (member.IsMap)
+                        {
+                            member.ResolveMapTypes(out _, out _, out type); // don't need key-type
+                        }
+                        if (type == null) type = member.MemberType;
+                        TryGetCoreSerializer(list, type);
+                    }
+                }
+                foreach (var genericArgument in metaType.GetAllGenericArguments())
+                {
+                    TryGetCoreSerializer(list, genericArgument);
+                }
+                if (metaType.HasSubtypes)
+                {
+                    foreach (SubType subType in metaType.GetSubtypes())
+                    {
+                        tmp = subType.DerivedType.GetSurrogateOrSelf(); // note: exclude base-types!
+                        if (!list.Contains(tmp))
+                        {
+                            list.Add(tmp);
+                            CascadeDependents(list, tmp);
+                        }
+                    }
+                }
+                tmp = metaType.BaseType;
+                if (tmp != null) tmp = tmp.GetSurrogateOrSelf(); // note: already walking base-types; exclude base
+                if (tmp != null && !list.Contains(tmp))
+                {
+                    list.Add(tmp);
+                    CascadeDependents(list, tmp);
+                }
+            }
+        }
+
+        private void TryGetCoreSerializer(BasicList list, Type itemType)
+        {
+            var coreSerializer = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, itemType, out _, false, false, false, false);
+            if (coreSerializer != null)
+            {
+                return;
+            }
+            int index = FindOrAddAuto(itemType, false, false, false);
+            if (index < 0)
+            {
+                return;
+            }
+            var temp = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
+            if (list.Contains(temp))
+            {
+                return;
+            }
+            // could perhaps also implement as a queue, but this should work OK for sane models
+            list.Add(temp);
+            CascadeDependents(list, temp);
+        }
+
+#if !NO_RUNTIME
+        /// <summary>
+        /// Creates a new runtime model, to which the caller
+        /// can add support for a range of types. A model
+        /// can be used "as is", or can be compiled for
+        /// optimal performance.
+        /// </summary>
+        /// <param name="name">not used currently; this is for compatibility with v3</param>
+        public static RuntimeTypeModel Create(string name = null)
+        {
+            return new RuntimeTypeModel(false);
+        }
+#endif
+
+        private RuntimeTypeModel(bool isDefault)
+        {
+            AutoAddMissingTypes = true;
+            UseImplicitZeroDefaults = true;
+            SetOption(OPTIONS_IsDefaultModel, isDefault);
+#if FEAT_COMPILER && !DEBUG
+            try
+            {
+                AutoCompile = EnableAutoCompile();
+            }
+            catch { } // this is all kinds of brittle on things like UWP
+#endif
+        }
+
+#if FEAT_COMPILER
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        internal static bool EnableAutoCompile()
+        {
+            try
+            {
+                var dm = new DynamicMethod("CheckCompilerAvailable", typeof(bool), new Type[] { typeof(int) });
+                var il = dm.GetILGenerator();
+                il.Emit(OpCodes.Ldarg_0);
+                il.Emit(OpCodes.Ldc_I4, 42);
+                il.Emit(OpCodes.Ceq);
+                il.Emit(OpCodes.Ret);
+                var func = (Predicate<int>)dm.CreateDelegate(typeof(Predicate<int>));
+                return func(42);
+            }
+            catch (Exception ex)
+            {
+                Debug.WriteLine(ex);
+                return false;
+            }
+        }
+#endif
+
+        /// <summary>
+        /// Obtains the MetaType associated with a given Type for the current model,
+        /// allowing additional configuration.
+        /// </summary>
+        public MetaType this[Type type] { get { return (MetaType)types[FindOrAddAuto(type, true, false, false)]; } }
+
+        internal MetaType FindWithoutAdd(Type type)
+        {
+            // this list is thread-safe for reading
+            foreach (MetaType metaType in types)
+            {
+                if (metaType.Type == type)
+                {
+                    if (metaType.Pending) WaitOnLock(metaType);
+                    return metaType;
+                }
+            }
+            // if that failed, check for a proxy
+            Type underlyingType = ResolveProxies(type);
+            return underlyingType == null ? null : FindWithoutAdd(underlyingType);
+        }
+
+        static readonly BasicList.MatchPredicate
+            MetaTypeFinder = new BasicList.MatchPredicate(MetaTypeFinderImpl),
+            BasicTypeFinder = new BasicList.MatchPredicate(BasicTypeFinderImpl);
+
+        static bool MetaTypeFinderImpl(object value, object ctx)
+        {
+            return ((MetaType)value).Type == (Type)ctx;
+        }
+
+        static bool BasicTypeFinderImpl(object value, object ctx)
+        {
+            return ((BasicType)value).Type == (Type)ctx;
+        }
+
+        private void WaitOnLock(MetaType type)
+        {
+            int opaqueToken = 0;
+            try
+            {
+                TakeLock(ref opaqueToken);
+            }
+            finally
+            {
+                ReleaseLock(opaqueToken);
+            }
+        }
+
+        BasicList basicTypes = new BasicList();
+
+        sealed class BasicType
+        {
+            private readonly Type type;
+            public Type Type => type;
+            private readonly IProtoSerializer serializer;
+            public IProtoSerializer Serializer => serializer;
+
+            public BasicType(Type type, IProtoSerializer serializer)
+            {
+                this.type = type;
+                this.serializer = serializer;
+            }
+        }
+        internal IProtoSerializer TryGetBasicTypeSerializer(Type type)
+        {
+            int idx = basicTypes.IndexOf(BasicTypeFinder, type);
+
+            if (idx >= 0) return ((BasicType)basicTypes[idx]).Serializer;
+
+            lock (basicTypes)
+            { // don't need a full model lock for this
+
+                // double-checked
+                idx = basicTypes.IndexOf(BasicTypeFinder, type);
+                if (idx >= 0) return ((BasicType)basicTypes[idx]).Serializer;
+
+                MetaType.AttributeFamily family = MetaType.GetContractFamily(this, type, null);
+                IProtoSerializer ser = family == MetaType.AttributeFamily.None
+                    ? ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out WireType defaultWireType, false, false, false, false)
+                    : null;
+
+                if (ser != null) basicTypes.Add(new BasicType(type, ser));
+                return ser;
+            }
+        }
+
+        internal int FindOrAddAuto(Type type, bool demand, bool addWithContractOnly, bool addEvenIfAutoDisabled)
+        {
+            int key = types.IndexOf(MetaTypeFinder, type);
+            MetaType metaType;
+
+            // the fast happy path: meta-types we've already seen
+            if (key >= 0)
+            {
+                metaType = (MetaType)types[key];
+                if (metaType.Pending)
+                {
+                    WaitOnLock(metaType);
+                }
+                return key;
+            }
+
+            // the fast fail path: types that will never have a meta-type
+            bool shouldAdd = AutoAddMissingTypes || addEvenIfAutoDisabled;
+
+            if (!Helpers.IsEnum(type) && TryGetBasicTypeSerializer(type) != null)
+            {
+                if (shouldAdd && !addWithContractOnly) throw MetaType.InbuiltType(type);
+                return -1; // this will never be a meta-type
+            }
+
+            // otherwise: we don't yet know
+
+            // check for proxy types
+            Type underlyingType = ResolveProxies(type);
+            if (underlyingType != null && underlyingType != type)
+            {
+                key = types.IndexOf(MetaTypeFinder, underlyingType);
+                type = underlyingType; // if new added, make it reflect the underlying type
+            }
+
+            if (key < 0)
+            {
+                int opaqueToken = 0;
+                Type origType = type;
+                bool weAdded = false;
+                try
+                {
+                    TakeLock(ref opaqueToken);
+                    // try to recognise a few familiar patterns...
+                    if ((metaType = RecogniseCommonTypes(type)) == null)
+                    { // otherwise, check if it is a contract
+                        MetaType.AttributeFamily family = MetaType.GetContractFamily(this, type, null);
+                        if (family == MetaType.AttributeFamily.AutoTuple)
+                        {
+                            shouldAdd = addEvenIfAutoDisabled = true; // always add basic tuples, such as KeyValuePair
+                        }
+
+                        if (!shouldAdd || (
+                            !Helpers.IsEnum(type) && addWithContractOnly && family == MetaType.AttributeFamily.None)
+                            )
+                        {
+                            if (demand) ThrowUnexpectedType(type);
+                            return key;
+                        }
+                        metaType = Create(type);
+                    }
+
+                    metaType.Pending = true;                    
+                    
+                    // double-checked
+                    int winner = types.IndexOf(MetaTypeFinder, type);
+                    if (winner < 0)
+                    {
+                        ThrowIfFrozen();
+                        key = types.Add(metaType);
+                        weAdded = true;
+                    }
+                    else
+                    {
+                        key = winner;
+                    }
+                    if (weAdded)
+                    {
+                        metaType.ApplyDefaultBehaviour();
+                        metaType.Pending = false;
+                    }
+                }
+                finally
+                {
+                    ReleaseLock(opaqueToken);
+                    if (weAdded)
+                    {
+                        ResetKeyCache();
+                    }
+                }
+            }
+            return key;
+        }
+
+        private MetaType RecogniseCommonTypes(Type type)
+        {
+            //            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>))
+            //            {
+            //                MetaType mt = new MetaType(this, type);
+
+            //                Type surrogate = typeof (KeyValuePairSurrogate<,>).MakeGenericType(type.GetGenericArguments());
+
+            //                mt.SetSurrogate(surrogate);
+            //                mt.IncludeSerializerMethod = false;
+            //                mt.Freeze();
+
+            //                MetaType surrogateMeta = (MetaType)types[FindOrAddAuto(surrogate, true, true, true)]; // this forcibly adds it if needed
+            //                if(surrogateMeta.IncludeSerializerMethod)
+            //                { // don't blindly set - it might be frozen
+            //                    surrogateMeta.IncludeSerializerMethod = false;
+            //                }
+            //                surrogateMeta.Freeze();
+            //                return mt;
+            //            }
+            return null;
+        }
+        private MetaType Create(Type type)
+        {
+            ThrowIfFrozen();
+            return new MetaType(this, type, defaultFactory);
+        }
+
+        /// <summary>
+        /// Adds support for an additional type in this model, optionally
+        /// applying inbuilt patterns. If the type is already known to the
+        /// model, the existing type is returned **without** applying
+        /// any additional behaviour.
+        /// </summary>
+        /// <remarks>Inbuilt patterns include:
+        /// [ProtoContract]/[ProtoMember(n)]
+        /// [DataContract]/[DataMember(Order=n)]
+        /// [XmlType]/[XmlElement(Order=n)]
+        /// [On{Des|S}erializ{ing|ed}]
+        /// ShouldSerialize*/*Specified
+        /// </remarks>
+        /// <param name="type">The type to be supported</param>
+        /// <param name="applyDefaultBehaviour">Whether to apply the inbuilt configuration patterns (via attributes etc), or
+        /// just add the type with no additional configuration (the type must then be manually configured).</param>
+        /// <returns>The MetaType representing this type, allowing
+        /// further configuration.</returns>
+        public MetaType Add(Type type, bool applyDefaultBehaviour)
+        {
+            if (type == null) throw new ArgumentNullException("type");
+            MetaType newType = FindWithoutAdd(type);
+            if (newType != null) return newType; // return existing
+            int opaqueToken = 0;
+
+#if COREFX || PROFILE259
+            TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(type);
+            if (typeInfo.IsInterface && MetaType.ienumerable.IsAssignableFrom(typeInfo)
+#else
+            if (type.IsInterface && MapType(MetaType.ienumerable).IsAssignableFrom(type)
+#endif
+                    && GetListItemType(this, type) == null)
+            {
+                throw new ArgumentException("IEnumerable[<T>] data cannot be used as a meta-type unless an Add method can be resolved");
+            }
+            try
+            {
+                newType = RecogniseCommonTypes(type);
+                if (newType != null)
+                {
+                    if (!applyDefaultBehaviour)
+                    {
+                        throw new ArgumentException(
+                            "Default behaviour must be observed for certain types with special handling; " + type.FullName,
+                            "applyDefaultBehaviour");
+                    }
+                    // we should assume that type is fully configured, though; no need to re-run:
+                    applyDefaultBehaviour = false;
+                }
+                if (newType == null) newType = Create(type);
+                newType.Pending = true;
+                TakeLock(ref opaqueToken);
+                // double checked
+                if (FindWithoutAdd(type) != null) throw new ArgumentException("Duplicate type", "type");
+                ThrowIfFrozen();
+                types.Add(newType);
+                if (applyDefaultBehaviour) { newType.ApplyDefaultBehaviour(); }
+                newType.Pending = false;
+            }
+            finally
+            {
+                ReleaseLock(opaqueToken);
+                ResetKeyCache();
+            }
+
+            return newType;
+        }
+
+#if FEAT_COMPILER
+        /// <summary>
+        /// Should serializers be compiled on demand? It may be useful
+        /// to disable this for debugging purposes.
+        /// </summary>
+        public bool AutoCompile
+        {
+            get { return GetOption(OPTIONS_AutoCompile); }
+            set { SetOption(OPTIONS_AutoCompile, value); }
+        }
+#endif
+        /// <summary>
+        /// Should support for unexpected types be added automatically?
+        /// If false, an exception is thrown when unexpected types
+        /// are encountered.
+        /// </summary>
+        public bool AutoAddMissingTypes
+        {
+            get { return GetOption(OPTIONS_AutoAddMissingTypes); }
+            set
+            {
+                if (!value && GetOption(OPTIONS_IsDefaultModel))
+                {
+                    throw new InvalidOperationException("The default model must allow missing types");
+                }
+                ThrowIfFrozen();
+                SetOption(OPTIONS_AutoAddMissingTypes, value);
+            }
+        }
+        /// <summary>
+        /// Verifies that the model is still open to changes; if not, an exception is thrown
+        /// </summary>
+        private void ThrowIfFrozen()
+        {
+            if (GetOption(OPTIONS_Frozen)) throw new InvalidOperationException("The model cannot be changed once frozen");
+        }
+
+        /// <summary>
+        /// Prevents further changes to this model
+        /// </summary>
+        public void Freeze()
+        {
+            if (GetOption(OPTIONS_IsDefaultModel)) throw new InvalidOperationException("The default model cannot be frozen");
+            SetOption(OPTIONS_Frozen, true);
+        }
+
+        private readonly BasicList types = new BasicList();
+
+        /// <summary>
+        /// Provides the key that represents a given type in the current model.
+        /// </summary>
+        protected override int GetKeyImpl(Type type)
+        {
+            return GetKey(type, false, true);
+        }
+
+        internal int GetKey(Type type, bool demand, bool getBaseKey)
+        {
+            Helpers.DebugAssert(type != null);
+            try
+            {
+                int typeIndex = FindOrAddAuto(type, demand, true, false);
+                if (typeIndex >= 0)
+                {
+                    MetaType mt = (MetaType)types[typeIndex];
+                    if (getBaseKey)
+                    {
+                        mt = MetaType.GetRootType(mt);
+                        typeIndex = FindOrAddAuto(mt.Type, true, true, false);
+                    }
+                }
+                return typeIndex;
+            }
+            catch (NotSupportedException)
+            {
+                throw; // re-surface "as-is"
+            }
+            catch (Exception ex)
+            {
+                if (ex.Message.IndexOf(type.FullName) >= 0) throw;  // already enough info
+                throw new ProtoException(ex.Message + " (" + type.FullName + ")", ex);
+            }
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream.
+        /// </summary>
+        /// <param name="key">Represents the type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        protected internal override void Serialize(int key, object value, ProtoWriter dest)
+        {
+            //Helpers.DebugWriteLine("Serialize", value);
+            ((MetaType)types[key]).Serializer.Write(value, dest);
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="key">Represents the type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        protected internal override object Deserialize(int key, object value, ProtoReader source)
+        {
+            //Helpers.DebugWriteLine("Deserialize", value);
+            IProtoSerializer ser = ((MetaType)types[key]).Serializer;
+            if (value == null && Helpers.IsValueType(ser.ExpectedType))
+            {
+                if (ser.RequiresOldValue) value = Activator.CreateInstance(ser.ExpectedType);
+                return ser.Read(value, source);
+            }
+            else
+            {
+                return ser.Read(value, source);
+            }
+        }
+
+#if FEAT_COMPILER
+        // this is used by some unit-tests; do not remove
+        internal Compiler.ProtoSerializer GetSerializer(IProtoSerializer serializer, bool compiled)
+        {
+            if (serializer == null) throw new ArgumentNullException("serializer");
+#if FEAT_COMPILER
+            if (compiled) return Compiler.CompilerContext.BuildSerializer(serializer, this);
+#endif
+            return new Compiler.ProtoSerializer(serializer.Write);
+        }
+
+        /// <summary>
+        /// Compiles the serializers individually; this is *not* a full
+        /// standalone compile, but can significantly boost performance
+        /// while allowing additional types to be added.
+        /// </summary>
+        /// <remarks>An in-place compile can access non-public types / members</remarks>
+        public void CompileInPlace()
+        {
+            foreach (MetaType type in types)
+            {
+                type.CompileInPlace();
+            }
+        }
+
+#endif
+        //internal override IProtoSerializer GetTypeSerializer(Type type)
+        //{   // this list is thread-safe for reading
+        //    .Serializer;
+        //}
+        //internal override IProtoSerializer GetTypeSerializer(int key)
+        //{   // this list is thread-safe for reading
+        //    MetaType type = (MetaType)types.TryGet(key);
+        //    if (type != null) return type.Serializer;
+        //    throw new KeyNotFoundException();
+
+        //}
+
+#if FEAT_COMPILER
+        private void BuildAllSerializers()
+        {
+            // note that types.Count may increase during this operation, as some serializers
+            // bring other types into play
+            for (int i = 0; i < types.Count; i++)
+            {
+                // the primary purpose of this is to force the creation of the Serializer
+                MetaType mt = (MetaType)types[i];
+                if (mt.Serializer == null)
+                    throw new InvalidOperationException("No serializer available for " + mt.Type.Name);
+            }
+        }
+
+        internal sealed class SerializerPair : IComparable
+        {
+            int IComparable.CompareTo(object obj)
+            {
+                if (obj == null) throw new ArgumentException("obj");
+                SerializerPair other = (SerializerPair)obj;
+
+                // we want to bunch all the items with the same base-type together, but we need the items with a
+                // different base **first**.
+                if (this.BaseKey == this.MetaKey)
+                {
+                    if (other.BaseKey == other.MetaKey)
+                    { // neither is a subclass
+                        return this.MetaKey.CompareTo(other.MetaKey);
+                    }
+                    else
+                    { // "other" (only) is involved in inheritance; "other" should be first
+                        return 1;
+                    }
+                }
+                else
+                {
+                    if (other.BaseKey == other.MetaKey)
+                    { // "this" (only) is involved in inheritance; "this" should be first
+                        return -1;
+                    }
+                    else
+                    { // both are involved in inheritance
+                        int result = this.BaseKey.CompareTo(other.BaseKey);
+                        if (result == 0) result = this.MetaKey.CompareTo(other.MetaKey);
+                        return result;
+                    }
+                }
+            }
+            public readonly int MetaKey, BaseKey;
+            public readonly MetaType Type;
+            public readonly MethodBuilder Serialize, Deserialize;
+            public readonly ILGenerator SerializeBody, DeserializeBody;
+            public SerializerPair(int metaKey, int baseKey, MetaType type, MethodBuilder serialize, MethodBuilder deserialize,
+                ILGenerator serializeBody, ILGenerator deserializeBody)
+            {
+                this.MetaKey = metaKey;
+                this.BaseKey = baseKey;
+                this.Serialize = serialize;
+                this.Deserialize = deserialize;
+                this.SerializeBody = serializeBody;
+                this.DeserializeBody = deserializeBody;
+                this.Type = type;
+            }
+        }
+
+        /// <summary>
+        /// Fully compiles the current model into a static-compiled model instance
+        /// </summary>
+        /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
+        /// <returns>An instance of the newly created compiled type-model</returns>
+        public TypeModel Compile()
+        {
+            CompilerOptions options = new CompilerOptions();
+            return Compile(options);
+        }
+
+        static ILGenerator Override(TypeBuilder type, string name)
+        {
+            MethodInfo baseMethod = type.BaseType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance);
+
+            ParameterInfo[] parameters = baseMethod.GetParameters();
+            Type[] paramTypes = new Type[parameters.Length];
+            for (int i = 0; i < paramTypes.Length; i++)
+            {
+                paramTypes[i] = parameters[i].ParameterType;
+            }
+            MethodBuilder newMethod = type.DefineMethod(baseMethod.Name,
+                (baseMethod.Attributes & ~MethodAttributes.Abstract) | MethodAttributes.Final, baseMethod.CallingConvention, baseMethod.ReturnType, paramTypes);
+            ILGenerator il = newMethod.GetILGenerator();
+            type.DefineMethodOverride(newMethod, baseMethod);
+            return il;
+        }
+
+        /// <summary>
+        /// Represents configuration options for compiling a model to 
+        /// a standalone assembly.
+        /// </summary>
+        public sealed class CompilerOptions
+        {
+            /// <summary>
+            /// Import framework options from an existing type
+            /// </summary>
+            public void SetFrameworkOptions(MetaType from)
+            {
+                if (from == null) throw new ArgumentNullException("from");
+                AttributeMap[] attribs = AttributeMap.Create(from.Model, Helpers.GetAssembly(from.Type));
+                foreach (AttributeMap attrib in attribs)
+                {
+                    if (attrib.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute")
+                    {
+                        object tmp;
+                        if (attrib.TryGet("FrameworkName", out tmp)) TargetFrameworkName = (string)tmp;
+                        if (attrib.TryGet("FrameworkDisplayName", out tmp)) TargetFrameworkDisplayName = (string)tmp;
+                        break;
+                    }
+                }
+            }
+
+            private string targetFrameworkName, targetFrameworkDisplayName, typeName, outputPath, imageRuntimeVersion;
+            private int metaDataVersion;
+            /// <summary>
+            /// The TargetFrameworkAttribute FrameworkName value to burn into the generated assembly
+            /// </summary>
+            public string TargetFrameworkName { get { return targetFrameworkName; } set { targetFrameworkName = value; } }
+
+            /// <summary>
+            /// The TargetFrameworkAttribute FrameworkDisplayName value to burn into the generated assembly
+            /// </summary>
+            public string TargetFrameworkDisplayName { get { return targetFrameworkDisplayName; } set { targetFrameworkDisplayName = value; } }
+            /// <summary>
+            /// The name of the TypeModel class to create
+            /// </summary>
+            public string TypeName { get { return typeName; } set { typeName = value; } }
+
+#if COREFX
+            internal const string NoPersistence = "Assembly persistence not supported on this runtime";
+#endif
+            /// <summary>
+            /// The path for the new dll
+            /// </summary>
+#if COREFX
+            [Obsolete(NoPersistence)]
+#endif
+            public string OutputPath { get { return outputPath; } set { outputPath = value; } }
+            /// <summary>
+            /// The runtime version for the generated assembly
+            /// </summary>
+            public string ImageRuntimeVersion { get { return imageRuntimeVersion; } set { imageRuntimeVersion = value; } }
+            /// <summary>
+            /// The runtime version for the generated assembly
+            /// </summary>
+            public int MetaDataVersion { get { return metaDataVersion; } set { metaDataVersion = value; } }
+
+
+            private Accessibility accessibility = Accessibility.Public;
+            /// <summary>
+            /// The acecssibility of the generated serializer
+            /// </summary>
+            public Accessibility Accessibility { get { return accessibility; } set { accessibility = value; } }
+        }
+
+        /// <summary>
+        /// Type accessibility
+        /// </summary>
+        public enum Accessibility
+        {
+            /// <summary>
+            /// Available to all callers
+            /// </summary>
+            Public,
+            /// <summary>
+            /// Available to all callers in the same assembly, or assemblies specified via [InternalsVisibleTo(...)]
+            /// </summary>
+            Internal
+        }
+
+#if !COREFX
+        /// <summary>
+        /// Fully compiles the current model into a static-compiled serialization dll
+        /// (the serialization dll still requires protobuf-net for support services).
+        /// </summary>
+        /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
+        /// <param name="name">The name of the TypeModel class to create</param>
+        /// <param name="path">The path for the new dll</param>
+        /// <returns>An instance of the newly created compiled type-model</returns>
+        public TypeModel Compile(string name, string path)
+        {
+            CompilerOptions options = new CompilerOptions();
+            options.TypeName = name;
+            options.OutputPath = path;
+            return Compile(options);
+        }
+#endif
+        /// <summary>
+        /// Fully compiles the current model into a static-compiled serialization dll
+        /// (the serialization dll still requires protobuf-net for support services).
+        /// </summary>
+        /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
+        /// <returns>An instance of the newly created compiled type-model</returns>
+        public TypeModel Compile(CompilerOptions options)
+        {
+            if (options == null) throw new ArgumentNullException("options");
+            string typeName = options.TypeName;
+#pragma warning disable 0618
+            string path = options.OutputPath;
+#pragma warning restore 0618
+            BuildAllSerializers();
+            Freeze();
+            bool save = !string.IsNullOrEmpty(path);
+            if (string.IsNullOrEmpty(typeName))
+            {
+                if (save) throw new ArgumentNullException("typeName");
+                typeName = Guid.NewGuid().ToString();
+            }
+
+
+            string assemblyName, moduleName;
+            if (path == null)
+            {
+                assemblyName = typeName;
+                moduleName = assemblyName + ".dll";
+            }
+            else
+            {
+                assemblyName = new System.IO.FileInfo(System.IO.Path.GetFileNameWithoutExtension(path)).Name;
+                moduleName = assemblyName + System.IO.Path.GetExtension(path);
+            }
+
+#if COREFX
+            AssemblyName an = new AssemblyName();
+            an.Name = assemblyName;
+            AssemblyBuilder asm = AssemblyBuilder.DefineDynamicAssembly(an,
+                AssemblyBuilderAccess.Run);
+            ModuleBuilder module = asm.DefineDynamicModule(moduleName);
+#else
+            AssemblyName an = new AssemblyName();
+            an.Name = assemblyName;
+            AssemblyBuilder asm = AppDomain.CurrentDomain.DefineDynamicAssembly(an,
+                (save ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run)
+                );
+            ModuleBuilder module = save ? asm.DefineDynamicModule(moduleName, path)
+                                        : asm.DefineDynamicModule(moduleName);
+#endif
+
+            WriteAssemblyAttributes(options, assemblyName, asm);
+
+            TypeBuilder type = WriteBasicTypeModel(options, typeName, module);
+
+            int index;
+            bool hasInheritance;
+            SerializerPair[] methodPairs;
+            Compiler.CompilerContext.ILVersion ilVersion;
+            WriteSerializers(options, assemblyName, type, out index, out hasInheritance, out methodPairs, out ilVersion);
+
+            ILGenerator il;
+            int knownTypesCategory;
+            FieldBuilder knownTypes;
+            Type knownTypesLookupType;
+            WriteGetKeyImpl(type, hasInheritance, methodPairs, ilVersion, assemblyName, out il, out knownTypesCategory, out knownTypes, out knownTypesLookupType);
+
+            // trivial flags
+            il = Override(type, "SerializeDateTimeKind");
+            il.Emit(IncludeDateTimeKind ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
+            il.Emit(OpCodes.Ret);
+            // end: trivial flags
+
+            Compiler.CompilerContext ctx = WriteSerializeDeserialize(assemblyName, type, methodPairs, ilVersion, ref il);
+
+            WriteConstructors(type, ref index, methodPairs, ref il, knownTypesCategory, knownTypes, knownTypesLookupType, ctx);
+
+
+#if COREFX
+            Type finalType = type.CreateTypeInfo().AsType();
+#else
+            Type finalType = type.CreateType();
+#endif
+            if (!string.IsNullOrEmpty(path))
+            {
+#if COREFX
+                throw new NotSupportedException(CompilerOptions.NoPersistence);
+#else
+                try
+                {
+                    asm.Save(path);
+                }
+                catch (IOException ex)
+                {
+                    // advertise the file info
+                    throw new IOException(path + ", " + ex.Message, ex);
+                }
+                Helpers.DebugWriteLine("Wrote dll:" + path);
+#endif
+            }
+            return (TypeModel)Activator.CreateInstance(finalType);
+        }
+
+        private void WriteConstructors(TypeBuilder type, ref int index, SerializerPair[] methodPairs, ref ILGenerator il, int knownTypesCategory, FieldBuilder knownTypes, Type knownTypesLookupType, Compiler.CompilerContext ctx)
+        {
+            type.DefineDefaultConstructor(MethodAttributes.Public);
+            il = type.DefineTypeInitializer().GetILGenerator();
+            switch (knownTypesCategory)
+            {
+                case KnownTypes_Array:
+                    {
+                        Compiler.CompilerContext.LoadValue(il, types.Count);
+                        il.Emit(OpCodes.Newarr, ctx.MapType(typeof(System.Type)));
+                        index = 0;
+                        foreach (SerializerPair pair in methodPairs)
+                        {
+                            il.Emit(OpCodes.Dup);
+                            Compiler.CompilerContext.LoadValue(il, index);
+                            il.Emit(OpCodes.Ldtoken, pair.Type.Type);
+                            il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
+                            il.Emit(OpCodes.Stelem_Ref);
+                            index++;
+                        }
+                        il.Emit(OpCodes.Stsfld, knownTypes);
+                        il.Emit(OpCodes.Ret);
+                    }
+                    break;
+                case KnownTypes_Dictionary:
+                    {
+                        Compiler.CompilerContext.LoadValue(il, types.Count);
+                        //LocalBuilder loc = il.DeclareLocal(knownTypesLookupType);
+                        il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) }));
+                        il.Emit(OpCodes.Stsfld, knownTypes);
+                        int typeIndex = 0;
+                        foreach (SerializerPair pair in methodPairs)
+                        {
+                            il.Emit(OpCodes.Ldsfld, knownTypes);
+                            il.Emit(OpCodes.Ldtoken, pair.Type.Type);
+                            il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
+                            int keyIndex = typeIndex++, lastKey = pair.BaseKey;
+                            if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type
+                            {
+                                keyIndex = -1; // assume epic fail
+                                for (int j = 0; j < methodPairs.Length; j++)
+                                {
+                                    if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
+                                    {
+                                        keyIndex = j;
+                                        break;
+                                    }
+                                }
+                            }
+                            Compiler.CompilerContext.LoadValue(il, keyIndex);
+                            il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(System.Type)), MapType(typeof(int)) }), null);
+                        }
+                        il.Emit(OpCodes.Ret);
+                    }
+                    break;
+                case KnownTypes_Hashtable:
+                    {
+                        Compiler.CompilerContext.LoadValue(il, types.Count);
+                        il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) }));
+                        il.Emit(OpCodes.Stsfld, knownTypes);
+                        int typeIndex = 0;
+                        foreach (SerializerPair pair in methodPairs)
+                        {
+                            il.Emit(OpCodes.Ldsfld, knownTypes);
+                            il.Emit(OpCodes.Ldtoken, pair.Type.Type);
+                            il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
+                            int keyIndex = typeIndex++, lastKey = pair.BaseKey;
+                            if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type
+                            {
+                                keyIndex = -1; // assume epic fail
+                                for (int j = 0; j < methodPairs.Length; j++)
+                                {
+                                    if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
+                                    {
+                                        keyIndex = j;
+                                        break;
+                                    }
+                                }
+                            }
+                            Compiler.CompilerContext.LoadValue(il, keyIndex);
+                            il.Emit(OpCodes.Box, MapType(typeof(int)));
+                            il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(object)), MapType(typeof(object)) }), null);
+                        }
+                        il.Emit(OpCodes.Ret);
+                    }
+                    break;
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
+        private Compiler.CompilerContext WriteSerializeDeserialize(string assemblyName, TypeBuilder type, SerializerPair[] methodPairs, Compiler.CompilerContext.ILVersion ilVersion, ref ILGenerator il)
+        {
+            il = Override(type, "Serialize");
+            Compiler.CompilerContext ctx = new Compiler.CompilerContext(il, false, true, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Serialize " + type.Name);
+            // arg0 = this, arg1 = key, arg2=obj, arg3=dest
+            Compiler.CodeLabel[] jumpTable = new Compiler.CodeLabel[types.Count];
+            for (int i = 0; i < jumpTable.Length; i++)
+            {
+                jumpTable[i] = ctx.DefineLabel();
+            }
+            il.Emit(OpCodes.Ldarg_1);
+            ctx.Switch(jumpTable);
+            ctx.Return();
+            for (int i = 0; i < jumpTable.Length; i++)
+            {
+                SerializerPair pair = methodPairs[i];
+                ctx.MarkLabel(jumpTable[i]);
+                il.Emit(OpCodes.Ldarg_2);
+                ctx.CastFromObject(pair.Type.Type);
+                il.Emit(OpCodes.Ldarg_3);
+                il.EmitCall(OpCodes.Call, pair.Serialize, null);
+                ctx.Return();
+            }
+
+            il = Override(type, "Deserialize");
+            ctx = new Compiler.CompilerContext(il, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Deserialize " + type.Name);
+            // arg0 = this, arg1 = key, arg2=obj, arg3=source
+            for (int i = 0; i < jumpTable.Length; i++)
+            {
+                jumpTable[i] = ctx.DefineLabel();
+            }
+            il.Emit(OpCodes.Ldarg_1);
+            ctx.Switch(jumpTable);
+            ctx.LoadNullRef();
+            ctx.Return();
+            for (int i = 0; i < jumpTable.Length; i++)
+            {
+                SerializerPair pair = methodPairs[i];
+                ctx.MarkLabel(jumpTable[i]);
+                Type keyType = pair.Type.Type;
+                if (Helpers.IsValueType(keyType))
+                {
+                    il.Emit(OpCodes.Ldarg_2);
+                    il.Emit(OpCodes.Ldarg_3);
+                    il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, i, keyType, methodPairs, this, ilVersion, assemblyName), null);
+                    ctx.Return();
+                }
+                else
+                {
+                    il.Emit(OpCodes.Ldarg_2);
+                    ctx.CastFromObject(keyType);
+                    il.Emit(OpCodes.Ldarg_3);
+                    il.EmitCall(OpCodes.Call, pair.Deserialize, null);
+                    ctx.Return();
+                }
+            }
+            return ctx;
+        }
+
+        private const int KnownTypes_Array = 1, KnownTypes_Dictionary = 2, KnownTypes_Hashtable = 3, KnownTypes_ArrayCutoff = 20;
+        private void WriteGetKeyImpl(TypeBuilder type, bool hasInheritance, SerializerPair[] methodPairs, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName, out ILGenerator il, out int knownTypesCategory, out FieldBuilder knownTypes, out Type knownTypesLookupType)
+        {
+
+            il = Override(type, "GetKeyImpl");
+            Compiler.CompilerContext ctx = new Compiler.CompilerContext(il, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(System.Type), true), "GetKeyImpl");
+
+
+            if (types.Count <= KnownTypes_ArrayCutoff)
+            {
+                knownTypesCategory = KnownTypes_Array;
+                knownTypesLookupType = MapType(typeof(System.Type[]), true);
+            }
+            else
+            {
+                knownTypesLookupType = MapType(typeof(System.Collections.Generic.Dictionary<System.Type, int>), false);
+
+#if !COREFX
+                if (knownTypesLookupType == null)
+                {
+                    knownTypesLookupType = MapType(typeof(Hashtable), true);
+                    knownTypesCategory = KnownTypes_Hashtable;
+                }
+                else
+#endif
+                {
+                    knownTypesCategory = KnownTypes_Dictionary;
+                }
+            }
+            knownTypes = type.DefineField("knownTypes", knownTypesLookupType, FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static);
+
+            switch (knownTypesCategory)
+            {
+                case KnownTypes_Array:
+                    {
+                        il.Emit(OpCodes.Ldsfld, knownTypes);
+                        il.Emit(OpCodes.Ldarg_1);
+                        // note that Array.IndexOf is not supported under CF
+                        il.EmitCall(OpCodes.Callvirt, MapType(typeof(IList)).GetMethod(
+                            "IndexOf", new Type[] { MapType(typeof(object)) }), null);
+                        if (hasInheritance)
+                        {
+                            il.DeclareLocal(MapType(typeof(int))); // loc-0
+                            il.Emit(OpCodes.Dup);
+                            il.Emit(OpCodes.Stloc_0);
+
+                            BasicList getKeyLabels = new BasicList();
+                            int lastKey = -1;
+                            for (int i = 0; i < methodPairs.Length; i++)
+                            {
+                                if (methodPairs[i].MetaKey == methodPairs[i].BaseKey) break;
+                                if (lastKey == methodPairs[i].BaseKey)
+                                {   // add the last label again
+                                    getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]);
+                                }
+                                else
+                                {   // add a new unique label
+                                    getKeyLabels.Add(ctx.DefineLabel());
+                                    lastKey = methodPairs[i].BaseKey;
+                                }
+                            }
+                            Compiler.CodeLabel[] subtypeLabels = new Compiler.CodeLabel[getKeyLabels.Count];
+                            getKeyLabels.CopyTo(subtypeLabels, 0);
+
+                            ctx.Switch(subtypeLabels);
+                            il.Emit(OpCodes.Ldloc_0); // not a sub-type; use the original value
+                            il.Emit(OpCodes.Ret);
+
+                            lastKey = -1;
+                            // now output the different branches per sub-type (not derived type)
+                            for (int i = subtypeLabels.Length - 1; i >= 0; i--)
+                            {
+                                if (lastKey != methodPairs[i].BaseKey)
+                                {
+                                    lastKey = methodPairs[i].BaseKey;
+                                    // find the actual base-index for this base-key (i.e. the index of
+                                    // the base-type)
+                                    int keyIndex = -1;
+                                    for (int j = subtypeLabels.Length; j < methodPairs.Length; j++)
+                                    {
+                                        if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
+                                        {
+                                            keyIndex = j;
+                                            break;
+                                        }
+                                    }
+                                    ctx.MarkLabel(subtypeLabels[i]);
+                                    Compiler.CompilerContext.LoadValue(il, keyIndex);
+                                    il.Emit(OpCodes.Ret);
+                                }
+                            }
+                        }
+                        else
+                        {
+                            il.Emit(OpCodes.Ret);
+                        }
+                    }
+                    break;
+                case KnownTypes_Dictionary:
+                    {
+                        LocalBuilder result = il.DeclareLocal(MapType(typeof(int)));
+                        Label otherwise = il.DefineLabel();
+                        il.Emit(OpCodes.Ldsfld, knownTypes);
+                        il.Emit(OpCodes.Ldarg_1);
+                        il.Emit(OpCodes.Ldloca_S, result);
+                        il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public), null);
+                        il.Emit(OpCodes.Brfalse_S, otherwise);
+                        il.Emit(OpCodes.Ldloc_S, result);
+                        il.Emit(OpCodes.Ret);
+                        il.MarkLabel(otherwise);
+                        il.Emit(OpCodes.Ldc_I4_M1);
+                        il.Emit(OpCodes.Ret);
+                    }
+                    break;
+                case KnownTypes_Hashtable:
+                    {
+                        Label otherwise = il.DefineLabel();
+                        il.Emit(OpCodes.Ldsfld, knownTypes);
+                        il.Emit(OpCodes.Ldarg_1);
+                        il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetProperty("Item").GetGetMethod(), null);
+                        il.Emit(OpCodes.Dup);
+                        il.Emit(OpCodes.Brfalse_S, otherwise);
+                        if (ilVersion == Compiler.CompilerContext.ILVersion.Net1)
+                        {
+                            il.Emit(OpCodes.Unbox, MapType(typeof(int)));
+                            il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
+                        }
+                        else
+                        {
+                            il.Emit(OpCodes.Unbox_Any, MapType(typeof(int)));
+                        }
+                        il.Emit(OpCodes.Ret);
+                        il.MarkLabel(otherwise);
+                        il.Emit(OpCodes.Pop);
+                        il.Emit(OpCodes.Ldc_I4_M1);
+                        il.Emit(OpCodes.Ret);
+                    }
+                    break;
+                default:
+                    throw new InvalidOperationException();
+            }
+        }
+
+        private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out Compiler.CompilerContext.ILVersion ilVersion)
+        {
+            Compiler.CompilerContext ctx;
+
+            index = 0;
+            hasInheritance = false;
+            methodPairs = new SerializerPair[types.Count];
+            foreach (MetaType metaType in types)
+            {
+                MethodBuilder writeMethod = type.DefineMethod("Write"
+#if DEBUG
+ + metaType.Type.Name
+#endif
+,
+                    MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
+                    MapType(typeof(void)), new Type[] { metaType.Type, MapType(typeof(ProtoWriter)) });
+
+                MethodBuilder readMethod = type.DefineMethod("Read"
+#if DEBUG
+ + metaType.Type.Name
+#endif
+,
+                    MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
+                    metaType.Type, new Type[] { metaType.Type, MapType(typeof(ProtoReader)) });
+
+                SerializerPair pair = new SerializerPair(
+                    GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType,
+                    writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator());
+                methodPairs[index++] = pair;
+                if (pair.MetaKey != pair.BaseKey) hasInheritance = true;
+            }
+
+            if (hasInheritance)
+            {
+                Array.Sort(methodPairs);
+            }
+
+            ilVersion = Compiler.CompilerContext.ILVersion.Net2;
+            if (options.MetaDataVersion == 0x10000)
+            {
+                ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school!
+            }
+            for (index = 0; index < methodPairs.Length; index++)
+            {
+                SerializerPair pair = methodPairs[index];
+                ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, methodPairs, this, ilVersion, assemblyName, pair.Type.Type, "SerializeImpl " + pair.Type.Type.Name);
+                MemberInfo returnType = pair.Deserialize.ReturnType
+#if COREFX
+                    .GetTypeInfo()
+#endif
+                    ;
+                ctx.CheckAccessibility(ref returnType);
+                pair.Type.Serializer.EmitWrite(ctx, ctx.InputValue);
+                ctx.Return();
+
+                ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, methodPairs, this, ilVersion, assemblyName, pair.Type.Type, "DeserializeImpl " + pair.Type.Type.Name);
+                pair.Type.Serializer.EmitRead(ctx, ctx.InputValue);
+                if (!pair.Type.Serializer.ReturnsValue)
+                {
+                    ctx.LoadValue(ctx.InputValue);
+                }
+                ctx.Return();
+            }
+        }
+
+        private TypeBuilder WriteBasicTypeModel(CompilerOptions options, string typeName, ModuleBuilder module)
+        {
+            Type baseType = MapType(typeof(TypeModel));
+#if COREFX
+            TypeAttributes typeAttributes = (baseType.GetTypeInfo().Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed;
+#else
+            TypeAttributes typeAttributes = (baseType.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed;
+#endif
+            if (options.Accessibility == Accessibility.Internal)
+            {
+                typeAttributes &= ~TypeAttributes.Public;
+            }
+
+            TypeBuilder type = module.DefineType(typeName, typeAttributes, baseType);
+            return type;
+        }
+
+        private void WriteAssemblyAttributes(CompilerOptions options, string assemblyName, AssemblyBuilder asm)
+        {
+            if (!string.IsNullOrEmpty(options.TargetFrameworkName))
+            {
+                // get [TargetFramework] from mscorlib/equivalent and burn into the new assembly
+                Type versionAttribType = null;
+                try
+                { // this is best-endeavours only
+                    versionAttribType = GetType("System.Runtime.Versioning.TargetFrameworkAttribute", Helpers.GetAssembly(MapType(typeof(string))));
+                }
+                catch { /* don't stress */ }
+                if (versionAttribType != null)
+                {
+                    PropertyInfo[] props;
+                    object[] propValues;
+                    if (string.IsNullOrEmpty(options.TargetFrameworkDisplayName))
+                    {
+                        props = new PropertyInfo[0];
+                        propValues = new object[0];
+                    }
+                    else
+                    {
+                        props = new PropertyInfo[1] { versionAttribType.GetProperty("FrameworkDisplayName") };
+                        propValues = new object[1] { options.TargetFrameworkDisplayName };
+                    }
+                    CustomAttributeBuilder builder = new CustomAttributeBuilder(
+                        versionAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }),
+                        new object[] { options.TargetFrameworkName },
+                        props,
+                        propValues);
+                    asm.SetCustomAttribute(builder);
+                }
+            }
+
+            // copy assembly:InternalsVisibleTo
+            Type internalsVisibleToAttribType = null;
+
+            try
+            {
+                internalsVisibleToAttribType = MapType(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute));
+            }
+            catch { /* best endeavors only */ }
+
+            if (internalsVisibleToAttribType != null)
+            {
+                BasicList internalAssemblies = new BasicList(), consideredAssemblies = new BasicList();
+                foreach (MetaType metaType in types)
+                {
+                    Assembly assembly = Helpers.GetAssembly(metaType.Type);
+                    if (consideredAssemblies.IndexOfReference(assembly) >= 0) continue;
+                    consideredAssemblies.Add(assembly);
+
+                    AttributeMap[] assemblyAttribsMap = AttributeMap.Create(this, assembly);
+                    for (int i = 0; i < assemblyAttribsMap.Length; i++)
+                    {
+
+                        if (assemblyAttribsMap[i].AttributeType != internalsVisibleToAttribType) continue;
+
+                        object privelegedAssemblyObj;
+                        assemblyAttribsMap[i].TryGet("AssemblyName", out privelegedAssemblyObj);
+                        string privelegedAssemblyName = privelegedAssemblyObj as string;
+                        if (privelegedAssemblyName == assemblyName || string.IsNullOrEmpty(privelegedAssemblyName)) continue; // ignore
+
+                        if (internalAssemblies.IndexOfString(privelegedAssemblyName) >= 0) continue; // seen it before
+                        internalAssemblies.Add(privelegedAssemblyName);
+
+                        CustomAttributeBuilder builder = new CustomAttributeBuilder(
+                            internalsVisibleToAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }),
+                            new object[] { privelegedAssemblyName });
+                        asm.SetCustomAttribute(builder);
+                    }
+                }
+            }
+        }
+
+        private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, TypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName)
+        {
+            MethodInfo dedicated = methodPairs[i].Deserialize;
+            MethodBuilder boxedSerializer = type.DefineMethod("_" + i.ToString(), MethodAttributes.Static, CallingConventions.Standard,
+                model.MapType(typeof(object)), new Type[] { model.MapType(typeof(object)), model.MapType(typeof(ProtoReader)) });
+            Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object)), "BoxedSerializer " + valueType.Name);
+            ctx.LoadValue(ctx.InputValue);
+            Compiler.CodeLabel @null = ctx.DefineLabel();
+            ctx.BranchIfFalse(@null, true);
+
+            Type mappedValueType = valueType;
+            ctx.LoadValue(ctx.InputValue);
+            ctx.CastFromObject(mappedValueType);
+            ctx.LoadReaderWriter();
+            ctx.EmitCall(dedicated);
+            ctx.CastToObject(mappedValueType);
+            ctx.Return();
+
+            ctx.MarkLabel(@null);
+            using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType))
+            {
+                // create a new valueType
+                ctx.LoadAddress(typedVal, mappedValueType);
+                ctx.EmitCtor(mappedValueType);
+                ctx.LoadValue(typedVal);
+                ctx.LoadReaderWriter();
+                ctx.EmitCall(dedicated);
+                ctx.CastToObject(mappedValueType);
+                ctx.Return();
+            }
+            return boxedSerializer;
+        }
+
+#endif
+        //internal bool IsDefined(Type type, int fieldNumber)
+        //{
+        //    return FindWithoutAdd(type).IsDefined(fieldNumber);
+        //}
+
+        // note that this is used by some of the unit tests
+        internal bool IsPrepared(Type type)
+        {
+            MetaType meta = FindWithoutAdd(type);
+            return meta != null && meta.IsPrepared();
+        }
+
+        internal EnumSerializer.EnumPair[] GetEnumMap(Type type)
+        {
+            int index = FindOrAddAuto(type, false, false, false);
+            return index < 0 ? null : ((MetaType)types[index]).GetEnumMap();
+        }
+
+        private int metadataTimeoutMilliseconds = 5000;
+        /// <summary>
+        /// The amount of time to wait if there are concurrent metadata access operations
+        /// </summary>
+        public int MetadataTimeoutMilliseconds
+        {
+            get { return metadataTimeoutMilliseconds; }
+            set
+            {
+                if (value <= 0) throw new ArgumentOutOfRangeException("MetadataTimeoutMilliseconds");
+                metadataTimeoutMilliseconds = value;
+            }
+        }
+
+#if DEBUG
+        int lockCount;
+        /// <summary>
+        /// Gets how many times a model lock was taken
+        /// </summary>
+        public int LockCount { get { return lockCount; } }
+#endif
+        internal void TakeLock(ref int opaqueToken)
+        {
+            const string message = "Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection; please also see the LockContended event";
+            opaqueToken = 0;
+#if PORTABLE
+            if(!Monitor.TryEnter(types, metadataTimeoutMilliseconds)) throw new TimeoutException(message);
+            opaqueToken = Interlocked.CompareExchange(ref contentionCounter, 0, 0); // just fetch current value (starts at 1)
+#elif CF2 || CF35
+            int remaining = metadataTimeoutMilliseconds;
+            bool lockTaken;
+            do {
+                lockTaken = Monitor.TryEnter(types);
+                if(!lockTaken)
+                {
+                    if(remaining <= 0) throw new TimeoutException(message);
+                    remaining -= 50;
+                    Thread.Sleep(50);
+                }
+            } while(!lockTaken);
+            opaqueToken = Interlocked.CompareExchange(ref contentionCounter, 0, 0); // just fetch current value (starts at 1)
+#else
+            if (Monitor.TryEnter(types, metadataTimeoutMilliseconds))
+            {
+                opaqueToken = GetContention(); // just fetch current value (starts at 1)
+            }
+            else
+            {
+                AddContention();
+
+                throw new TimeoutException(message);
+            }
+#endif
+
+#if DEBUG // note that here, through all code-paths: we have the lock
+            lockCount++;
+#endif
+        }
+
+        private int contentionCounter = 1;
+#if PLAT_NO_INTERLOCKED
+        private readonly object contentionLock = new object();
+#endif
+        private int GetContention()
+        {
+#if PLAT_NO_INTERLOCKED
+            lock(contentionLock)
+            {
+                return contentionCounter;
+            }
+#else
+            return Interlocked.CompareExchange(ref contentionCounter, 0, 0);
+#endif
+        }
+        private void AddContention()
+        {
+#if PLAT_NO_INTERLOCKED
+            lock(contentionLock)
+            {
+                contentionCounter++;
+            }
+#else
+            Interlocked.Increment(ref contentionCounter);
+#endif
+        }
+
+        internal void ReleaseLock(int opaqueToken)
+        {
+            if (opaqueToken != 0)
+            {
+                Monitor.Exit(types);
+                if (opaqueToken != GetContention()) // contention-count changes since we looked!
+                {
+                    LockContentedEventHandler handler = LockContended;
+                    if (handler != null)
+                    {
+                        // not hugely elegant, but this is such a far-corner-case that it doesn't need to be slick - I'll settle for cross-platform
+                        string stackTrace;
+                        try
+                        {
+                            throw new ProtoException();
+                        }
+                        catch (Exception ex)
+                        {
+                            stackTrace = ex.StackTrace;
+                        }
+
+                        handler(this, new LockContentedEventArgs(stackTrace));
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// If a lock-contention is detected, this event signals the *owner* of the lock responsible for the blockage, indicating
+        /// what caused the problem; this is only raised if the lock-owning code successfully completes.
+        /// </summary>
+        public event LockContentedEventHandler LockContended;
+
+        internal void ResolveListTypes(Type type, ref Type itemType, ref Type defaultType)
+        {
+            if (type == null) return;
+            if (Helpers.GetTypeCode(type) != ProtoTypeCode.Unknown) return; // don't try this[type] for inbuilts
+
+            // handle arrays
+            if (type.IsArray)
+            {
+                if (type.GetArrayRank() != 1)
+                {
+                    throw new NotSupportedException("Multi-dimension arrays are supported");
+                }
+                itemType = type.GetElementType();
+                if (itemType == MapType(typeof(byte)))
+                {
+                    defaultType = itemType = null;
+                }
+                else
+                {
+                    defaultType = type;
+                }
+            }
+            else
+            {
+                // if not an array, first check it isn't explicitly opted out
+                if (this[type].IgnoreListHandling) return;
+            }
+
+            // handle lists 
+            if (itemType == null) { itemType = TypeModel.GetListItemType(this, type); }
+
+            // check for nested data (not allowed)
+            if (itemType != null)
+            {
+                Type nestedItemType = null, nestedDefaultType = null;
+                ResolveListTypes(itemType, ref nestedItemType, ref nestedDefaultType);
+                if (nestedItemType != null)
+                {
+                    throw TypeModel.CreateNestedListsNotSupported(type);
+                }
+            }
+
+            if (itemType != null && defaultType == null)
+            {
+#if COREFX || PROFILE259
+				TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(type);
+                if (typeInfo.IsClass && !typeInfo.IsAbstract && Helpers.GetConstructor(typeInfo, Helpers.EmptyTypes, true) != null)
+#else
+                if (type.IsClass && !type.IsAbstract && Helpers.GetConstructor(type, Helpers.EmptyTypes, true) != null)
+#endif
+                {
+                    defaultType = type;
+                }
+                if (defaultType == null)
+                {
+#if COREFX || PROFILE259
+					if (typeInfo.IsInterface)
+#else
+                    if (type.IsInterface)
+#endif
+                    {
+
+                        Type[] genArgs;
+#if COREFX || PROFILE259
+                        if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>)
+                            && itemType == typeof(System.Collections.Generic.KeyValuePair<,>).MakeGenericType(genArgs = typeInfo.GenericTypeArguments))
+#else
+                        if (type.IsGenericType && type.GetGenericTypeDefinition() == MapType(typeof(System.Collections.Generic.IDictionary<,>))
+                            && itemType == MapType(typeof(System.Collections.Generic.KeyValuePair<,>)).MakeGenericType(genArgs = type.GetGenericArguments()))
+#endif
+                        {
+                            defaultType = MapType(typeof(System.Collections.Generic.Dictionary<,>)).MakeGenericType(genArgs);
+                        }
+                        else
+                        {
+                            defaultType = MapType(typeof(System.Collections.Generic.List<>)).MakeGenericType(itemType);
+                        }
+                    }
+                }
+                // verify that the default type is appropriate
+                if (defaultType != null && !Helpers.IsAssignableFrom(type, defaultType)) { defaultType = null; }
+            }
+        }
+
+        internal string GetSchemaTypeName(Type effectiveType, DataFormat dataFormat, bool asReference, bool dynamicType, ref CommonImports imports)
+        {
+            Type tmp = Helpers.GetUnderlyingType(effectiveType);
+            if (tmp != null) effectiveType = tmp;
+
+            if (effectiveType == this.MapType(typeof(byte[]))) return "bytes";
+
+            WireType wireType;
+            IProtoSerializer ser = ValueMember.TryGetCoreSerializer(this, dataFormat, effectiveType, out wireType, false, false, false, false);
+            if (ser == null)
+            {   // model type
+                if (asReference || dynamicType)
+                {
+                    imports |= CommonImports.Bcl;
+                    return ".bcl.NetObjectProxy";
+                }
+                return this[effectiveType].GetSurrogateOrBaseOrSelf(true).GetSchemaTypeName();
+            }
+            else
+            {
+                if (ser is ParseableSerializer)
+                {
+                    if (asReference) imports |= CommonImports.Bcl;
+                    return asReference ? ".bcl.NetObjectProxy" : "string";
+                }
+
+                switch (Helpers.GetTypeCode(effectiveType))
+                {
+                    case ProtoTypeCode.Boolean: return "bool";
+                    case ProtoTypeCode.Single: return "float";
+                    case ProtoTypeCode.Double: return "double";
+                    case ProtoTypeCode.String:
+                        if (asReference) imports |= CommonImports.Bcl;
+                        return asReference ? ".bcl.NetObjectProxy" : "string";
+                    case ProtoTypeCode.Byte:
+                    case ProtoTypeCode.Char:
+                    case ProtoTypeCode.UInt16:
+                    case ProtoTypeCode.UInt32:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.FixedSize: return "fixed32";
+                            default: return "uint32";
+                        }
+                    case ProtoTypeCode.SByte:
+                    case ProtoTypeCode.Int16:
+                    case ProtoTypeCode.Int32:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.ZigZag: return "sint32";
+                            case DataFormat.FixedSize: return "sfixed32";
+                            default: return "int32";
+                        }
+                    case ProtoTypeCode.UInt64:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.FixedSize: return "fixed64";
+                            default: return "uint64";
+                        }
+                    case ProtoTypeCode.Int64:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.ZigZag: return "sint64";
+                            case DataFormat.FixedSize: return "sfixed64";
+                            default: return "int64";
+                        }
+                    case ProtoTypeCode.DateTime:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.FixedSize: return "sint64";
+                            case DataFormat.WellKnown:
+                                imports |= CommonImports.Timestamp;
+                                return ".google.protobuf.Timestamp";
+                            default:
+                                imports |= CommonImports.Bcl;
+                                return ".bcl.DateTime";
+                        }
+                    case ProtoTypeCode.TimeSpan:
+                        switch (dataFormat)
+                        {
+                            case DataFormat.FixedSize: return "sint64";
+                            case DataFormat.WellKnown:
+                                imports |= CommonImports.Duration;
+                                return ".google.protobuf.Duration";
+                            default:
+                                imports |= CommonImports.Bcl;
+                                return ".bcl.TimeSpan";
+                        }
+                    case ProtoTypeCode.Decimal: imports |= CommonImports.Bcl; return ".bcl.Decimal";
+                    case ProtoTypeCode.Guid: imports |= CommonImports.Bcl; return ".bcl.Guid";
+                    case ProtoTypeCode.Type: return "string";
+                    default: throw new NotSupportedException("No .proto map found for: " + effectiveType.FullName);
+                }
+            }
+
+        }
+
+        /// <summary>
+        /// Designate a factory-method to use to create instances of any type; note that this only affect types seen by the serializer *after* setting the factory.
+        /// </summary>
+        public void SetDefaultFactory(MethodInfo methodInfo)
+        {
+            VerifyFactory(methodInfo, null);
+            defaultFactory = methodInfo;
+        }
+        private MethodInfo defaultFactory;
+
+        internal void VerifyFactory(MethodInfo factory, Type type)
+        {
+            if (factory != null)
+            {
+                if (type != null && Helpers.IsValueType(type)) throw new InvalidOperationException();
+                if (!factory.IsStatic) throw new ArgumentException("A factory-method must be static", "factory");
+                if ((type != null && factory.ReturnType != type) && factory.ReturnType != MapType(typeof(object))) throw new ArgumentException("The factory-method must return object" + (type == null ? "" : (" or " + type.FullName)), "factory");
+
+                if (!CallbackSet.CheckCallbackParameters(this, factory)) throw new ArgumentException("Invalid factory signature in " + factory.DeclaringType.FullName + "." + factory.Name, "factory");
+            }
+        }
+
+    }
+    /// <summary>
+    /// Contains the stack-trace of the owning code when a lock-contention scenario is detected
+    /// </summary>
+    public sealed class LockContentedEventArgs : EventArgs
+    {
+        private readonly string ownerStackTrace;
+        internal LockContentedEventArgs(string ownerStackTrace)
+        {
+            this.ownerStackTrace = ownerStackTrace;
+        }
+
+        /// <summary>
+        /// The stack-trace of the code that owned the lock when a lock-contention scenario occurred
+        /// </summary>
+        public string OwnerStackTrace => ownerStackTrace;
+    }
+    /// <summary>
+    /// Event-type that is raised when a lock-contention scenario is detected
+    /// </summary>
+    public delegate void LockContentedEventHandler(object sender, LockContentedEventArgs args);
+}
+#endif

+ 97 - 0
Server/ThirdParty/protobuf-net/Meta/SubType.cs

@@ -0,0 +1,97 @@
+#if !NO_RUNTIME
+using System;
+using System.Collections.Generic;
+using ProtoBuf.Serializers;
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Represents an inherited type in a type hierarchy.
+    /// </summary>
+    public sealed class SubType
+    {
+        internal sealed class Comparer : System.Collections.IComparer, IComparer<SubType>
+        {
+            public static readonly Comparer Default = new Comparer();
+
+            public int Compare(object x, object y)
+            {
+                return Compare(x as SubType, y as SubType);
+            }
+
+            public int Compare(SubType x, SubType y)
+            {
+                if (ReferenceEquals(x, y)) return 0;
+                if (x == null) return -1;
+                if (y == null) return 1;
+
+                return x.FieldNumber.CompareTo(y.FieldNumber);
+            }
+        }
+
+        private int _fieldNumber;
+
+        /// <summary>
+        /// The field-number that is used to encapsulate the data (as a nested
+        /// message) for the derived dype.
+        /// </summary>
+        public int FieldNumber
+        {
+            get => _fieldNumber;
+            internal set
+            {
+                if (_fieldNumber != value)
+                {
+                    MetaType.AssertValidFieldNumber(value);
+                    ThrowIfFrozen();
+                    _fieldNumber = value;
+                }
+            }
+        }
+
+        private void ThrowIfFrozen()
+        {
+            if (serializer != null) throw new InvalidOperationException("The type cannot be changed once a serializer has been generated");
+        }
+
+
+        /// <summary>
+        /// The sub-type to be considered.
+        /// </summary>
+        public MetaType DerivedType => derivedType;
+        private readonly MetaType derivedType;
+
+        /// <summary>
+        /// Creates a new SubType instance.
+        /// </summary>
+        /// <param name="fieldNumber">The field-number that is used to encapsulate the data (as a nested
+        /// message) for the derived dype.</param>
+        /// <param name="derivedType">The sub-type to be considered.</param>
+        /// <param name="format">Specific encoding style to use; in particular, Grouped can be used to avoid buffering, but is not the default.</param>
+        public SubType(int fieldNumber, MetaType derivedType, DataFormat format)
+        {
+            if (derivedType == null) throw new ArgumentNullException(nameof(derivedType));
+            if (fieldNumber <= 0) throw new ArgumentOutOfRangeException(nameof(fieldNumber));
+            _fieldNumber = fieldNumber;
+            this.derivedType = derivedType;
+            this.dataFormat = format;
+        }
+
+        private readonly DataFormat dataFormat;
+
+        private IProtoSerializer serializer;
+
+        internal IProtoSerializer Serializer => serializer ?? (serializer = BuildSerializer());
+
+        private IProtoSerializer BuildSerializer()
+        {
+            // note the caller here is MetaType.BuildSerializer, which already has the sync-lock
+            WireType wireType = WireType.String;
+            if(dataFormat == DataFormat.Group) wireType = WireType.StartGroup; // only one exception
+            
+            IProtoSerializer ser = new SubItemSerializer(derivedType.Type, derivedType.GetKey(false, false), derivedType, false);
+            return new TagDecorator(_fieldNumber, wireType, false, ser);
+        }
+    }
+}
+#endif

+ 64 - 0
Server/ThirdParty/protobuf-net/Meta/TypeFormatEventArgs.cs

@@ -0,0 +1,64 @@
+using System;
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Event arguments needed to perform type-formatting functions; this could be resolving a Type to a string suitable for serialization, or could
+    /// be requesting a Type from a string. If no changes are made, a default implementation will be used (from the assembly-qualified names).
+    /// </summary>
+    public class TypeFormatEventArgs : EventArgs
+    {
+        private Type type;
+        private string formattedName;
+        private readonly bool typeFixed;
+        /// <summary>
+        /// The type involved in this map; if this is initially null, a Type is expected to be provided for the string in FormattedName.
+        /// </summary>
+        public Type Type
+        {
+            get { return type; }
+            set
+            {
+                if (type != value)
+                {
+                    if (typeFixed) throw new InvalidOperationException("The type is fixed and cannot be changed");
+                    type = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// The formatted-name involved in this map; if this is initially null, a formatted-name is expected from the type in Type.
+        /// </summary>
+        public string FormattedName
+        {
+            get { return formattedName; }
+            set
+            {
+                if (formattedName != value)
+                {
+                    if (!typeFixed) throw new InvalidOperationException("The formatted-name is fixed and cannot be changed");
+                    formattedName = value;
+                }
+            }
+        }
+
+        internal TypeFormatEventArgs(string formattedName)
+        {
+            if (string.IsNullOrEmpty(formattedName)) throw new ArgumentNullException("formattedName");
+            this.formattedName = formattedName;
+            // typeFixed = false; <== implicit
+        }
+
+        internal TypeFormatEventArgs(Type type)
+        {
+            this.type = type ?? throw new ArgumentNullException(nameof(type));
+            typeFixed = true;
+        }
+    }
+
+    /// <summary>
+    /// Delegate type used to perform type-formatting functions; the sender originates as the type-model.
+    /// </summary>
+    public delegate void TypeFormatEventHandler(object sender, TypeFormatEventArgs args);
+}

+ 45 - 0
Server/ThirdParty/protobuf-net/Meta/TypeModel.InputOutput.cs

@@ -0,0 +1,45 @@
+using System;
+using System.IO;
+
+namespace ProtoBuf.Meta
+{
+    partial class TypeModel :
+        IProtoInput<Stream>,
+        IProtoInput<ArraySegment<byte>>,
+        IProtoInput<byte[]>,
+        IProtoOutput<Stream>
+    {
+        static SerializationContext CreateContext(object userState)
+        {
+            if (userState == null)
+                return SerializationContext.Default;
+            if (userState is SerializationContext ctx)
+                return ctx;
+
+            var obj = new SerializationContext { Context = userState };
+            obj.Freeze();
+            return obj;
+        }
+        T IProtoInput<Stream>.Deserialize<T>(Stream source, T value, object userState)
+            => (T)Deserialize(source, value, typeof(T), CreateContext(userState));
+
+        T IProtoInput<ArraySegment<byte>>.Deserialize<T>(ArraySegment<byte> source, T value, object userState)
+        {
+            using (var ms = new MemoryStream(source.Array, source.Offset, source.Count))
+            {
+                return (T)Deserialize(ms, value, typeof(T), CreateContext(userState));
+            }
+        }
+
+        T IProtoInput<byte[]>.Deserialize<T>(byte[] source, T value, object userState)
+        {
+            using (var ms = new MemoryStream(source))
+            {
+                return (T)Deserialize(ms, value, typeof(T), CreateContext(userState));
+            }
+        }
+
+        void IProtoOutput<Stream>.Serialize<T>(Stream destination, T value, object userState)
+            => Serialize(destination, value, CreateContext(userState));
+    }
+}

+ 1696 - 0
Server/ThirdParty/protobuf-net/Meta/TypeModel.cs

@@ -0,0 +1,1696 @@
+using System;
+using System.IO;
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Provides protobuf serialization support for a number of types
+    /// </summary>
+    public abstract partial class TypeModel
+    {
+#if COREFX
+        internal TypeInfo MapType(TypeInfo type)
+        {
+            return type;
+        }
+#endif
+
+        /// <summary>
+        /// Should the <c>Kind</c> be included on date/time values?
+        /// </summary>
+        protected internal virtual bool SerializeDateTimeKind() { return false; }
+
+        /// <summary>
+        /// Resolve a System.Type to the compiler-specific type
+        /// </summary>
+        protected internal Type MapType(Type type)
+        {
+            return MapType(type, true);
+        }
+        /// <summary>
+        /// Resolve a System.Type to the compiler-specific type
+        /// </summary>
+        protected internal virtual Type MapType(Type type, bool demand)
+        {
+            return type;
+        }
+
+        private WireType GetWireType(ProtoTypeCode code, DataFormat format, ref Type type, out int modelKey)
+        {
+            modelKey = -1;
+            if (Helpers.IsEnum(type))
+            {
+                modelKey = GetKey(ref type);
+                return WireType.Variant;
+            }
+            switch (code)
+            {
+                case ProtoTypeCode.Int64:
+                case ProtoTypeCode.UInt64:
+                    return format == DataFormat.FixedSize ? WireType.Fixed64 : WireType.Variant;
+                case ProtoTypeCode.Int16:
+                case ProtoTypeCode.Int32:
+                case ProtoTypeCode.UInt16:
+                case ProtoTypeCode.UInt32:
+                case ProtoTypeCode.Boolean:
+                case ProtoTypeCode.SByte:
+                case ProtoTypeCode.Byte:
+                case ProtoTypeCode.Char:
+                    return format == DataFormat.FixedSize ? WireType.Fixed32 : WireType.Variant;
+                case ProtoTypeCode.Double:
+                    return WireType.Fixed64;
+                case ProtoTypeCode.Single:
+                    return WireType.Fixed32;
+                case ProtoTypeCode.String:
+                case ProtoTypeCode.DateTime:
+                case ProtoTypeCode.Decimal:
+                case ProtoTypeCode.ByteArray:
+                case ProtoTypeCode.TimeSpan:
+                case ProtoTypeCode.Guid:
+                case ProtoTypeCode.Uri:
+                    return WireType.String;
+            }
+
+            if ((modelKey = GetKey(ref type)) >= 0)
+            {
+                return WireType.String;
+            }
+            return WireType.None;
+        }
+
+
+        /// <summary>
+        /// This is the more "complete" version of Serialize, which handles single instances of mapped types.
+        /// The value is written as a complete field, including field-header and (for sub-objects) a
+        /// length-prefix
+        /// In addition to that, this provides support for:
+        ///  - basic values; individual int / string / Guid / etc
+        ///  - IEnumerable sequences of any type handled by TrySerializeAuxiliaryType
+        ///  
+        /// </summary>
+        internal bool TrySerializeAuxiliaryType(ProtoWriter writer, Type type, DataFormat format, int tag, object value, bool isInsideList, object parentList)
+        {
+            if (type == null) { type = value.GetType(); }
+
+            ProtoTypeCode typecode = Helpers.GetTypeCode(type);
+            // note the "ref type" here normalizes against proxies
+            WireType wireType = GetWireType(typecode, format, ref type, out int modelKey);
+
+
+            if (modelKey >= 0)
+            {   // write the header, but defer to the model
+                if (Helpers.IsEnum(type))
+                { // no header
+                    Serialize(modelKey, value, writer);
+                    return true;
+                }
+                else
+                {
+                    ProtoWriter.WriteFieldHeader(tag, wireType, writer);
+                    switch (wireType)
+                    {
+                        case WireType.None:
+                            throw ProtoWriter.CreateException(writer);
+                        case WireType.StartGroup:
+                        case WireType.String:
+                            // needs a wrapping length etc
+                            SubItemToken token = ProtoWriter.StartSubItem(value, writer);
+                            Serialize(modelKey, value, writer);
+                            ProtoWriter.EndSubItem(token, writer);
+                            return true;
+                        default:
+                            Serialize(modelKey, value, writer);
+                            return true;
+                    }
+                }
+            }
+
+            if (wireType != WireType.None)
+            {
+                ProtoWriter.WriteFieldHeader(tag, wireType, writer);
+            }
+            switch (typecode)
+            {
+                case ProtoTypeCode.Int16: ProtoWriter.WriteInt16((short)value, writer); return true;
+                case ProtoTypeCode.Int32: ProtoWriter.WriteInt32((int)value, writer); return true;
+                case ProtoTypeCode.Int64: ProtoWriter.WriteInt64((long)value, writer); return true;
+                case ProtoTypeCode.UInt16: ProtoWriter.WriteUInt16((ushort)value, writer); return true;
+                case ProtoTypeCode.UInt32: ProtoWriter.WriteUInt32((uint)value, writer); return true;
+                case ProtoTypeCode.UInt64: ProtoWriter.WriteUInt64((ulong)value, writer); return true;
+                case ProtoTypeCode.Boolean: ProtoWriter.WriteBoolean((bool)value, writer); return true;
+                case ProtoTypeCode.SByte: ProtoWriter.WriteSByte((sbyte)value, writer); return true;
+                case ProtoTypeCode.Byte: ProtoWriter.WriteByte((byte)value, writer); return true;
+                case ProtoTypeCode.Char: ProtoWriter.WriteUInt16((ushort)(char)value, writer); return true;
+                case ProtoTypeCode.Double: ProtoWriter.WriteDouble((double)value, writer); return true;
+                case ProtoTypeCode.Single: ProtoWriter.WriteSingle((float)value, writer); return true;
+                case ProtoTypeCode.DateTime:
+                    if (SerializeDateTimeKind())
+                        BclHelpers.WriteDateTimeWithKind((DateTime)value, writer);
+                    else
+                        BclHelpers.WriteDateTime((DateTime)value, writer);
+                    return true;
+                case ProtoTypeCode.Decimal: BclHelpers.WriteDecimal((decimal)value, writer); return true;
+                case ProtoTypeCode.String: ProtoWriter.WriteString((string)value, writer); return true;
+                case ProtoTypeCode.ByteArray: ProtoWriter.WriteBytes((byte[])value, writer); return true;
+                case ProtoTypeCode.TimeSpan: BclHelpers.WriteTimeSpan((TimeSpan)value, writer); return true;
+                case ProtoTypeCode.Guid: BclHelpers.WriteGuid((Guid)value, writer); return true;
+                case ProtoTypeCode.Uri: ProtoWriter.WriteString(((Uri)value).OriginalString, writer); return true;
+            }
+
+            // by now, we should have covered all the simple cases; if we wrote a field-header, we have
+            // forgotten something!
+            Helpers.DebugAssert(wireType == WireType.None);
+
+            // now attempt to handle sequences (including arrays and lists)
+            if (value is IEnumerable sequence)
+            {
+                if (isInsideList) throw CreateNestedListsNotSupported(parentList?.GetType());
+                foreach (object item in sequence)
+                {
+                    if (item == null) { throw new NullReferenceException(); }
+                    if (!TrySerializeAuxiliaryType(writer, null, format, tag, item, true, sequence))
+                    {
+                        ThrowUnexpectedType(item.GetType());
+                    }
+                }
+                return true;
+            }
+            return false;
+        }
+
+        private void SerializeCore(ProtoWriter writer, object value)
+        {
+            if (value == null) throw new ArgumentNullException(nameof(value));
+            Type type = value.GetType();
+            int key = GetKey(ref type);
+            if (key >= 0)
+            {
+                Serialize(key, value, writer);
+            }
+            else if (!TrySerializeAuxiliaryType(writer, type, DataFormat.Default, Serializer.ListItemTag, value, false, null))
+            {
+                ThrowUnexpectedType(type);
+            }
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream.
+        /// </summary>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        public void Serialize(Stream dest, object value)
+        {
+            Serialize(dest, value, null);
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream.
+        /// </summary>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public void Serialize(Stream dest, object value, SerializationContext context)
+        {
+            using (ProtoWriter writer = ProtoWriter.Create(dest, this, context))
+            {
+                writer.SetRootObject(value);
+                SerializeCore(writer, value);
+                writer.Close();
+            }
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied writer.
+        /// </summary>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="dest">The destination writer to write to.</param>
+        public void Serialize(ProtoWriter dest, object value)
+        {
+            if (dest == null) throw new ArgumentNullException(nameof(dest));
+            dest.CheckDepthFlushlock();
+            dest.SetRootObject(value);
+            SerializeCore(dest, value);
+            dest.CheckDepthFlushlock();
+            ProtoWriter.Flush(dest);
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
+        /// data - useful with network IO.
+        /// </summary>
+        /// <param name="type">The type being merged.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int fieldNumber)
+            => DeserializeWithLengthPrefix(source, value, type, style, fieldNumber, null, out long bytesRead);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
+        /// data - useful with network IO.
+        /// </summary>
+        /// <param name="type">The type being merged.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="expectedField">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        /// <param name="resolver">Used to resolve types on a per-field basis.</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
+            => DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out long bytesRead);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
+        /// data - useful with network IO.
+        /// </summary>
+        /// <param name="type">The type being merged.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="expectedField">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        /// <param name="resolver">Used to resolve types on a per-field basis.</param>
+        /// <param name="bytesRead">Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out int bytesRead)
+        {
+            object result = DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out long bytesRead64, out bool haveObject, null);
+            bytesRead = checked((int)bytesRead64);
+            return result;
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (or null), using length-prefixed
+        /// data - useful with network IO.
+        /// </summary>
+        /// <param name="type">The type being merged.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="expectedField">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        /// <param name="resolver">Used to resolve types on a per-field basis.</param>
+        /// <param name="bytesRead">Returns the number of bytes consumed by this operation (includes length-prefix overheads and any skipped data).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out long bytesRead) => DeserializeWithLengthPrefix(source, value, type, style, expectedField, resolver, out bytesRead, out bool haveObject, null);
+
+        private object DeserializeWithLengthPrefix(Stream source, object value, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, out long bytesRead, out bool haveObject, SerializationContext context)
+        {
+            haveObject = false;
+            bool skip;
+            long len;
+            bytesRead = 0;
+            if (type == null && (style != PrefixStyle.Base128 || resolver == null))
+            {
+                throw new InvalidOperationException("A type must be provided unless base-128 prefixing is being used in combination with a resolver");
+            }
+            do
+            {
+
+                bool expectPrefix = expectedField > 0 || resolver != null;
+                len = ProtoReader.ReadLongLengthPrefix(source, expectPrefix, style, out int actualField, out int tmpBytesRead);
+                if (tmpBytesRead == 0) return value;
+                bytesRead += tmpBytesRead;
+                if (len < 0) return value;
+
+                switch (style)
+                {
+                    case PrefixStyle.Base128:
+                        if (expectPrefix && expectedField == 0 && type == null && resolver != null)
+                        {
+                            type = resolver(actualField);
+                            skip = type == null;
+                        }
+                        else { skip = expectedField != actualField; }
+                        break;
+                    default:
+                        skip = false;
+                        break;
+                }
+
+                if (skip)
+                {
+                    if (len == long.MaxValue) throw new InvalidOperationException();
+                    ProtoReader.Seek(source, len, null);
+                    bytesRead += len;
+                }
+            } while (skip);
+
+            ProtoReader reader = null;
+            try
+            {
+                reader = ProtoReader.Create(source, this, context, len);
+                int key = GetKey(ref type);
+                if (key >= 0 && !Helpers.IsEnum(type))
+                {
+                    value = Deserialize(key, value, reader);
+                }
+                else
+                {
+                    if (!(TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false, null) || len == 0))
+                    {
+                        TypeModel.ThrowUnexpectedType(type); // throws
+                    }
+                }
+                bytesRead += reader.LongPosition;
+                haveObject = true;
+                return value;
+            }
+            finally
+            {
+                ProtoReader.Recycle(reader);
+            }
+        }
+
+        /// <summary>
+        /// Reads a sequence of consecutive length-prefixed items from a stream, using
+        /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
+        /// are directly comparable to serializing multiple items in succession
+        /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
+        /// when serializing a list/array). When a tag is
+        /// specified, any records with different tags are silently omitted. The
+        /// tag is ignored. The tag is ignores for fixed-length prefixes.
+        /// </summary>
+        /// <param name="source">The binary stream containing the serialized records.</param>
+        /// <param name="style">The prefix style used in the data.</param>
+        /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
+        /// expected and all records are returned).</param>
+        /// <param name="resolver">On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). </param>
+        /// <param name="type">The type of object to deserialize (can be null if "resolver" is specified).</param>
+        /// <returns>The sequence of deserialized objects.</returns>
+        public IEnumerable DeserializeItems(System.IO.Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver)
+        {
+            return DeserializeItems(source, type, style, expectedField, resolver, null);
+        }
+        /// <summary>
+        /// Reads a sequence of consecutive length-prefixed items from a stream, using
+        /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
+        /// are directly comparable to serializing multiple items in succession
+        /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
+        /// when serializing a list/array). When a tag is
+        /// specified, any records with different tags are silently omitted. The
+        /// tag is ignored. The tag is ignores for fixed-length prefixes.
+        /// </summary>
+        /// <param name="source">The binary stream containing the serialized records.</param>
+        /// <param name="style">The prefix style used in the data.</param>
+        /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
+        /// expected and all records are returned).</param>
+        /// <param name="resolver">On a field-by-field basis, the type of object to deserialize (can be null if "type" is specified). </param>
+        /// <param name="type">The type of object to deserialize (can be null if "resolver" is specified).</param>
+        /// <returns>The sequence of deserialized objects.</returns>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public IEnumerable DeserializeItems(System.IO.Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
+        {
+            return new DeserializeItemsIterator(this, source, type, style, expectedField, resolver, context);
+        }
+
+        /// <summary>
+        /// Reads a sequence of consecutive length-prefixed items from a stream, using
+        /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
+        /// are directly comparable to serializing multiple items in succession
+        /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
+        /// when serializing a list/array). When a tag is
+        /// specified, any records with different tags are silently omitted. The
+        /// tag is ignored. The tag is ignores for fixed-length prefixes.
+        /// </summary>
+        /// <typeparam name="T">The type of object to deserialize.</typeparam>
+        /// <param name="source">The binary stream containing the serialized records.</param>
+        /// <param name="style">The prefix style used in the data.</param>
+        /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
+        /// expected and all records are returned).</param>
+        /// <returns>The sequence of deserialized objects.</returns>
+        public IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField)
+        {
+            return DeserializeItems<T>(source, style, expectedField, null);
+        }
+        /// <summary>
+        /// Reads a sequence of consecutive length-prefixed items from a stream, using
+        /// either base-128 or fixed-length prefixes. Base-128 prefixes with a tag
+        /// are directly comparable to serializing multiple items in succession
+        /// (use the <see cref="Serializer.ListItemTag"/> tag to emulate the implicit behavior
+        /// when serializing a list/array). When a tag is
+        /// specified, any records with different tags are silently omitted. The
+        /// tag is ignored. The tag is ignores for fixed-length prefixes.
+        /// </summary>
+        /// <typeparam name="T">The type of object to deserialize.</typeparam>
+        /// <param name="source">The binary stream containing the serialized records.</param>
+        /// <param name="style">The prefix style used in the data.</param>
+        /// <param name="expectedField">The tag of records to return (if non-positive, then no tag is
+        /// expected and all records are returned).</param>
+        /// <returns>The sequence of deserialized objects.</returns>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public IEnumerable<T> DeserializeItems<T>(Stream source, PrefixStyle style, int expectedField, SerializationContext context)
+        {
+            return new DeserializeItemsIterator<T>(this, source, style, expectedField, context);
+        }
+
+        private sealed class DeserializeItemsIterator<T> : DeserializeItemsIterator,
+            IEnumerator<T>,
+            IEnumerable<T>
+        {
+            IEnumerator<T> IEnumerable<T>.GetEnumerator() { return this; }
+            public new T Current { get { return (T)base.Current; } }
+            void IDisposable.Dispose() { }
+            public DeserializeItemsIterator(TypeModel model, Stream source, PrefixStyle style, int expectedField, SerializationContext context)
+                : base(model, source, model.MapType(typeof(T)), style, expectedField, null, context) { }
+        }
+
+        private class DeserializeItemsIterator : IEnumerator, IEnumerable
+        {
+            IEnumerator IEnumerable.GetEnumerator() { return this; }
+            private bool haveObject;
+            private object current;
+            public bool MoveNext()
+            {
+                if (haveObject)
+                {
+                    current = model.DeserializeWithLengthPrefix(source, null, type, style, expectedField, resolver, out long bytesRead, out haveObject, context);
+                }
+                return haveObject;
+            }
+            void IEnumerator.Reset() { throw new NotSupportedException(); }
+            public object Current { get { return current; } }
+            private readonly Stream source;
+            private readonly Type type;
+            private readonly PrefixStyle style;
+            private readonly int expectedField;
+            private readonly Serializer.TypeResolver resolver;
+            private readonly TypeModel model;
+            private readonly SerializationContext context;
+            public DeserializeItemsIterator(TypeModel model, Stream source, Type type, PrefixStyle style, int expectedField, Serializer.TypeResolver resolver, SerializationContext context)
+            {
+                haveObject = true;
+                this.source = source;
+                this.type = type;
+                this.style = style;
+                this.expectedField = expectedField;
+                this.resolver = resolver;
+                this.model = model;
+                this.context = context;
+            }
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream,
+        /// with a length-prefix. This is useful for socket programming,
+        /// as DeserializeWithLengthPrefix can be used to read the single object back
+        /// from an ongoing stream.
+        /// </summary>
+        /// <param name="type">The type being serialized.</param>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber)
+        {
+            SerializeWithLengthPrefix(dest, value, type, style, fieldNumber, null);
+        }
+
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream,
+        /// with a length-prefix. This is useful for socket programming,
+        /// as DeserializeWithLengthPrefix can be used to read the single object back
+        /// from an ongoing stream.
+        /// </summary>
+        /// <param name="type">The type being serialized.</param>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="style">How to encode the length prefix.</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        /// <param name="fieldNumber">The tag used as a prefix to each record (only used with base-128 style prefixes).</param>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public void SerializeWithLengthPrefix(Stream dest, object value, Type type, PrefixStyle style, int fieldNumber, SerializationContext context)
+        {
+            if (type == null)
+            {
+                if (value == null) throw new ArgumentNullException(nameof(value));
+                type = MapType(value.GetType());
+            }
+            int key = GetKey(ref type);
+            using (ProtoWriter writer = ProtoWriter.Create(dest, this, context))
+            {
+                switch (style)
+                {
+                    case PrefixStyle.None:
+                        Serialize(key, value, writer);
+                        break;
+                    case PrefixStyle.Base128:
+                    case PrefixStyle.Fixed32:
+                    case PrefixStyle.Fixed32BigEndian:
+                        ProtoWriter.WriteObject(value, key, writer, style, fieldNumber);
+                        break;
+                    default:
+                        throw new ArgumentOutOfRangeException("style");
+                }
+                writer.Close();
+            }
+        }
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object Deserialize(Stream source, object value, Type type)
+        {
+            return Deserialize(source, value, type, null);
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public object Deserialize(Stream source, object value, Type type, SerializationContext context)
+        {
+            bool autoCreate = PrepareDeserialize(value, ref type);
+            ProtoReader reader = null;
+            try
+            {
+                reader = ProtoReader.Create(source, this, context, ProtoReader.TO_EOF);
+                if (value != null) reader.SetRootObject(value);
+                object obj = DeserializeCore(reader, type, value, autoCreate);
+                reader.CheckFullyConsumed();
+                return obj;
+            }
+            finally
+            {
+                ProtoReader.Recycle(reader);
+            }
+        }
+
+        private bool PrepareDeserialize(object value, ref Type type)
+        {
+            if (type == null)
+            {
+                if (value == null)
+                {
+                    throw new ArgumentNullException(nameof(type));
+                }
+                else
+                {
+                    type = MapType(value.GetType());
+                }
+            }
+
+            bool autoCreate = true;
+            Type underlyingType = Helpers.GetUnderlyingType(type);
+            if (underlyingType != null)
+            {
+                type = underlyingType;
+                autoCreate = false;
+            }
+            return autoCreate;
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="length">The number of bytes to consume.</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object Deserialize(Stream source, object value, System.Type type, int length)
+            => Deserialize(source, value, type, length, null);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="length">The number of bytes to consume.</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object Deserialize(Stream source, object value, System.Type type, long length)
+            => Deserialize(source, value, type, length, null);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="length">The number of bytes to consume (or -1 to read to the end of the stream).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public object Deserialize(Stream source, object value, System.Type type, int length, SerializationContext context)
+            => Deserialize(source, value, type, length == int.MaxValue ? long.MaxValue : (long)length, context);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <param name="length">The number of bytes to consume (or -1 to read to the end of the stream).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        /// <param name="context">Additional information about this serialization operation.</param>
+        public object Deserialize(Stream source, object value, System.Type type, long length, SerializationContext context)
+        {
+            bool autoCreate = PrepareDeserialize(value, ref type);
+            ProtoReader reader = null;
+            try
+            {
+                reader = ProtoReader.Create(source, this, context, length);
+                if (value != null) reader.SetRootObject(value);
+                object obj = DeserializeCore(reader, type, value, autoCreate);
+                reader.CheckFullyConsumed();
+                return obj;
+            }
+            finally
+            {
+                ProtoReader.Recycle(reader);
+            }
+        }
+
+        /// <summary>
+        /// Applies a protocol-buffer reader to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="type">The type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The reader to apply to the instance (cannot be null).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        public object Deserialize(ProtoReader source, object value, System.Type type)
+        {
+            if (source == null) throw new ArgumentNullException("source");
+            bool autoCreate = PrepareDeserialize(value, ref type);
+            if (value != null) source.SetRootObject(value);
+            object obj = DeserializeCore(source, type, value, autoCreate);
+            source.CheckFullyConsumed();
+            return obj;
+        }
+
+        private object DeserializeCore(ProtoReader reader, Type type, object value, bool noAutoCreate)
+        {
+            int key = GetKey(ref type);
+            if (key >= 0 && !Helpers.IsEnum(type))
+            {
+                return Deserialize(key, value, reader);
+            }
+            // this returns true to say we actively found something, but a value is assigned either way (or throws)
+            TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, noAutoCreate, false, null);
+            return value;
+        }
+
+#if COREFX
+        private static readonly System.Reflection.TypeInfo ilist = typeof(IList).GetTypeInfo();
+#else
+        private static readonly System.Type ilist = typeof(IList);
+#endif
+        internal static MethodInfo ResolveListAdd(TypeModel model, Type listType, Type itemType, out bool isList)
+        {
+#if COREFX || PROFILE259
+			TypeInfo listTypeInfo = listType.GetTypeInfo();
+#else
+            Type listTypeInfo = listType;
+#endif
+#if PROFILE259
+			isList = model.MapType(ilist).GetTypeInfo().IsAssignableFrom(listTypeInfo);
+#else
+            isList = model.MapType(ilist).IsAssignableFrom(listTypeInfo);
+#endif
+            Type[] types = { itemType };
+            MethodInfo add = Helpers.GetInstanceMethod(listTypeInfo, "Add", types);
+
+#if !NO_GENERICS
+            if (add == null)
+            {   // fallback: look for ICollection<T>'s Add(typedObject) method
+
+                bool forceList = listTypeInfo.IsInterface &&
+                    model.MapType(typeof(System.Collections.Generic.IEnumerable<>)).MakeGenericType(types)
+#if COREFX || PROFILE259
+					.GetTypeInfo()
+#endif
+                    .IsAssignableFrom(listTypeInfo);
+
+#if COREFX || PROFILE259
+				TypeInfo constuctedListType = typeof(System.Collections.Generic.ICollection<>).MakeGenericType(types).GetTypeInfo();
+#else
+                Type constuctedListType = model.MapType(typeof(System.Collections.Generic.ICollection<>)).MakeGenericType(types);
+#endif
+                if (forceList || constuctedListType.IsAssignableFrom(listTypeInfo))
+                {
+                    add = Helpers.GetInstanceMethod(constuctedListType, "Add", types);
+                }
+            }
+
+            if (add == null)
+            {
+
+#if COREFX || PROFILE259
+				foreach (Type tmpType in listTypeInfo.ImplementedInterfaces)
+#else
+                foreach (Type interfaceType in listTypeInfo.GetInterfaces())
+#endif
+                {
+#if COREFX || PROFILE259
+					TypeInfo interfaceType = tmpType.GetTypeInfo();
+#endif
+                    if (interfaceType.Name == "IProducerConsumerCollection`1" && interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
+                    {
+                        add = Helpers.GetInstanceMethod(interfaceType, "TryAdd", types);
+                        if (add != null) break;
+                    }
+                }
+            }
+#endif
+
+            if (add == null)
+            {   // fallback: look for a public list.Add(object) method
+                types[0] = model.MapType(typeof(object));
+                add = Helpers.GetInstanceMethod(listTypeInfo, "Add", types);
+            }
+            if (add == null && isList)
+            {   // fallback: look for IList's Add(object) method
+                add = Helpers.GetInstanceMethod(model.MapType(ilist), "Add", types);
+            }
+            return add;
+        }
+        internal static Type GetListItemType(TypeModel model, Type listType)
+        {
+            Helpers.DebugAssert(listType != null);
+
+#if PROFILE259
+			TypeInfo listTypeInfo = listType.GetTypeInfo();
+            if (listType == typeof(string) || listType.IsArray
+                || !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(listTypeInfo)) return null;
+#else
+            if (listType == model.MapType(typeof(string)) || listType.IsArray
+                || !model.MapType(typeof(IEnumerable)).IsAssignableFrom(listType)) return null;
+#endif
+
+            BasicList candidates = new BasicList();
+#if PROFILE259
+			foreach (MethodInfo method in listType.GetRuntimeMethods())
+#else
+            foreach (MethodInfo method in listType.GetMethods())
+#endif
+            {
+                if (method.IsStatic || method.Name != "Add") continue;
+                ParameterInfo[] parameters = method.GetParameters();
+                Type paramType;
+                if (parameters.Length == 1 && !candidates.Contains(paramType = parameters[0].ParameterType))
+                {
+                    candidates.Add(paramType);
+                }
+            }
+
+            string name = listType.Name;
+            bool isQueueStack = name != null && (name.IndexOf("Queue") >= 0 || name.IndexOf("Stack") >= 0);
+
+            if (!isQueueStack)
+            {
+                TestEnumerableListPatterns(model, candidates, listType);
+#if PROFILE259
+				foreach (Type iType in listTypeInfo.ImplementedInterfaces)
+                {
+                    TestEnumerableListPatterns(model, candidates, iType);
+                }
+#else
+                foreach (Type iType in listType.GetInterfaces())
+                {
+                    TestEnumerableListPatterns(model, candidates, iType);
+                }
+#endif
+            }
+
+#if PROFILE259
+			// more convenient GetProperty overload not supported on all platforms
+			foreach (PropertyInfo indexer in listType.GetRuntimeProperties())
+            {
+                if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) continue;
+                ParameterInfo[] args = indexer.GetIndexParameters();
+                if (args.Length != 1 || args[0].ParameterType != typeof(int)) continue;
+                MethodInfo getter = indexer.GetMethod;
+                if (getter == null || getter.IsStatic) continue;
+                candidates.Add(indexer.PropertyType);
+            }
+#else
+            // more convenient GetProperty overload not supported on all platforms
+            foreach (PropertyInfo indexer in listType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+            {
+                if (indexer.Name != "Item" || candidates.Contains(indexer.PropertyType)) continue;
+                ParameterInfo[] args = indexer.GetIndexParameters();
+                if (args.Length != 1 || args[0].ParameterType != model.MapType(typeof(int))) continue;
+                candidates.Add(indexer.PropertyType);
+            }
+#endif
+
+            switch (candidates.Count)
+            {
+                case 0:
+                    return null;
+                case 1:
+                    if ((Type)candidates[0] == listType) return null; // recursive
+                    return (Type)candidates[0];
+                case 2:
+                    if ((Type)candidates[0] != listType && CheckDictionaryAccessors(model, (Type)candidates[0], (Type)candidates[1])) return (Type)candidates[0];
+                    if ((Type)candidates[1] != listType && CheckDictionaryAccessors(model, (Type)candidates[1], (Type)candidates[0])) return (Type)candidates[1];
+                    break;
+            }
+
+            return null;
+        }
+
+        private static void TestEnumerableListPatterns(TypeModel model, BasicList candidates, Type iType)
+        {
+
+#if COREFX || PROFILE259
+			TypeInfo iTypeInfo = iType.GetTypeInfo();
+            if (iTypeInfo.IsGenericType)
+            {
+                Type typeDef = iTypeInfo.GetGenericTypeDefinition();
+                if(
+                   typeDef == model.MapType(typeof(System.Collections.Generic.IEnumerable<>))
+                || typeDef == model.MapType(typeof(System.Collections.Generic.ICollection<>))
+                || typeDef.GetTypeInfo().FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
+                {
+                        
+                    Type[] iTypeArgs = iTypeInfo.GenericTypeArguments;
+                    if (!candidates.Contains(iTypeArgs[0]))
+                    {
+                        candidates.Add(iTypeArgs[0]);
+                    }
+                }
+            }
+#else
+            if (iType.IsGenericType)
+            {
+                Type typeDef = iType.GetGenericTypeDefinition();
+                if (typeDef == model.MapType(typeof(System.Collections.Generic.IEnumerable<>))
+                    || typeDef == model.MapType(typeof(System.Collections.Generic.ICollection<>))
+                    || typeDef.FullName == "System.Collections.Concurrent.IProducerConsumerCollection`1")
+                {
+                    Type[] iTypeArgs = iType.GetGenericArguments();
+                    if (!candidates.Contains(iTypeArgs[0]))
+                    {
+                        candidates.Add(iTypeArgs[0]);
+                    }
+                }
+            }
+#endif
+        }
+
+        private static bool CheckDictionaryAccessors(TypeModel model, Type pair, Type value)
+        {
+#if COREFX || PROFILE259
+			TypeInfo finalType = pair.GetTypeInfo();
+            return finalType.IsGenericType && finalType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)
+                && finalType.GenericTypeArguments[1] == value;
+#else
+            return pair.IsGenericType && pair.GetGenericTypeDefinition() == model.MapType(typeof(System.Collections.Generic.KeyValuePair<,>))
+                && pair.GetGenericArguments()[1] == value;
+#endif
+        }
+
+        private bool TryDeserializeList(TypeModel model, ProtoReader reader, DataFormat format, int tag, Type listType, Type itemType, ref object value)
+        {
+            MethodInfo addMethod = TypeModel.ResolveListAdd(model, listType, itemType, out bool isList);
+            if (addMethod == null) throw new NotSupportedException("Unknown list variant: " + listType.FullName);
+            bool found = false;
+            object nextItem = null;
+            IList list = value as IList;
+            object[] args = isList ? null : new object[1];
+            BasicList arraySurrogate = listType.IsArray ? new BasicList() : null;
+
+            while (TryDeserializeAuxiliaryType(reader, format, tag, itemType, ref nextItem, true, true, true, true, value ?? listType))
+            {
+                found = true;
+                if (value == null && arraySurrogate == null)
+                {
+                    value = CreateListInstance(listType, itemType);
+                    list = value as IList;
+                }
+                if (list != null)
+                {
+                    list.Add(nextItem);
+                }
+                else if (arraySurrogate != null)
+                {
+                    arraySurrogate.Add(nextItem);
+                }
+                else
+                {
+                    args[0] = nextItem;
+                    addMethod.Invoke(value, args);
+                }
+                nextItem = null;
+            }
+            if (arraySurrogate != null)
+            {
+                Array newArray;
+                if (value != null)
+                {
+                    if (arraySurrogate.Count == 0)
+                    {   // we'll stay with what we had, thanks
+                    }
+                    else
+                    {
+                        Array existing = (Array)value;
+                        newArray = Array.CreateInstance(itemType, existing.Length + arraySurrogate.Count);
+                        Array.Copy(existing, newArray, existing.Length);
+                        arraySurrogate.CopyTo(newArray, existing.Length);
+                        value = newArray;
+                    }
+                }
+                else
+                {
+                    newArray = Array.CreateInstance(itemType, arraySurrogate.Count);
+                    arraySurrogate.CopyTo(newArray, 0);
+                    value = newArray;
+                }
+            }
+            return found;
+        }
+
+        private static object CreateListInstance(Type listType, Type itemType)
+        {
+            Type concreteListType = listType;
+
+            if (listType.IsArray)
+            {
+                return Array.CreateInstance(itemType, 0);
+            }
+
+#if COREFX || PROFILE259
+			TypeInfo listTypeInfo = listType.GetTypeInfo();
+            if (!listTypeInfo.IsClass || listTypeInfo.IsAbstract ||
+                Helpers.GetConstructor(listTypeInfo, Helpers.EmptyTypes, true) == null)
+#else
+            if (!listType.IsClass || listType.IsAbstract ||
+                Helpers.GetConstructor(listType, Helpers.EmptyTypes, true) == null)
+#endif
+            {
+                string fullName;
+                bool handled = false;
+#if COREFX || PROFILE259
+				if (listTypeInfo.IsInterface &&
+#else
+                if (listType.IsInterface &&
+#endif
+                    (fullName = listType.FullName) != null && fullName.IndexOf("Dictionary") >= 0) // have to try to be frugal here...
+                {
+#if COREFX || PROFILE259
+					TypeInfo finalType = listType.GetTypeInfo();
+                    if (finalType.IsGenericType && finalType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>))
+                    {
+                        Type[] genericTypes = listType.GenericTypeArguments;
+                        concreteListType = typeof(System.Collections.Generic.Dictionary<,>).MakeGenericType(genericTypes);
+                        handled = true;
+                    }
+#else
+                    if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>))
+                    {
+                        Type[] genericTypes = listType.GetGenericArguments();
+                        concreteListType = typeof(System.Collections.Generic.Dictionary<,>).MakeGenericType(genericTypes);
+                        handled = true;
+                    }
+#endif
+
+#if !PORTABLE && !COREFX && !PROFILE259
+                    if (!handled && listType == typeof(IDictionary))
+                    {
+                        concreteListType = typeof(Hashtable);
+                        handled = true;
+                    }
+#endif
+                }
+
+                if (!handled)
+                {
+                    concreteListType = typeof(System.Collections.Generic.List<>).MakeGenericType(itemType);
+                    handled = true;
+                }
+
+#if !PORTABLE && !COREFX && !PROFILE259
+                if (!handled)
+                {
+                    concreteListType = typeof(ArrayList);
+                    handled = true;
+                }
+#endif
+            }
+            return Activator.CreateInstance(concreteListType);
+        }
+
+        /// <summary>
+        /// This is the more "complete" version of Deserialize, which handles single instances of mapped types.
+        /// The value is read as a complete field, including field-header and (for sub-objects) a
+        /// length-prefix..kmc  
+        /// 
+        /// In addition to that, this provides support for:
+        ///  - basic values; individual int / string / Guid / etc
+        ///  - IList sets of any type handled by TryDeserializeAuxiliaryType
+        /// </summary>
+        internal bool TryDeserializeAuxiliaryType(ProtoReader reader, DataFormat format, int tag, Type type, ref object value, bool skipOtherFields, bool asListItem, bool autoCreate, bool insideList, object parentListOrType)
+        {
+            if (type == null) throw new ArgumentNullException(nameof(type));
+            Type itemType = null;
+            ProtoTypeCode typecode = Helpers.GetTypeCode(type);
+            WireType wiretype = GetWireType(typecode, format, ref type, out int modelKey);
+
+            bool found = false;
+            if (wiretype == WireType.None)
+            {
+                itemType = GetListItemType(this, type);
+                if (itemType == null && type.IsArray && type.GetArrayRank() == 1 && type != typeof(byte[]))
+                {
+                    itemType = type.GetElementType();
+                }
+                if (itemType != null)
+                {
+                    if (insideList) throw TypeModel.CreateNestedListsNotSupported((parentListOrType as Type) ?? (parentListOrType?.GetType()));
+                    found = TryDeserializeList(this, reader, format, tag, type, itemType, ref value);
+                    if (!found && autoCreate)
+                    {
+                        value = CreateListInstance(type, itemType);
+                    }
+                    return found;
+                }
+
+                // otherwise, not a happy bunny...
+                ThrowUnexpectedType(type);
+            }
+
+            // to treat correctly, should read all values
+
+            while (true)
+            {
+                // for convenience (re complex exit conditions), additional exit test here:
+                // if we've got the value, are only looking for one, and we aren't a list - then exit
+                if (found && asListItem) break;
+
+
+                // read the next item
+                int fieldNumber = reader.ReadFieldHeader();
+                if (fieldNumber <= 0) break;
+                if (fieldNumber != tag)
+                {
+                    if (skipOtherFields)
+                    {
+                        reader.SkipField();
+                        continue;
+                    }
+                    throw ProtoReader.AddErrorData(new InvalidOperationException(
+                        "Expected field " + tag.ToString() + ", but found " + fieldNumber.ToString()), reader);
+                }
+                found = true;
+                reader.Hint(wiretype); // handle signed data etc
+
+                if (modelKey >= 0)
+                {
+                    switch (wiretype)
+                    {
+                        case WireType.String:
+                        case WireType.StartGroup:
+                            SubItemToken token = ProtoReader.StartSubItem(reader);
+                            value = Deserialize(modelKey, value, reader);
+                            ProtoReader.EndSubItem(token, reader);
+                            continue;
+                        default:
+                            value = Deserialize(modelKey, value, reader);
+                            continue;
+                    }
+                }
+                switch (typecode)
+                {
+                    case ProtoTypeCode.Int16: value = reader.ReadInt16(); continue;
+                    case ProtoTypeCode.Int32: value = reader.ReadInt32(); continue;
+                    case ProtoTypeCode.Int64: value = reader.ReadInt64(); continue;
+                    case ProtoTypeCode.UInt16: value = reader.ReadUInt16(); continue;
+                    case ProtoTypeCode.UInt32: value = reader.ReadUInt32(); continue;
+                    case ProtoTypeCode.UInt64: value = reader.ReadUInt64(); continue;
+                    case ProtoTypeCode.Boolean: value = reader.ReadBoolean(); continue;
+                    case ProtoTypeCode.SByte: value = reader.ReadSByte(); continue;
+                    case ProtoTypeCode.Byte: value = reader.ReadByte(); continue;
+                    case ProtoTypeCode.Char: value = (char)reader.ReadUInt16(); continue;
+                    case ProtoTypeCode.Double: value = reader.ReadDouble(); continue;
+                    case ProtoTypeCode.Single: value = reader.ReadSingle(); continue;
+                    case ProtoTypeCode.DateTime: value = BclHelpers.ReadDateTime(reader); continue;
+                    case ProtoTypeCode.Decimal: value = BclHelpers.ReadDecimal(reader); continue;
+                    case ProtoTypeCode.String: value = reader.ReadString(); continue;
+                    case ProtoTypeCode.ByteArray: value = ProtoReader.AppendBytes((byte[])value, reader); continue;
+                    case ProtoTypeCode.TimeSpan: value = BclHelpers.ReadTimeSpan(reader); continue;
+                    case ProtoTypeCode.Guid: value = BclHelpers.ReadGuid(reader); continue;
+                    case ProtoTypeCode.Uri: value = new Uri(reader.ReadString(), UriKind.RelativeOrAbsolute); continue;
+                }
+
+            }
+            if (!found && !asListItem && autoCreate)
+            {
+                if (type != typeof(string))
+                {
+                    value = Activator.CreateInstance(type);
+                }
+            }
+            return found;
+        }
+
+#if !NO_RUNTIME
+        /// <summary>
+        /// Creates a new runtime model, to which the caller
+        /// can add support for a range of types. A model
+        /// can be used "as is", or can be compiled for
+        /// optimal performance.
+        /// </summary>
+        [Obsolete("Please use RuntimeTypeModel.Create", false)]
+        public static RuntimeTypeModel Create()
+        {
+            return RuntimeTypeModel.Create();
+        }
+#endif
+
+        /// <summary>
+        /// Applies common proxy scenarios, resolving the actual type to consider
+        /// </summary>
+        protected internal static Type ResolveProxies(Type type)
+        {
+            if (type == null) return null;
+#if !NO_GENERICS
+            if (type.IsGenericParameter) return null;
+            // Nullable<T>
+            Type tmp = Helpers.GetUnderlyingType(type);
+            if (tmp != null) return tmp;
+#endif
+
+#if !CF
+            // EF POCO
+            string fullName = type.FullName;
+            if (fullName != null && fullName.StartsWith("System.Data.Entity.DynamicProxies."))
+            {
+#if COREFX || PROFILE259
+				return type.GetTypeInfo().BaseType;
+#else
+                return type.BaseType;
+#endif
+            }
+
+            // NHibernate
+#if PROFILE259
+			IEnumerable<Type> interfaces = type.GetTypeInfo().ImplementedInterfaces;
+#else
+            Type[] interfaces = type.GetInterfaces();
+#endif
+            foreach (Type t in interfaces)
+            {
+                switch (t.FullName)
+                {
+                    case "NHibernate.Proxy.INHibernateProxy":
+                    case "NHibernate.Proxy.DynamicProxy.IProxy":
+                    case "NHibernate.Intercept.IFieldInterceptorAccessor":
+#if COREFX || PROFILE259
+						return type.GetTypeInfo().BaseType;
+#else
+                        return type.BaseType;
+#endif
+                }
+            }
+#endif
+            return null;
+        }
+
+        /// <summary>
+        /// Indicates whether the supplied type is explicitly modelled by the model
+        /// </summary>
+        public bool IsDefined(Type type) => GetKey(ref type) >= 0;
+
+        readonly Dictionary<Type, KnownTypeKey> knownKeys = new Dictionary<Type, KnownTypeKey>();
+
+        // essentially just a ValueTuple<int,Type> - I just don't want the extra dependency
+        private readonly struct KnownTypeKey
+        {
+            public KnownTypeKey(Type type, int key)
+            {
+                Type = type;
+                Key = key;
+            }
+
+            public int Key { get; }
+
+            public Type Type { get; }
+        }
+
+        /// <summary>
+        /// Provides the key that represents a given type in the current model.
+        /// The type is also normalized for proxies at the same time.
+        /// </summary>
+        protected internal int GetKey(ref Type type)
+        {
+            if (type == null) return -1;
+            int key;
+            lock (knownKeys)
+            {
+                if (knownKeys.TryGetValue(type, out var tuple))
+                {
+                    // the type can be changed via ResolveProxies etc
+#if DEBUG
+                    var actualKey = GetKeyImpl(type);
+                    if(actualKey != tuple.Key)
+                    {
+                        throw new InvalidOperationException(
+                            $"Key cache failure; got {tuple.Key} instead of {actualKey} for '{type.Name}'");
+                    }
+#endif
+                    type = tuple.Type;
+                    return tuple.Key;
+                }
+            }
+            key = GetKeyImpl(type);
+            Type originalType = type;
+            if (key < 0)
+            {
+                Type normalized = ResolveProxies(type);
+                if (normalized != null && normalized != type)
+                {
+                    type = normalized; // hence ref
+                    key = GetKeyImpl(type);
+                }
+            }
+            lock (knownKeys)
+            {
+                knownKeys[originalType] = new KnownTypeKey(type, key);
+            }
+            return key;
+        }
+
+        /// <summary>
+        /// Advertise that a type's key can have changed
+        /// </summary>
+        internal void ResetKeyCache()
+        {
+            // clear *everything* (think: multi-level - can be many descendents)
+            lock(knownKeys)
+            {
+                knownKeys.Clear();
+            }
+        }
+
+        /// <summary>
+        /// Provides the key that represents a given type in the current model.
+        /// </summary>
+        protected abstract int GetKeyImpl(Type type);
+        /// <summary>
+        /// Writes a protocol-buffer representation of the given instance to the supplied stream.
+        /// </summary>
+        /// <param name="key">Represents the type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be serialized (cannot be null).</param>
+        /// <param name="dest">The destination stream to write to.</param>
+        protected internal abstract void Serialize(int key, object value, ProtoWriter dest);
+
+        /// <summary>
+        /// Applies a protocol-buffer stream to an existing instance (which may be null).
+        /// </summary>
+        /// <param name="key">Represents the type (including inheritance) to consider.</param>
+        /// <param name="value">The existing instance to be modified (can be null).</param>
+        /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
+        /// <returns>The updated instance; this may be different to the instance argument if
+        /// either the original instance was null, or the stream defines a known sub-type of the
+        /// original instance.</returns>
+        protected internal abstract object Deserialize(int key, object value, ProtoReader source);
+
+        //internal ProtoSerializer Create(IProtoSerializer head)
+        //{
+        //    return new RuntimeSerializer(head, this);
+        //}
+        //internal ProtoSerializer Compile
+
+        /// <summary>
+        /// Indicates the type of callback to be used
+        /// </summary>
+        protected internal enum CallbackType
+        {
+            /// <summary>
+            /// Invoked before an object is serialized
+            /// </summary>
+            BeforeSerialize,
+            /// <summary>
+            /// Invoked after an object is serialized
+            /// </summary>
+            AfterSerialize,
+            /// <summary>
+            /// Invoked before an object is deserialized (or when a new instance is created)
+            /// </summary>            
+            BeforeDeserialize,
+            /// <summary>
+            /// Invoked after an object is deserialized
+            /// </summary>
+            AfterDeserialize
+        }
+
+        /// <summary>
+        /// Create a deep clone of the supplied instance; any sub-items are also cloned.
+        /// </summary>
+        public object DeepClone(object value)
+        {
+            if (value == null) return null;
+            Type type = value.GetType();
+            int key = GetKey(ref type);
+
+            if (key >= 0 && !Helpers.IsEnum(type))
+            {
+                using (MemoryStream ms = new MemoryStream())
+                {
+                    using (ProtoWriter writer = ProtoWriter.Create(ms, this, null))
+                    {
+                        writer.SetRootObject(value);
+                        Serialize(key, value, writer);
+                        writer.Close();
+                    }
+                    ms.Position = 0;
+                    ProtoReader reader = null;
+                    try
+                    {
+                        reader = ProtoReader.Create(ms, this, null, ProtoReader.TO_EOF);
+                        return Deserialize(key, null, reader);
+                    }
+                    finally
+                    {
+                        ProtoReader.Recycle(reader);
+                    }
+                }
+            }
+            if (type == typeof(byte[]))
+            {
+                byte[] orig = (byte[])value, clone = new byte[orig.Length];
+                Buffer.BlockCopy(orig, 0, clone, 0, orig.Length);
+                return clone;
+            }
+            else if (GetWireType(Helpers.GetTypeCode(type), DataFormat.Default, ref type, out int modelKey) != WireType.None && modelKey < 0)
+            {   // immutable; just return the original value
+                return value;
+            }
+            using (MemoryStream ms = new MemoryStream())
+            {
+                using (ProtoWriter writer = ProtoWriter.Create(ms, this, null))
+                {
+                    if (!TrySerializeAuxiliaryType(writer, type, DataFormat.Default, Serializer.ListItemTag, value, false, null)) ThrowUnexpectedType(type);
+                    writer.Close();
+                }
+                ms.Position = 0;
+                ProtoReader reader = null;
+                try
+                {
+                    reader = ProtoReader.Create(ms, this, null, ProtoReader.TO_EOF);
+                    value = null; // start from scratch!
+                    TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false, null);
+                    return value;
+                }
+                finally
+                {
+                    ProtoReader.Recycle(reader);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Indicates that while an inheritance tree exists, the exact type encountered was not
+        /// specified in that hierarchy and cannot be processed.
+        /// </summary>
+        protected internal static void ThrowUnexpectedSubtype(Type expected, Type actual)
+        {
+            if (expected != TypeModel.ResolveProxies(actual))
+            {
+                throw new InvalidOperationException("Unexpected sub-type: " + actual.FullName);
+            }
+        }
+
+        /// <summary>
+        /// Indicates that the given type was not expected, and cannot be processed.
+        /// </summary>
+        protected internal static void ThrowUnexpectedType(Type type)
+        {
+            string fullName = type == null ? "(unknown)" : type.FullName;
+
+            if (type != null)
+            {
+                Type baseType = type
+#if COREFX || PROFILE259
+					.GetTypeInfo()
+#endif
+                    .BaseType;
+                if (baseType != null && baseType
+#if COREFX || PROFILE259
+					.GetTypeInfo()
+#endif
+                    .IsGenericType && baseType.GetGenericTypeDefinition().Name == "GeneratedMessage`2")
+                {
+                    throw new InvalidOperationException(
+                        "Are you mixing protobuf-net and protobuf-csharp-port? See https://stackoverflow.com/q/11564914/23354; type: " + fullName);
+                }
+            }
+
+            throw new InvalidOperationException("Type is not expected, and no contract can be inferred: " + fullName);
+        }
+
+        internal static Exception CreateNestedListsNotSupported(Type type)
+        {
+            return new NotSupportedException("Nested or jagged lists and arrays are not supported: " + (type?.FullName ?? "(null)"));
+        }
+        
+        /// <summary>
+        /// Indicates that the given type cannot be constructed; it may still be possible to 
+        /// deserialize into existing instances.
+        /// </summary>
+        public static void ThrowCannotCreateInstance(Type type)
+        {
+            throw new ProtoException("No parameterless constructor found for " + (type?.FullName ?? "(null)"));
+        }
+
+        internal static string SerializeType(TypeModel model, System.Type type)
+        {
+            if (model != null)
+            {
+                TypeFormatEventHandler handler = model.DynamicTypeFormatting;
+                if (handler != null)
+                {
+                    TypeFormatEventArgs args = new TypeFormatEventArgs(type);
+                    handler(model, args);
+                    if (!string.IsNullOrEmpty(args.FormattedName)) return args.FormattedName;
+                }
+            }
+            return type.AssemblyQualifiedName;
+        }
+
+        internal static Type DeserializeType(TypeModel model, string value)
+        {
+
+            if (model != null)
+            {
+                TypeFormatEventHandler handler = model.DynamicTypeFormatting;
+                if (handler != null)
+                {
+                    TypeFormatEventArgs args = new TypeFormatEventArgs(value);
+                    handler(model, args);
+                    if (args.Type != null) return args.Type;
+                }
+            }
+            return Type.GetType(value);
+        }
+
+        /// <summary>
+        /// Returns true if the type supplied is either a recognised contract type,
+        /// or a *list* of a recognised contract type. 
+        /// </summary>
+        /// <remarks>Note that primitives always return false, even though the engine
+        /// will, if forced, try to serialize such</remarks>
+        /// <returns>True if this type is recognised as a serializable entity, else false</returns>
+        public bool CanSerializeContractType(Type type) => CanSerialize(type, false, true, true);
+
+        /// <summary>
+        /// Returns true if the type supplied is a basic type with inbuilt handling,
+        /// a recognised contract type, or a *list* of a basic / contract type. 
+        /// </summary>
+        public bool CanSerialize(Type type) => CanSerialize(type, true, true, true);
+
+        /// <summary>
+        /// Returns true if the type supplied is a basic type with inbuilt handling,
+        /// or a *list* of a basic type with inbuilt handling
+        /// </summary>
+        public bool CanSerializeBasicType(Type type) => CanSerialize(type, true, false, true);
+
+        private bool CanSerialize(Type type, bool allowBasic, bool allowContract, bool allowLists)
+        {
+            if (type == null) throw new ArgumentNullException(nameof(type));
+            Type tmp = Helpers.GetUnderlyingType(type);
+            if (tmp != null) type = tmp;
+
+            // is it a basic type?
+            ProtoTypeCode typeCode = Helpers.GetTypeCode(type);
+            switch (typeCode)
+            {
+                case ProtoTypeCode.Empty:
+                case ProtoTypeCode.Unknown:
+                    break;
+                default:
+                    return allowBasic; // well-known basic type
+            }
+            int modelKey = GetKey(ref type);
+            if (modelKey >= 0) return allowContract; // known contract type
+
+            // is it a list?
+            if (allowLists)
+            {
+                Type itemType = null;
+                if (type.IsArray)
+                {   // note we don't need to exclude byte[], as that is handled by GetTypeCode already
+                    if (type.GetArrayRank() == 1) itemType = type.GetElementType();
+                }
+                else
+                {
+                    itemType = GetListItemType(this, type);
+                }
+                if (itemType != null) return CanSerialize(itemType, allowBasic, allowContract, false);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Suggest a .proto definition for the given type
+        /// </summary>
+        /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param>
+        /// <returns>The .proto definition as a string</returns>
+        public virtual string GetSchema(Type type) => GetSchema(type, ProtoSyntax.Proto2);
+
+        /// <summary>
+        /// Suggest a .proto definition for the given type
+        /// </summary>
+        /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param>
+        /// <returns>The .proto definition as a string</returns>
+        /// <param name="syntax">The .proto syntax to use for the operation</param>
+        public virtual string GetSchema(Type type, ProtoSyntax syntax)
+        {
+            throw new NotSupportedException();
+        }
+
+        /// <summary>
+        /// Used to provide custom services for writing and parsing type names when using dynamic types. Both parsing and formatting
+        /// are provided on a single API as it is essential that both are mapped identically at all times.
+        /// </summary>
+        public event TypeFormatEventHandler DynamicTypeFormatting;
+
+#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
+        /// <summary>
+        /// Creates a new IFormatter that uses protocol-buffer [de]serialization.
+        /// </summary>
+        /// <returns>A new IFormatter to be used during [de]serialization.</returns>
+        /// <param name="type">The type of object to be [de]deserialized by the formatter.</param>
+        public System.Runtime.Serialization.IFormatter CreateFormatter(Type type)
+        {
+            return new Formatter(this, type);
+        }
+
+        internal sealed class Formatter : System.Runtime.Serialization.IFormatter
+        {
+            private readonly TypeModel model;
+            private readonly Type type;
+            internal Formatter(TypeModel model, Type type)
+            {
+                this.model = model ?? throw new ArgumentNullException(nameof(model));
+                this.type = type ?? throw new ArgumentNullException(nameof(type));
+            }
+            private System.Runtime.Serialization.SerializationBinder binder;
+            public System.Runtime.Serialization.SerializationBinder Binder
+            {
+                get { return binder; }
+                set { binder = value; }
+            }
+
+            private System.Runtime.Serialization.StreamingContext context;
+            public System.Runtime.Serialization.StreamingContext Context
+            {
+                get { return context; }
+                set { context = value; }
+            }
+
+            public object Deserialize(Stream source)
+            {
+                return model.Deserialize(source, null, type, (long)-1, Context);
+            }
+
+            public void Serialize(Stream destination, object graph)
+            {
+                model.Serialize(destination, graph, Context);
+            }
+
+            private System.Runtime.Serialization.ISurrogateSelector surrogateSelector;
+            public System.Runtime.Serialization.ISurrogateSelector SurrogateSelector
+            {
+                get { return surrogateSelector; }
+                set { surrogateSelector = value; }
+            }
+        }
+#endif
+
+#if DEBUG // this is used by some unit tests only, to ensure no buffering when buffering is disabled
+        private bool forwardsOnly;
+        /// <summary>
+        /// If true, buffering of nested objects is disabled
+        /// </summary>
+        public bool ForwardsOnly
+        {
+            get { return forwardsOnly; }
+            set { forwardsOnly = value; }
+        }
+#endif
+
+        internal virtual Type GetType(string fullName, Assembly context)
+        {
+            return ResolveKnownType(fullName, this, context);
+        }
+
+        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+        internal static Type ResolveKnownType(string name, TypeModel model, Assembly assembly)
+        {
+            if (string.IsNullOrEmpty(name)) return null;
+            try
+            {
+                Type type = Type.GetType(name);
+
+                if (type != null) return type;
+            }
+            catch { }
+            try
+            {
+                int i = name.IndexOf(',');
+                string fullName = (i > 0 ? name.Substring(0, i) : name).Trim();
+#if !(COREFX || PROFILE259)
+                if (assembly == null) assembly = Assembly.GetCallingAssembly();
+#endif
+                Type type = assembly?.GetType(fullName);
+                if (type != null) return type;
+            }
+            catch { }
+            return null;
+        }
+    }
+}

+ 855 - 0
Server/ThirdParty/protobuf-net/Meta/ValueMember.cs

@@ -0,0 +1,855 @@
+#if !NO_RUNTIME
+using System;
+
+using ProtoBuf.Serializers;
+using System.Globalization;
+using System.Collections.Generic;
+
+#if PROFILE259
+using System.Reflection;
+using System.Linq;
+#else
+using System.Reflection;
+#endif
+
+namespace ProtoBuf.Meta
+{
+    /// <summary>
+    /// Represents a member (property/field) that is mapped to a protobuf field
+    /// </summary>
+    public class ValueMember
+    {
+        private int _fieldNumber;
+        /// <summary>
+        /// The number that identifies this member in a protobuf stream
+        /// </summary>
+        public int FieldNumber
+        {
+            get => _fieldNumber;
+            internal set
+            {
+                if (_fieldNumber != value)
+                {
+                    MetaType.AssertValidFieldNumber(value);
+                    ThrowIfFrozen();
+                    _fieldNumber = value;
+                }
+            }
+        }
+
+        private readonly MemberInfo originalMember;
+        private MemberInfo backingMember;
+        /// <summary>
+        /// Gets the member (field/property) which this member relates to.
+        /// </summary>
+        public MemberInfo Member { get { return originalMember; } }
+        /// <summary>
+        /// Gets the backing member (field/property) which this member relates to
+        /// </summary>
+        public MemberInfo BackingMember
+        {
+            get { return backingMember; }
+            set
+            {
+                if (backingMember != value)
+                {
+                    ThrowIfFrozen();
+                    backingMember = value;
+                }
+            }
+        }
+
+        private readonly Type parentType, itemType, defaultType, memberType;
+        private object defaultValue;
+
+        /// <summary>
+        /// Within a list / array / etc, the type of object for each item in the list (especially useful with ArrayList)
+        /// </summary>
+        public Type ItemType => itemType;
+
+        /// <summary>
+        /// The underlying type of the member
+        /// </summary>
+        public Type MemberType => memberType;
+
+        /// <summary>
+        /// For abstract types (IList etc), the type of concrete object to create (if required)
+        /// </summary>
+        public Type DefaultType => defaultType;
+
+        /// <summary>
+        /// The type the defines the member
+        /// </summary>
+        public Type ParentType => parentType;
+
+        /// <summary>
+        /// The default value of the item (members with this value will not be serialized)
+        /// </summary>
+        public object DefaultValue
+        {
+            get { return defaultValue; }
+            set
+            {
+                if (defaultValue != value)
+                {
+                    ThrowIfFrozen();
+                    defaultValue = value;
+                }
+            }
+        }
+
+        private readonly RuntimeTypeModel model;
+        /// <summary>
+        /// Creates a new ValueMember instance
+        /// </summary>
+        public ValueMember(RuntimeTypeModel model, Type parentType, int fieldNumber, MemberInfo member, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat, object defaultValue)
+            : this(model, fieldNumber, memberType, itemType, defaultType, dataFormat)
+        {
+            if (parentType == null) throw new ArgumentNullException("parentType");
+            if (fieldNumber < 1 && !Helpers.IsEnum(parentType)) throw new ArgumentOutOfRangeException("fieldNumber");
+
+            this.originalMember = member ?? throw new ArgumentNullException("member");
+            this.parentType = parentType;
+            if (fieldNumber < 1 && !Helpers.IsEnum(parentType)) throw new ArgumentOutOfRangeException("fieldNumber");
+            //#if WINRT
+            if (defaultValue != null && model.MapType(defaultValue.GetType()) != memberType)
+            //#else
+            //            if (defaultValue != null && !memberType.IsInstanceOfType(defaultValue))
+            //#endif
+            {
+                defaultValue = ParseDefaultValue(memberType, defaultValue);
+            }
+            this.defaultValue = defaultValue;
+
+            MetaType type = model.FindWithoutAdd(memberType);
+            if (type != null)
+            {
+                AsReference = type.AsReferenceDefault;
+            }
+            else
+            { // we need to scan the hard way; can't risk recursion by fully walking it
+                AsReference = MetaType.GetAsReferenceDefault(model, memberType);
+            }
+        }
+        /// <summary>
+        /// Creates a new ValueMember instance
+        /// </summary>
+        internal ValueMember(RuntimeTypeModel model, int fieldNumber, Type memberType, Type itemType, Type defaultType, DataFormat dataFormat)
+        {
+            _fieldNumber = fieldNumber;
+            this.memberType = memberType ?? throw new ArgumentNullException(nameof(memberType));
+            this.itemType = itemType;
+            this.defaultType = defaultType;
+
+            this.model = model ?? throw new ArgumentNullException(nameof(model));
+            this.dataFormat = dataFormat;
+        }
+        internal object GetRawEnumValue()
+        {
+#if PORTABLE || CF || COREFX || PROFILE259
+			object value = ((FieldInfo)originalMember).GetValue(null);
+            switch(Helpers.GetTypeCode(Enum.GetUnderlyingType(((FieldInfo)originalMember).FieldType)))
+            {
+                case ProtoTypeCode.SByte: return (sbyte)value;
+                case ProtoTypeCode.Byte: return (byte)value;
+                case ProtoTypeCode.Int16: return (short)value;
+                case ProtoTypeCode.UInt16: return (ushort)value;
+                case ProtoTypeCode.Int32: return (int)value;
+                case ProtoTypeCode.UInt32: return (uint)value;
+                case ProtoTypeCode.Int64: return (long)value;
+                case ProtoTypeCode.UInt64: return (ulong)value;
+                default:
+                    throw new InvalidOperationException();
+            }
+#else
+            return ((FieldInfo)originalMember).GetRawConstantValue();
+#endif
+        }
+        private static object ParseDefaultValue(Type type, object value)
+        {
+            {
+                Type tmp = Helpers.GetUnderlyingType(type);
+                if (tmp != null) type = tmp;
+            }
+            if (value is string s)
+            {
+                if (Helpers.IsEnum(type)) return Helpers.ParseEnum(type, s);
+
+                switch (Helpers.GetTypeCode(type))
+                {
+                    case ProtoTypeCode.Boolean: return bool.Parse(s);
+                    case ProtoTypeCode.Byte: return byte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Char: // char.Parse missing on CF/phone7
+                        if (s.Length == 1) return s[0];
+                        throw new FormatException("Single character expected: \"" + s + "\"");
+                    case ProtoTypeCode.DateTime: return DateTime.Parse(s, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Decimal: return decimal.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Double: return double.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Int16: return short.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Int32: return int.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Int64: return long.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.SByte: return sbyte.Parse(s, NumberStyles.Integer, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.Single: return float.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.String: return s;
+                    case ProtoTypeCode.UInt16: return ushort.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.UInt32: return uint.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.UInt64: return ulong.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture);
+                    case ProtoTypeCode.TimeSpan: return TimeSpan.Parse(s);
+                    case ProtoTypeCode.Uri: return s; // Uri is decorated as string
+                    case ProtoTypeCode.Guid: return new Guid(s);
+                }
+            }
+
+            if (Helpers.IsEnum(type)) return Enum.ToObject(type, value);
+            return Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
+
+        }
+
+        private IProtoSerializer serializer;
+        internal IProtoSerializer Serializer
+        {
+            get
+            {
+                return serializer ?? (serializer = BuildSerializer());
+            }
+        }
+
+        private DataFormat dataFormat;
+        /// <summary>
+        /// Specifies the rules used to process the field; this is used to determine the most appropriate
+        /// wite-type, but also to describe subtypes <i>within</i> that wire-type (such as SignedVariant)
+        /// </summary>
+        public DataFormat DataFormat
+        {
+            get { return dataFormat; }
+            set
+            {
+                if (value != dataFormat)
+                {
+                    ThrowIfFrozen();
+                    this.dataFormat = value;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Indicates whether this field should follow strict encoding rules; this means (for example) that if a "fixed32"
+        /// is encountered when "variant" is defined, then it will fail (throw an exception) when parsing. Note that
+        /// when serializing the defined type is always used.
+        /// </summary>
+        public bool IsStrict
+        {
+            get { return HasFlag(OPTIONS_IsStrict); }
+            set { SetFlag(OPTIONS_IsStrict, value, true); }
+        }
+
+        /// <summary>
+        /// Indicates whether this field should use packed encoding (which can save lots of space for repeated primitive values).
+        /// This option only applies to list/array data of primitive types (int, double, etc).
+        /// </summary>
+        public bool IsPacked
+        {
+            get { return HasFlag(OPTIONS_IsPacked); }
+            set { SetFlag(OPTIONS_IsPacked, value, true); }
+        }
+
+        /// <summary>
+        /// Indicates whether this field should *repace* existing values (the default is false, meaning *append*).
+        /// This option only applies to list/array data.
+        /// </summary>
+        public bool OverwriteList
+        {
+            get { return HasFlag(OPTIONS_OverwriteList); }
+            set { SetFlag(OPTIONS_OverwriteList, value, true); }
+        }
+
+        /// <summary>
+        /// Indicates whether this field is mandatory.
+        /// </summary>
+        public bool IsRequired
+        {
+            get { return HasFlag(OPTIONS_IsRequired); }
+            set { SetFlag(OPTIONS_IsRequired, value, true); }
+        }
+
+        /// <summary>
+        /// Enables full object-tracking/full-graph support.
+        /// </summary>
+        public bool AsReference
+        {
+            get { return HasFlag(OPTIONS_AsReference); }
+            set { SetFlag(OPTIONS_AsReference, value, true); }
+        }
+
+        /// <summary>
+        /// Embeds the type information into the stream, allowing usage with types not known in advance.
+        /// </summary>
+        public bool DynamicType
+        {
+            get { return HasFlag(OPTIONS_DynamicType); }
+            set { SetFlag(OPTIONS_DynamicType, value, true); }
+        }
+
+        /// <summary>
+        /// Indicates that the member should be treated as a protobuf Map
+        /// </summary>
+        public bool IsMap
+        {
+            get { return HasFlag(OPTIONS_IsMap); }
+            set { SetFlag(OPTIONS_IsMap, value, true); }
+        }
+
+        private DataFormat mapKeyFormat, mapValueFormat;
+        /// <summary>
+        /// Specifies the data-format that should be used for the key, when IsMap is enabled
+        /// </summary>
+        public DataFormat MapKeyFormat
+        {
+            get { return mapKeyFormat; }
+            set
+            {
+                if (mapKeyFormat != value)
+                {
+                    ThrowIfFrozen();
+                    mapKeyFormat = value;
+                }
+            }
+        }
+        /// <summary>
+        /// Specifies the data-format that should be used for the value, when IsMap is enabled
+        /// </summary>
+        public DataFormat MapValueFormat
+        {
+            get { return mapValueFormat; }
+            set
+            {
+                if (mapValueFormat != value)
+                {
+                    ThrowIfFrozen();
+                    mapValueFormat = value;
+                }
+            }
+        }
+
+        private MethodInfo getSpecified, setSpecified;
+        /// <summary>
+        /// Specifies methods for working with optional data members.
+        /// </summary>
+        /// <param name="getSpecified">Provides a method (null for none) to query whether this member should
+        /// be serialized; it must be of the form "bool {Method}()". The member is only serialized if the
+        /// method returns true.</param>
+        /// <param name="setSpecified">Provides a method (null for none) to indicate that a member was
+        /// deserialized; it must be of the form "void {Method}(bool)", and will be called with "true"
+        /// when data is found.</param>
+        public void SetSpecified(MethodInfo getSpecified, MethodInfo setSpecified)
+        {
+            if (this.getSpecified != getSpecified || this.setSpecified != setSpecified)
+            {
+                if (getSpecified != null)
+                {
+                    if (getSpecified.ReturnType != model.MapType(typeof(bool))
+                        || getSpecified.IsStatic
+                        || getSpecified.GetParameters().Length != 0)
+                    {
+                        throw new ArgumentException("Invalid pattern for checking member-specified", "getSpecified");
+                    }
+                }
+                if (setSpecified != null)
+                {
+                    ParameterInfo[] args;
+                    if (setSpecified.ReturnType != model.MapType(typeof(void))
+                        || setSpecified.IsStatic
+                        || (args = setSpecified.GetParameters()).Length != 1
+                        || args[0].ParameterType != model.MapType(typeof(bool)))
+                    {
+                        throw new ArgumentException("Invalid pattern for setting member-specified", "setSpecified");
+                    }
+                }
+
+                ThrowIfFrozen();
+                this.getSpecified = getSpecified;
+                this.setSpecified = setSpecified;
+            }
+        }
+
+        private void ThrowIfFrozen()
+        {
+            if (serializer != null) throw new InvalidOperationException("The type cannot be changed once a serializer has been generated");
+        }
+
+        internal bool ResolveMapTypes(out Type dictionaryType, out Type keyType, out Type valueType)
+        {
+            dictionaryType = keyType = valueType = null;
+            try
+            {
+#if COREFX || PROFILE259
+				var info = memberType.GetTypeInfo();
+#else
+                var info = memberType;
+#endif
+                if (ImmutableCollectionDecorator.IdentifyImmutable(model, MemberType, out _, out _, out _, out _, out _, out _))
+                {
+                    return false;
+                }
+                if (info.IsInterface && info.IsGenericType && info.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+                {
+#if PROFILE259
+					var typeArgs = memberType.GetGenericTypeDefinition().GenericTypeArguments;
+#else
+                    var typeArgs = memberType.GetGenericArguments();
+#endif
+                    if (IsValidMapKeyType(typeArgs[0]))
+                    {
+                        keyType = typeArgs[0];
+                        valueType = typeArgs[1];
+                        dictionaryType = memberType;
+                    }
+                    return false;
+                }
+#if PROFILE259
+				foreach (var iType in memberType.GetTypeInfo().ImplementedInterfaces)
+#else
+                foreach (var iType in memberType.GetInterfaces())
+#endif
+                {
+#if COREFX || PROFILE259
+					info = iType.GetTypeInfo();
+#else
+                    info = iType;
+#endif
+                    if (info.IsGenericType && info.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+                    {
+                        if (dictionaryType != null) throw new InvalidOperationException("Multiple dictionary interfaces implemented by type: " + memberType.FullName);
+#if PROFILE259
+						var typeArgs = iType.GetGenericTypeDefinition().GenericTypeArguments;
+#else
+                        var typeArgs = iType.GetGenericArguments();
+#endif
+                        if (IsValidMapKeyType(typeArgs[0]))
+                        {
+                            keyType = typeArgs[0];
+                            valueType = typeArgs[1];
+                            dictionaryType = memberType;
+                        }
+                    }
+                }
+                if (dictionaryType == null) return false;
+
+                // (note we checked the key type already)
+                // not a map if value is repeated
+                Type itemType = null, defaultType = null;
+                model.ResolveListTypes(valueType, ref itemType, ref defaultType);
+                if (itemType != null) return false;
+
+                return dictionaryType != null;
+            }
+            catch
+            {
+                // if it isn't a good fit; don't use "map"
+                return false;
+            }
+        }
+
+        static bool IsValidMapKeyType(Type type)
+        {
+            if (type == null || Helpers.IsEnum(type)) return false;
+            switch (Helpers.GetTypeCode(type))
+            {
+                case ProtoTypeCode.Boolean:
+                case ProtoTypeCode.Byte:
+                case ProtoTypeCode.Char:
+                case ProtoTypeCode.Int16:
+                case ProtoTypeCode.Int32:
+                case ProtoTypeCode.Int64:
+                case ProtoTypeCode.String:
+
+                case ProtoTypeCode.SByte:
+                case ProtoTypeCode.UInt16:
+                case ProtoTypeCode.UInt32:
+                case ProtoTypeCode.UInt64:
+                    return true;
+            }
+            return false;
+        }
+        private IProtoSerializer BuildSerializer()
+        {
+            int opaqueToken = 0;
+            try
+            {
+                model.TakeLock(ref opaqueToken);// check nobody is still adding this type
+                var member = backingMember ?? originalMember;
+                IProtoSerializer ser;
+                if (IsMap)
+                {
+                    ResolveMapTypes(out var dictionaryType, out var keyType, out var valueType);
+
+                    if (dictionaryType == null)
+                    {
+                        throw new InvalidOperationException("Unable to resolve map type for type: " + memberType.FullName);
+                    }
+                    var concreteType = defaultType;
+                    if (concreteType == null && Helpers.IsClass(memberType))
+                    {
+                        concreteType = memberType;
+                    }
+                    var keySer = TryGetCoreSerializer(model, MapKeyFormat, keyType, out var keyWireType, false, false, false, false);
+                    if (!AsReference)
+                    {
+                        AsReference = MetaType.GetAsReferenceDefault(model, valueType);
+                    }
+                    var valueSer = TryGetCoreSerializer(model, MapValueFormat, valueType, out var valueWireType, AsReference, DynamicType, false, true);
+#if PROFILE259
+					IEnumerable<ConstructorInfo> ctors = typeof(MapDecorator<,,>).MakeGenericType(new Type[] { dictionaryType, keyType, valueType }).GetTypeInfo().DeclaredConstructors;
+	                if (ctors.Count() != 1)
+	                {
+		                throw new InvalidOperationException("Unable to resolve MapDecorator constructor");
+	                }
+	                ser = (IProtoSerializer)ctors.First().Invoke(new object[] {model, concreteType, keySer, valueSer, _fieldNumber,
+		                DataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String, keyWireType, valueWireType, OverwriteList });
+#else
+                    var ctors = typeof(MapDecorator<,,>).MakeGenericType(new Type[] { dictionaryType, keyType, valueType }).GetConstructors(
+                        BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+                    if (ctors.Length != 1) throw new InvalidOperationException("Unable to resolve MapDecorator constructor");
+                    ser = (IProtoSerializer)ctors[0].Invoke(new object[] {model, concreteType, keySer, valueSer, _fieldNumber,
+                        DataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String, keyWireType, valueWireType, OverwriteList });
+#endif
+                }
+                else
+                {
+                    Type finalType = itemType ?? memberType;
+                    ser = TryGetCoreSerializer(model, dataFormat, finalType, out WireType wireType, AsReference, DynamicType, OverwriteList, true);
+                    if (ser == null)
+                    {
+                        throw new InvalidOperationException("No serializer defined for type: " + finalType.FullName);
+                    }
+
+                    // apply tags
+                    if (itemType != null && SupportNull)
+                    {
+                        if (IsPacked)
+                        {
+                            throw new NotSupportedException("Packed encodings cannot support null values");
+                        }
+                        ser = new TagDecorator(NullDecorator.Tag, wireType, IsStrict, ser);
+                        ser = new NullDecorator(model, ser);
+                        ser = new TagDecorator(_fieldNumber, WireType.StartGroup, false, ser);
+                    }
+                    else
+                    {
+                        ser = new TagDecorator(_fieldNumber, wireType, IsStrict, ser);
+                    }
+                    // apply lists if appropriate
+                    if (itemType != null)
+                    {
+                        Type underlyingItemType = SupportNull ? itemType : Helpers.GetUnderlyingType(itemType) ?? itemType;
+
+                        Helpers.DebugAssert(underlyingItemType == ser.ExpectedType
+                            || (ser.ExpectedType == model.MapType(typeof(object)) && !Helpers.IsValueType(underlyingItemType))
+                            , "Wrong type in the tail; expected {0}, received {1}", ser.ExpectedType, underlyingItemType);
+                        if (memberType.IsArray)
+                        {
+                            ser = new ArrayDecorator(model, ser, _fieldNumber, IsPacked, wireType, memberType, OverwriteList, SupportNull);
+                        }
+                        else
+                        {
+                            ser = ListDecorator.Create(model, memberType, defaultType, ser, _fieldNumber, IsPacked, wireType, member != null && PropertyDecorator.CanWrite(model, member), OverwriteList, SupportNull);
+                        }
+                    }
+                    else if (defaultValue != null && !IsRequired && getSpecified == null)
+                    {   // note: "ShouldSerialize*" / "*Specified" / etc ^^^^ take precedence over defaultValue,
+                        // as does "IsRequired"
+                        ser = new DefaultValueDecorator(model, defaultValue, ser);
+                    }
+                    if (memberType == model.MapType(typeof(Uri)))
+                    {
+                        ser = new UriDecorator(model, ser);
+                    }
+#if PORTABLE
+                    else if(memberType.FullName == typeof(Uri).FullName)
+                    {
+                        // In PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri)
+                        ser = new ReflectedUriDecorator(memberType, model, ser);
+                    }
+#endif
+                }
+                if (member != null)
+                {
+                    if (member is PropertyInfo prop)
+                    {
+                        ser = new PropertyDecorator(model, parentType, prop, ser);
+                    }
+                    else if (member is FieldInfo fld)
+                    {
+                        ser = new FieldDecorator(parentType, fld, ser);
+                    }
+                    else
+                    {
+                        throw new InvalidOperationException();
+                    }
+                    
+                    if (getSpecified != null || setSpecified != null)
+                    {
+                        ser = new MemberSpecifiedDecorator(getSpecified, setSpecified, ser);
+                    }
+                }
+                return ser;
+            }
+            finally
+            {
+                model.ReleaseLock(opaqueToken);
+            }
+        }
+
+        private static WireType GetIntWireType(DataFormat format, int width)
+        {
+            switch (format)
+            {
+                case DataFormat.ZigZag: return WireType.SignedVariant;
+                case DataFormat.FixedSize: return width == 32 ? WireType.Fixed32 : WireType.Fixed64;
+                case DataFormat.TwosComplement:
+                case DataFormat.Default: return WireType.Variant;
+                default: throw new InvalidOperationException();
+            }
+        }
+        private static WireType GetDateTimeWireType(DataFormat format)
+        {
+            switch (format)
+            {
+
+                case DataFormat.Group: return WireType.StartGroup;
+                case DataFormat.FixedSize: return WireType.Fixed64;
+                case DataFormat.WellKnown:
+                case DataFormat.Default:
+                    return WireType.String;
+                default: throw new InvalidOperationException();
+            }
+        }
+
+        internal static IProtoSerializer TryGetCoreSerializer(RuntimeTypeModel model, DataFormat dataFormat, Type type, out WireType defaultWireType,
+            bool asReference, bool dynamicType, bool overwriteList, bool allowComplexTypes)
+        {
+            {
+                Type tmp = Helpers.GetUnderlyingType(type);
+                if (tmp != null) type = tmp;
+            }
+            if (Helpers.IsEnum(type))
+            {
+                if (allowComplexTypes && model != null)
+                {
+                    // need to do this before checking the typecode; an int enum will report Int32 etc
+                    defaultWireType = WireType.Variant;
+                    return new EnumSerializer(type, model.GetEnumMap(type));
+                }
+                else
+                { // enum is fine for adding as a meta-type
+                    defaultWireType = WireType.None;
+                    return null;
+                }
+            }
+            ProtoTypeCode code = Helpers.GetTypeCode(type);
+            switch (code)
+            {
+                case ProtoTypeCode.Int32:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new Int32Serializer(model);
+                case ProtoTypeCode.UInt32:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new UInt32Serializer(model);
+                case ProtoTypeCode.Int64:
+                    defaultWireType = GetIntWireType(dataFormat, 64);
+                    return new Int64Serializer(model);
+                case ProtoTypeCode.UInt64:
+                    defaultWireType = GetIntWireType(dataFormat, 64);
+                    return new UInt64Serializer(model);
+                case ProtoTypeCode.String:
+                    defaultWireType = WireType.String;
+                    if (asReference)
+                    {
+                        return new NetObjectSerializer(model, model.MapType(typeof(string)), 0, BclHelpers.NetObjectOptions.AsReference);
+                    }
+                    return new StringSerializer(model);
+                case ProtoTypeCode.Single:
+                    defaultWireType = WireType.Fixed32;
+                    return new SingleSerializer(model);
+                case ProtoTypeCode.Double:
+                    defaultWireType = WireType.Fixed64;
+                    return new DoubleSerializer(model);
+                case ProtoTypeCode.Boolean:
+                    defaultWireType = WireType.Variant;
+                    return new BooleanSerializer(model);
+                case ProtoTypeCode.DateTime:
+                    defaultWireType = GetDateTimeWireType(dataFormat);
+                    return new DateTimeSerializer(dataFormat, model);
+                case ProtoTypeCode.Decimal:
+                    defaultWireType = WireType.String;
+                    return new DecimalSerializer(model);
+                case ProtoTypeCode.Byte:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new ByteSerializer(model);
+                case ProtoTypeCode.SByte:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new SByteSerializer(model);
+                case ProtoTypeCode.Char:
+                    defaultWireType = WireType.Variant;
+                    return new CharSerializer(model);
+                case ProtoTypeCode.Int16:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new Int16Serializer(model);
+                case ProtoTypeCode.UInt16:
+                    defaultWireType = GetIntWireType(dataFormat, 32);
+                    return new UInt16Serializer(model);
+                case ProtoTypeCode.TimeSpan:
+                    defaultWireType = GetDateTimeWireType(dataFormat);
+                    return new TimeSpanSerializer(dataFormat, model);
+                case ProtoTypeCode.Guid:
+                    defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
+                    return new GuidSerializer(model);
+                case ProtoTypeCode.Uri:
+                    defaultWireType = WireType.String;
+                    return new StringSerializer(model);
+                case ProtoTypeCode.ByteArray:
+                    defaultWireType = WireType.String;
+                    return new BlobSerializer(model, overwriteList);
+                case ProtoTypeCode.Type:
+                    defaultWireType = WireType.String;
+                    return new SystemTypeSerializer(model);
+            }
+            IProtoSerializer parseable = model.AllowParseableTypes ? ParseableSerializer.TryCreate(type, model) : null;
+            if (parseable != null)
+            {
+                defaultWireType = WireType.String;
+                return parseable;
+            }
+            if (allowComplexTypes && model != null)
+            {
+                int key = model.GetKey(type, false, true);
+                MetaType meta = null;
+                if (key >= 0)
+                {
+                    meta = model[type];
+                    if (dataFormat == DataFormat.Default && meta.IsGroup)
+                    {
+                        dataFormat = DataFormat.Group;
+                    }
+                }
+
+                if (asReference || dynamicType)
+                {
+                    BclHelpers.NetObjectOptions options = BclHelpers.NetObjectOptions.None;
+                    if (asReference) options |= BclHelpers.NetObjectOptions.AsReference;
+                    if (dynamicType) options |= BclHelpers.NetObjectOptions.DynamicType;
+                    if (meta != null)
+                    { // exists
+                        if (asReference && Helpers.IsValueType(type))
+                        {
+                            string message = "AsReference cannot be used with value-types";
+
+                            if (type.Name == "KeyValuePair`2")
+                            {
+                                message += "; please see https://stackoverflow.com/q/14436606/23354";
+                            }
+                            else
+                            {
+                                message += ": " + type.FullName;
+                            }
+                            throw new InvalidOperationException(message);
+                        }
+
+                        if (asReference && meta.IsAutoTuple) options |= BclHelpers.NetObjectOptions.LateSet;
+                        if (meta.UseConstructor) options |= BclHelpers.NetObjectOptions.UseConstructor;
+                    }
+                    defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
+                    return new NetObjectSerializer(model, type, key, options);
+                }
+                if (key >= 0)
+                {
+                    defaultWireType = dataFormat == DataFormat.Group ? WireType.StartGroup : WireType.String;
+                    return new SubItemSerializer(type, key, meta, true);
+                }
+            }
+            defaultWireType = WireType.None;
+            return null;
+        }
+
+
+        private string name;
+        internal void SetName(string name)
+        {
+            if (name != this.name)
+            {
+                ThrowIfFrozen();
+                this.name = name;
+            }
+        }
+        /// <summary>
+        /// Gets the logical name for this member in the schema (this is not critical for binary serialization, but may be used
+        /// when inferring a schema).
+        /// </summary>
+        public string Name
+        {
+            get { return string.IsNullOrEmpty(name) ? originalMember.Name : name; }
+            set { SetName(value); }
+        }
+
+        private const byte
+           OPTIONS_IsStrict = 1,
+           OPTIONS_IsPacked = 2,
+           OPTIONS_IsRequired = 4,
+           OPTIONS_OverwriteList = 8,
+           OPTIONS_SupportNull = 16,
+           OPTIONS_AsReference = 32,
+           OPTIONS_IsMap = 64,
+           OPTIONS_DynamicType = 128;
+
+        private byte flags;
+        private bool HasFlag(byte flag) { return (flags & flag) == flag; }
+        private void SetFlag(byte flag, bool value, bool throwIfFrozen)
+        {
+            if (throwIfFrozen && HasFlag(flag) != value)
+            {
+                ThrowIfFrozen();
+            }
+            if (value)
+                flags |= flag;
+            else
+                flags = (byte)(flags & ~flag);
+        }
+
+        /// <summary>
+        /// Should lists have extended support for null values? Note this makes the serialization less efficient.
+        /// </summary>
+        public bool SupportNull
+        {
+            get { return HasFlag(OPTIONS_SupportNull); }
+            set { SetFlag(OPTIONS_SupportNull, value, true); }
+        }
+
+        internal string GetSchemaTypeName(bool applyNetObjectProxy, ref RuntimeTypeModel.CommonImports imports)
+        {
+            Type effectiveType = ItemType;
+            if (effectiveType == null) effectiveType = MemberType;
+            return model.GetSchemaTypeName(effectiveType, DataFormat, applyNetObjectProxy && AsReference, applyNetObjectProxy && DynamicType, ref imports);
+        }
+
+
+        internal sealed class Comparer : System.Collections.IComparer, IComparer<ValueMember>
+        {
+            public static readonly Comparer Default = new Comparer();
+
+            public int Compare(object x, object y)
+            {
+                return Compare(x as ValueMember, y as ValueMember);
+            }
+
+            public int Compare(ValueMember x, ValueMember y)
+            {
+                if (ReferenceEquals(x, y)) return 0;
+                if (x == null) return -1;
+                if (y == null) return 1;
+
+                return x.FieldNumber.CompareTo(y.FieldNumber);
+            }
+        }
+    }
+}
+#endif

+ 190 - 0
Server/ThirdParty/protobuf-net/NetObjectCache.cs

@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using ProtoBuf.Meta;
+
+namespace ProtoBuf
+{
+    internal sealed class NetObjectCache
+    {
+        internal const int Root = 0;
+        private MutableList underlyingList;
+
+        private MutableList List => underlyingList ?? (underlyingList = new MutableList());
+
+        internal object GetKeyedObject(int key)
+        {
+            if (key-- == Root)
+            {
+                if (rootObject == null) throw new ProtoException("No root object assigned");
+                return rootObject;
+            }
+            BasicList list = List;
+
+            if (key < 0 || key >= list.Count)
+            {
+                Helpers.DebugWriteLine("Missing key: " + key);
+                throw new ProtoException("Internal error; a missing key occurred");
+            }
+
+            object tmp = list[key];
+            if (tmp == null)
+            {
+                throw new ProtoException("A deferred key does not have a value yet");
+            }
+            return tmp;
+        }
+
+        internal void SetKeyedObject(int key, object value)
+        {
+            if (key-- == Root)
+            {
+                if (value == null) throw new ArgumentNullException(nameof(value));
+                if (rootObject != null && ((object)rootObject != (object)value)) throw new ProtoException("The root object cannot be reassigned");
+                rootObject = value;
+            }
+            else
+            {
+                MutableList list = List;
+                if (key < list.Count)
+                {
+                    object oldVal = list[key];
+                    if (oldVal == null)
+                    {
+                        list[key] = value;
+                    }
+                    else if (!ReferenceEquals(oldVal, value))
+                    {
+                        throw new ProtoException("Reference-tracked objects cannot change reference");
+                    } // otherwise was the same; nothing to do
+                }
+                else if (key != list.Add(value))
+                {
+                    throw new ProtoException("Internal error; a key mismatch occurred");
+                }
+            }
+        }
+
+        private object rootObject;
+        internal int AddObjectKey(object value, out bool existing)
+        {
+            if (value == null) throw new ArgumentNullException(nameof(value));
+
+            if ((object)value == (object)rootObject) // (object) here is no-op, but should be
+            {                                        // preserved even if this was typed - needs ref-check
+                existing = true;
+                return Root;
+            }
+
+            string s = value as string;
+            BasicList list = List;
+            int index;
+
+            if (s == null)
+            {
+#if CF || PORTABLE // CF has very limited proper object ref-tracking; so instead, we'll search it the hard way
+                index = list.IndexOfReference(value);
+#else
+                if (objectKeys == null)
+                {
+                    objectKeys = new Dictionary<object, int>(ReferenceComparer.Default);
+                    index = -1;
+                }
+                else
+                {
+                    if (!objectKeys.TryGetValue(value, out index)) index = -1;
+                }
+#endif
+            }
+            else
+            {
+                if (stringKeys == null)
+                {
+                    stringKeys = new Dictionary<string, int>();
+                    index = -1;
+                }
+                else
+                {
+                    if (!stringKeys.TryGetValue(s, out index)) index = -1;
+                }
+            }
+
+            if (!(existing = index >= 0))
+            {
+                index = list.Add(value);
+
+                if (s == null)
+                {
+#if !CF && !PORTABLE // CF can't handle the object keys very well
+                    objectKeys.Add(value, index);
+#endif
+                }
+                else
+                {
+                    stringKeys.Add(s, index);
+                }
+            }
+            return index + 1;
+        }
+
+        private int trapStartIndex; // defaults to 0 - optimization for RegisterTrappedObject
+                                    // to make it faster at seeking to find deferred-objects
+
+        internal void RegisterTrappedObject(object value)
+        {
+            if (rootObject == null)
+            {
+                rootObject = value;
+            }
+            else
+            {
+                if (underlyingList != null)
+                {
+                    for (int i = trapStartIndex; i < underlyingList.Count; i++)
+                    {
+                        trapStartIndex = i + 1; // things never *become* null; whether or
+                                                // not the next item is null, it will never
+                                                // need to be checked again
+
+                        if (underlyingList[i] == null)
+                        {
+                            underlyingList[i] = value;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        private Dictionary<string, int> stringKeys;
+
+#if !CF && !PORTABLE // CF lacks the ability to get a robust reference-based hash-code, so we'll do it the harder way instead
+        private System.Collections.Generic.Dictionary<object, int> objectKeys;
+        private sealed class ReferenceComparer : IEqualityComparer<object>
+        {
+            public readonly static ReferenceComparer Default = new ReferenceComparer();
+            private ReferenceComparer() { }
+
+            bool IEqualityComparer<object>.Equals(object x, object y)
+            {
+                return x == y; // ref equality
+            }
+
+            int IEqualityComparer<object>.GetHashCode(object obj)
+            {
+                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
+            }
+        }
+#endif
+
+        internal void Clear()
+        {
+            trapStartIndex = 0;
+            rootObject = null;
+            if (underlyingList != null) underlyingList.Clear();
+            if (stringKeys != null) stringKeys.Clear();
+#if !CF && !PORTABLE
+            if (objectKeys != null) objectKeys.Clear();
+#endif
+        }
+    }
+}

+ 26 - 0
Server/ThirdParty/protobuf-net/PrefixStyle.cs

@@ -0,0 +1,26 @@
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Specifies the type of prefix that should be applied to messages.
+    /// </summary>
+    public enum PrefixStyle
+    {
+        /// <summary>
+        /// No length prefix is applied to the data; the data is terminated only be the end of the stream.
+        /// </summary>
+        None = 0,
+        /// <summary>
+        /// A base-128 ("varint", the default prefix format in protobuf) length prefix is applied to the data (efficient for short messages).
+        /// </summary>
+        Base128 = 1,
+        /// <summary>
+        /// A fixed-length (little-endian) length prefix is applied to the data (useful for compatibility).
+        /// </summary>
+        Fixed32 = 2,
+        /// <summary>
+        /// A fixed-length (big-endian) length prefix is applied to the data (useful for compatibility).
+        /// </summary>
+        Fixed32BigEndian = 3
+    }
+}

+ 175 - 0
Server/ThirdParty/protobuf-net/ProtoContractAttribute.cs

@@ -0,0 +1,175 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates that a type is defined for protocol-buffer serialization.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface,
+        AllowMultiple = false, Inherited = false)]
+    public sealed class ProtoContractAttribute : Attribute
+    {
+        /// <summary>
+        /// Gets or sets the defined name of the type.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets or sets the fist offset to use with implicit field tags;
+        /// only uesd if ImplicitFields is set.
+        /// </summary>
+        public int ImplicitFirstTag
+        {
+            get { return implicitFirstTag; }
+            set
+            {
+                if (value < 1) throw new ArgumentOutOfRangeException("ImplicitFirstTag");
+                implicitFirstTag = value;
+            }
+        }
+        private int implicitFirstTag;
+
+        /// <summary>
+        /// If specified, alternative contract markers (such as markers for XmlSerailizer or DataContractSerializer) are ignored.
+        /// </summary>
+        public bool UseProtoMembersOnly
+        {
+            get { return HasFlag(OPTIONS_UseProtoMembersOnly); }
+            set { SetFlag(OPTIONS_UseProtoMembersOnly, value); }
+        }
+
+        /// <summary>
+        /// If specified, do NOT treat this type as a list, even if it looks like one.
+        /// </summary>
+        public bool IgnoreListHandling
+        {
+            get { return HasFlag(OPTIONS_IgnoreListHandling); }
+            set { SetFlag(OPTIONS_IgnoreListHandling, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets the mechanism used to automatically infer field tags
+        /// for members. This option should be used in advanced scenarios only.
+        /// Please review the important notes against the ImplicitFields enumeration.
+        /// </summary>
+        public ImplicitFields ImplicitFields { get; set; }
+
+        /// <summary>
+        /// Enables/disables automatic tag generation based on the existing name / order
+        /// of the defined members. This option is not used for members marked
+        /// with ProtoMemberAttribute, as intended to provide compatibility with
+        /// WCF serialization. WARNING: when adding new fields you must take
+        /// care to increase the Order for new elements, otherwise data corruption
+        /// may occur.
+        /// </summary>
+        /// <remarks>If not explicitly specified, the default is assumed from Serializer.GlobalOptions.InferTagFromName.</remarks>
+        public bool InferTagFromName
+        {
+            get { return HasFlag(OPTIONS_InferTagFromName); }
+            set
+            {
+                SetFlag(OPTIONS_InferTagFromName, value);
+                SetFlag(OPTIONS_InferTagFromNameHasValue, true);
+            }
+        }
+
+        /// <summary>
+        /// Has a InferTagFromName value been explicitly set? if not, the default from the type-model is assumed.
+        /// </summary>
+        internal bool InferTagFromNameHasValue
+        { // note that this property is accessed via reflection and should not be removed
+            get { return HasFlag(OPTIONS_InferTagFromNameHasValue); }
+        }
+
+        /// <summary>
+        /// Specifies an offset to apply to [DataMember(Order=...)] markers;
+        /// this is useful when working with mex-generated classes that have
+        /// a different origin (usually 1 vs 0) than the original data-contract.
+        /// 
+        /// This value is added to the Order of each member.
+        /// </summary>
+        public int DataMemberOffset { get; set; }
+
+        /// <summary>
+        /// If true, the constructor for the type is bypassed during deserialization, meaning any field initializers
+        /// or other initialization code is skipped.
+        /// </summary>
+        public bool SkipConstructor
+        {
+            get { return HasFlag(OPTIONS_SkipConstructor); }
+            set { SetFlag(OPTIONS_SkipConstructor, value); }
+        }
+
+        /// <summary>
+        /// Should this type be treated as a reference by default? Please also see the implications of this,
+        /// as recorded on ProtoMemberAttribute.AsReference
+        /// </summary>
+        public bool AsReferenceDefault
+        {
+            get { return HasFlag(OPTIONS_AsReferenceDefault); }
+            set
+            {
+                SetFlag(OPTIONS_AsReferenceDefault, value);
+            }
+        }
+
+        /// <summary>
+        /// Indicates whether this type should always be treated as a "group" (rather than a string-prefixed sub-message)
+        /// </summary>
+        public bool IsGroup
+        {
+            get { return HasFlag(OPTIONS_IsGroup); }
+            set
+            {
+                SetFlag(OPTIONS_IsGroup, value);
+            }
+        }
+
+        private bool HasFlag(ushort flag) { return (flags & flag) == flag; }
+        private void SetFlag(ushort flag, bool value)
+        {
+            if (value) flags |= flag;
+            else flags = (ushort)(flags & ~flag);
+        }
+
+        private ushort flags;
+
+        private const ushort
+            OPTIONS_InferTagFromName = 1,
+            OPTIONS_InferTagFromNameHasValue = 2,
+            OPTIONS_UseProtoMembersOnly = 4,
+            OPTIONS_SkipConstructor = 8,
+            OPTIONS_IgnoreListHandling = 16,
+            OPTIONS_AsReferenceDefault = 32,
+            OPTIONS_EnumPassthru = 64,
+            OPTIONS_EnumPassthruHasValue = 128,
+            OPTIONS_IsGroup = 256;
+
+        /// <summary>
+        /// Applies only to enums (not to DTO classes themselves); gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather
+        /// than enforcing .proto enum rules. This is useful *in particul* for [Flags] enums.
+        /// </summary>
+        public bool EnumPassthru
+        {
+            get { return HasFlag(OPTIONS_EnumPassthru); }
+            set
+            {
+                SetFlag(OPTIONS_EnumPassthru, value);
+                SetFlag(OPTIONS_EnumPassthruHasValue, true);
+            }
+        }
+
+        /// <summary>
+        /// Allows to define a surrogate type used for serialization/deserialization purpose.
+        /// </summary>
+        public Type Surrogate { get; set; }
+
+        /// <summary>
+        /// Has a EnumPassthru value been explicitly set?
+        /// </summary>
+        internal bool EnumPassthruHasValue
+        { // note that this property is accessed via reflection and should not be removed
+            get { return HasFlag(OPTIONS_EnumPassthruHasValue); }
+        }
+    }
+}

+ 13 - 0
Server/ThirdParty/protobuf-net/ProtoConverterAttribute.cs

@@ -0,0 +1,13 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates that a static member should be considered the same as though
+    /// were an implicit / explicit conversion operator; in particular, this
+    /// is useful for conversions that operator syntax does not allow, such as
+    /// to/from interface types.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+    public class ProtoConverterAttribute : Attribute { }
+}

+ 36 - 0
Server/ThirdParty/protobuf-net/ProtoEnumAttribute.cs

@@ -0,0 +1,36 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Used to define protocol-buffer specific behavior for
+    /// enumerated values.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
+    public sealed class ProtoEnumAttribute : Attribute
+    {
+        /// <summary>
+        /// Gets or sets the specific value to use for this enum during serialization.
+        /// </summary>
+        public int Value
+        {
+            get { return enumValue; }
+            set { this.enumValue = value; hasValue = true; }
+        }
+
+        /// <summary>
+        /// Indicates whether this instance has a customised value mapping
+        /// </summary>
+        /// <returns>true if a specific value is set</returns>
+        public bool HasValue() => hasValue;
+
+        private bool hasValue;
+        private int enumValue;
+
+        /// <summary>
+        /// Gets or sets the defined name of the enum, as used in .proto
+        /// (this name is not used during serialization).
+        /// </summary>
+        public string Name { get; set; }
+    }
+}

+ 30 - 0
Server/ThirdParty/protobuf-net/ProtoException.cs

@@ -0,0 +1,30 @@
+using System;
+
+#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
+using System.Runtime.Serialization;
+#endif
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates an error during serialization/deserialization of a proto stream.
+    /// </summary>
+#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
+    [Serializable]
+#endif
+    public class ProtoException : Exception
+    {
+        /// <summary>Creates a new ProtoException instance.</summary>
+        public ProtoException() { }
+
+        /// <summary>Creates a new ProtoException instance.</summary>
+        public ProtoException(string message) : base(message) { }
+
+        /// <summary>Creates a new ProtoException instance.</summary>
+        public ProtoException(string message, Exception innerException) : base(message, innerException) { }
+
+#if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259)
+        /// <summary>Creates a new ProtoException instance.</summary>
+        protected ProtoException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+#endif
+    }
+}

+ 40 - 0
Server/ThirdParty/protobuf-net/ProtoIgnoreAttribute.cs

@@ -0,0 +1,40 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates that a member should be excluded from serialization; this
+    /// is only normally used when using implict fields.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,
+        AllowMultiple = false, Inherited = true)]
+    public class ProtoIgnoreAttribute : Attribute { }
+
+    /// <summary>
+    /// Indicates that a member should be excluded from serialization; this
+    /// is only normally used when using implict fields. This allows
+    /// ProtoIgnoreAttribute usage
+    /// even for partial classes where the individual members are not
+    /// under direct control.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class,
+            AllowMultiple = true, Inherited = false)]
+    public sealed class ProtoPartialIgnoreAttribute : ProtoIgnoreAttribute
+    {
+        /// <summary>
+        /// Creates a new ProtoPartialIgnoreAttribute instance.
+        /// </summary>
+        /// <param name="memberName">Specifies the member to be ignored.</param>
+        public ProtoPartialIgnoreAttribute(string memberName)
+            : base()
+        {
+            if (string.IsNullOrEmpty(memberName)) throw new ArgumentNullException(nameof(memberName));
+
+            MemberName = memberName;
+        }
+        /// <summary>
+        /// The name of the member to be ignored.
+        /// </summary>
+        public string MemberName { get; }
+    }
+}

+ 60 - 0
Server/ThirdParty/protobuf-net/ProtoIncludeAttribute.cs

@@ -0,0 +1,60 @@
+using System;
+using System.ComponentModel;
+
+using ProtoBuf.Meta;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Indicates the known-types to support for an individual
+    /// message. This serializes each level in the hierarchy as
+    /// a nested message to retain wire-compatibility with
+    /// other protocol-buffer implementations.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
+    public sealed class ProtoIncludeAttribute : Attribute
+    {
+        ///<summary>
+        /// Creates a new instance of the ProtoIncludeAttribute.
+        /// </summary>
+        /// <param name="tag">The unique index (within the type) that will identify this data.</param>
+        /// <param name="knownType">The additional type to serialize/deserialize.</param>
+        public ProtoIncludeAttribute(int tag, Type knownType)
+            : this(tag, knownType == null ? "" : knownType.AssemblyQualifiedName) { }
+
+        /// <summary>
+        /// Creates a new instance of the ProtoIncludeAttribute.
+        /// </summary>
+        /// <param name="tag">The unique index (within the type) that will identify this data.</param>
+        /// <param name="knownTypeName">The additional type to serialize/deserialize.</param>
+        public ProtoIncludeAttribute(int tag, string knownTypeName)
+        {
+            if (tag <= 0) throw new ArgumentOutOfRangeException(nameof(tag), "Tags must be positive integers");
+            if (string.IsNullOrEmpty(knownTypeName)) throw new ArgumentNullException(nameof(knownTypeName), "Known type cannot be blank");
+            Tag = tag;
+            KnownTypeName = knownTypeName;
+        }
+
+        /// <summary>
+        /// Gets the unique index (within the type) that will identify this data.
+        /// </summary>
+        public int Tag { get; }
+
+        /// <summary>
+        /// Gets the additional type to serialize/deserialize.
+        /// </summary>
+        public string KnownTypeName { get; }
+
+        /// <summary>
+        /// Gets the additional type to serialize/deserialize.
+        /// </summary>
+        public Type KnownType => TypeModel.ResolveKnownType(KnownTypeName, null, null);
+
+        /// <summary>
+        /// Specifies whether the inherited sype's sub-message should be
+        /// written with a length-prefix (default), or with group markers.
+        /// </summary>
+        [DefaultValue(DataFormat.Default)]
+        public DataFormat DataFormat { get; set; } = DataFormat.Default;
+    }
+}

+ 29 - 0
Server/ThirdParty/protobuf-net/ProtoMapAttribute.cs

@@ -0,0 +1,29 @@
+using System;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Controls the formatting of elements in a dictionary, and indicates that
+    /// "map" rules should be used: duplicates *replace* earlier values, rather
+    /// than throwing an exception
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+    public class ProtoMapAttribute : Attribute
+    {
+        /// <summary>
+        /// Describes the data-format used to store the key
+        /// </summary>
+        public DataFormat KeyFormat { get; set; }
+        /// <summary>
+        /// Describes the data-format used to store the value
+        /// </summary>
+        public DataFormat ValueFormat { get; set; }
+
+        /// <summary>
+        /// Disables "map" handling; dictionaries will use ".Add(key,value)" instead of  "[key] = value",
+        /// which means duplicate keys will cause an exception (instead of retaining the final value); if
+        /// a proto schema is emitted, it will be produced using "repeated" instead of "map"
+        /// </summary>
+        public bool DisableMap { get; set; }
+    }
+}

+ 228 - 0
Server/ThirdParty/protobuf-net/ProtoMemberAttribute.cs

@@ -0,0 +1,228 @@
+using System;
+using System.Reflection;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// Declares a member to be used in protocol-buffer serialization, using
+    /// the given Tag. A DataFormat may be used to optimise the serialization
+    /// format (for instance, using zigzag encoding for negative numbers, or 
+    /// fixed-length encoding for large values.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,
+        AllowMultiple = false, Inherited = true)]
+    public class ProtoMemberAttribute : Attribute
+        , IComparable
+        , IComparable<ProtoMemberAttribute>
+
+    {
+        /// <summary>
+        /// Compare with another ProtoMemberAttribute for sorting purposes
+        /// </summary>
+        public int CompareTo(object other) => CompareTo(other as ProtoMemberAttribute);
+        /// <summary>
+        /// Compare with another ProtoMemberAttribute for sorting purposes
+        /// </summary>
+        public int CompareTo(ProtoMemberAttribute other)
+        {
+            if (other == null) return -1;
+            if ((object)this == (object)other) return 0;
+            int result = this.tag.CompareTo(other.tag);
+            if (result == 0) result = string.CompareOrdinal(this.name, other.name);
+            return result;
+        }
+
+        /// <summary>
+        /// Creates a new ProtoMemberAttribute instance.
+        /// </summary>
+        /// <param name="tag">Specifies the unique tag used to identify this member within the type.</param>
+        public ProtoMemberAttribute(int tag) : this(tag, false)
+        { }
+
+        internal ProtoMemberAttribute(int tag, bool forced)
+        {
+            if (tag <= 0 && !forced) throw new ArgumentOutOfRangeException(nameof(tag));
+            this.tag = tag;
+        }
+
+#if !NO_RUNTIME
+        internal MemberInfo Member, BackingMember;
+        internal bool TagIsPinned;
+#endif
+        /// <summary>
+        /// Gets or sets the original name defined in the .proto; not used
+        /// during serialization.
+        /// </summary>
+        public string Name { get { return name; } set { name = value; } }
+        private string name;
+
+        /// <summary>
+        /// Gets or sets the data-format to be used when encoding this value.
+        /// </summary>
+        public DataFormat DataFormat { get { return dataFormat; } set { dataFormat = value; } }
+        private DataFormat dataFormat;
+
+        /// <summary>
+        /// Gets the unique tag used to identify this member within the type.
+        /// </summary>
+        public int Tag { get { return tag; } }
+        private int tag;
+        internal void Rebase(int tag) { this.tag = tag; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this member is mandatory.
+        /// </summary>
+        public bool IsRequired
+        {
+            get { return (options & MemberSerializationOptions.Required) == MemberSerializationOptions.Required; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.Required;
+                else options &= ~MemberSerializationOptions.Required;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this member is packed.
+        /// This option only applies to list/array data of primitive types (int, double, etc).
+        /// </summary>
+        public bool IsPacked
+        {
+            get { return (options & MemberSerializationOptions.Packed) == MemberSerializationOptions.Packed; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.Packed;
+                else options &= ~MemberSerializationOptions.Packed;
+            }
+        }
+
+        /// <summary>
+        /// Indicates whether this field should *repace* existing values (the default is false, meaning *append*).
+        /// This option only applies to list/array data.
+        /// </summary>
+        public bool OverwriteList
+        {
+            get { return (options & MemberSerializationOptions.OverwriteList) == MemberSerializationOptions.OverwriteList; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.OverwriteList;
+                else options &= ~MemberSerializationOptions.OverwriteList;
+            }
+        }
+
+        /// <summary>
+        /// Enables full object-tracking/full-graph support.
+        /// </summary>
+        public bool AsReference
+        {
+            get { return (options & MemberSerializationOptions.AsReference) == MemberSerializationOptions.AsReference; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.AsReference;
+                else options &= ~MemberSerializationOptions.AsReference;
+
+                options |= MemberSerializationOptions.AsReferenceHasValue;
+            }
+        }
+
+        internal bool AsReferenceHasValue
+        {
+            get { return (options & MemberSerializationOptions.AsReferenceHasValue) == MemberSerializationOptions.AsReferenceHasValue; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.AsReferenceHasValue;
+                else options &= ~MemberSerializationOptions.AsReferenceHasValue;
+            }
+        }
+
+        /// <summary>
+        /// Embeds the type information into the stream, allowing usage with types not known in advance.
+        /// </summary>
+        public bool DynamicType
+        {
+            get { return (options & MemberSerializationOptions.DynamicType) == MemberSerializationOptions.DynamicType; }
+            set
+            {
+                if (value) options |= MemberSerializationOptions.DynamicType;
+                else options &= ~MemberSerializationOptions.DynamicType;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this member is packed (lists/arrays).
+        /// </summary>
+        public MemberSerializationOptions Options { get { return options; } set { options = value; } }
+        private MemberSerializationOptions options;
+
+
+    }
+
+    /// <summary>
+    /// Additional (optional) settings that control serialization of members
+    /// </summary>
+    [Flags]
+    public enum MemberSerializationOptions
+    {
+        /// <summary>
+        /// Default; no additional options
+        /// </summary>
+        None = 0,
+        /// <summary>
+        /// Indicates that repeated elements should use packed (length-prefixed) encoding
+        /// </summary>
+        Packed = 1,
+        /// <summary>
+        /// Indicates that the given item is required
+        /// </summary>
+        Required = 2,
+        /// <summary>
+        /// Enables full object-tracking/full-graph support
+        /// </summary>
+        AsReference = 4,
+        /// <summary>
+        /// Embeds the type information into the stream, allowing usage with types not known in advance
+        /// </summary>
+        DynamicType = 8,
+        /// <summary>
+        /// Indicates whether this field should *repace* existing values (the default is false, meaning *append*).
+        /// This option only applies to list/array data.
+        /// </summary>
+        OverwriteList = 16,
+        /// <summary>
+        /// Determines whether the types AsReferenceDefault value is used, or whether this member's AsReference should be used
+        /// </summary>
+        AsReferenceHasValue = 32
+    }
+
+    /// <summary>
+    /// Declares a member to be used in protocol-buffer serialization, using
+    /// the given Tag and MemberName. This allows ProtoMemberAttribute usage
+    /// even for partial classes where the individual members are not
+    /// under direct control.
+    /// A DataFormat may be used to optimise the serialization
+    /// format (for instance, using zigzag encoding for negative numbers, or 
+    /// fixed-length encoding for large values.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class,
+            AllowMultiple = true, Inherited = false)]
+    public sealed class ProtoPartialMemberAttribute : ProtoMemberAttribute
+    {
+        /// <summary>
+        /// Creates a new ProtoMemberAttribute instance.
+        /// </summary>
+        /// <param name="tag">Specifies the unique tag used to identify this member within the type.</param>
+        /// <param name="memberName">Specifies the member to be serialized.</param>
+        public ProtoPartialMemberAttribute(int tag, string memberName)
+            : base(tag)
+        {
+#if !NO_RUNTIME
+            if (string.IsNullOrEmpty(memberName)) throw new ArgumentNullException(nameof(memberName));
+#endif
+            this.MemberName = memberName;
+        }
+        /// <summary>
+        /// The name of the member to be serialized.
+        /// </summary>
+        public string MemberName { get; private set; }
+    }
+}

+ 1436 - 0
Server/ThirdParty/protobuf-net/ProtoReader.cs

@@ -0,0 +1,1436 @@
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using ProtoBuf.Meta;
+
+namespace ProtoBuf
+{
+    /// <summary>
+    /// A stateful reader, used to read a protobuf stream. Typical usage would be (sequentially) to call
+    /// ReadFieldHeader and (after matching the field) an appropriate Read* method.
+    /// </summary>
+    public sealed class ProtoReader : IDisposable
+    {
+        Stream source;
+        byte[] ioBuffer;
+        TypeModel model;
+        int fieldNumber, depth, ioIndex, available;
+        long position64, blockEnd64, dataRemaining64;
+        WireType wireType;
+        bool isFixedLength, internStrings;
+        private NetObjectCache netCache;
+
+        // this is how many outstanding objects do not currently have
+        // values for the purposes of reference tracking; we'll default
+        // to just trapping the root object
+        // note: objects are trapped (the ref and key mapped) via NoteObject
+        uint trapCount; // uint is so we can use beq/bne more efficiently than bgt
+
+        /// <summary>
+        /// Gets the number of the field being processed.
+        /// </summary>
+        public int FieldNumber => fieldNumber;
+
+        /// <summary>
+        /// Indicates the underlying proto serialization format on the wire.
+        /// </summary>
+        public WireType WireType => wireType;
+
+        /// <summary>
+        /// Creates a new reader against a stream
+        /// </summary>
+        /// <param name="source">The source stream</param>
+        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
+        /// <param name="context">Additional context about this serialization operation</param>
+        [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
+        public ProtoReader(Stream source, TypeModel model, SerializationContext context)
+        {
+
+            Init(this, source, model, context, TO_EOF);
+        }
+
+        internal const long TO_EOF = -1;
+
+        /// <summary>
+        /// Gets / sets a flag indicating whether strings should be checked for repetition; if
+        /// true, any repeated UTF-8 byte sequence will result in the same String instance, rather
+        /// than a second instance of the same string. Enabled by default. Note that this uses
+        /// a <i>custom</i> interner - the system-wide string interner is not used.
+        /// </summary>
+        public bool InternStrings { get { return internStrings; } set { internStrings = value; } }
+
+        /// <summary>
+        /// Creates a new reader against a stream
+        /// </summary>
+        /// <param name="source">The source stream</param>
+        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
+        /// <param name="context">Additional context about this serialization operation</param>
+        /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
+        [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
+        public ProtoReader(Stream source, TypeModel model, SerializationContext context, int length)
+        {
+            Init(this, source, model, context, length);
+        }
+
+        /// <summary>
+        /// Creates a new reader against a stream
+        /// </summary>
+        /// <param name="source">The source stream</param>
+        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
+        /// <param name="context">Additional context about this serialization operation</param>
+        /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
+        [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
+        public ProtoReader(Stream source, TypeModel model, SerializationContext context, long length)
+        {
+            Init(this, source, model, context, length);
+        }
+
+        private static void Init(ProtoReader reader, Stream source, TypeModel model, SerializationContext context, long length)
+        {
+            if (source == null) throw new ArgumentNullException(nameof(source));
+            if (!source.CanRead) throw new ArgumentException("Cannot read from stream", nameof(source));
+            reader.source = source;
+            reader.ioBuffer = BufferPool.GetBuffer();
+            reader.model = model;
+            bool isFixedLength = length >= 0;
+            reader.isFixedLength = isFixedLength;
+            reader.dataRemaining64 = isFixedLength ? length : 0;
+
+            if (context == null) { context = SerializationContext.Default; }
+            else { context.Freeze(); }
+            reader.context = context;
+            reader.position64 = 0;
+            reader.available = reader.depth = reader.fieldNumber = reader.ioIndex = 0;
+            reader.blockEnd64 = long.MaxValue;
+            reader.internStrings = RuntimeTypeModel.Default.InternStrings;
+            reader.wireType = WireType.None;
+            reader.trapCount = 1;
+            if (reader.netCache == null) reader.netCache = new NetObjectCache();
+        }
+
+        private SerializationContext context;
+
+        /// <summary>
+        /// Addition information about this deserialization operation.
+        /// </summary>
+        public SerializationContext Context => context;
+
+        /// <summary>
+        /// Releases resources used by the reader, but importantly <b>does not</b> Dispose the 
+        /// underlying stream; in many typical use-cases the stream is used for different
+        /// processes, so it is assumed that the consumer will Dispose their stream separately.
+        /// </summary>
+        public void Dispose()
+        {
+            // importantly, this does **not** own the stream, and does not dispose it
+            source = null;
+            model = null;
+            BufferPool.ReleaseBufferToPool(ref ioBuffer);
+            if (stringInterner != null)
+            {
+                stringInterner.Clear();
+                stringInterner = null;
+            }
+            if (netCache != null) netCache.Clear();
+        }
+        internal int TryReadUInt32VariantWithoutMoving(bool trimNegative, out uint value)
+        {
+            if (available < 10) Ensure(10, false);
+            if (available == 0)
+            {
+                value = 0;
+                return 0;
+            }
+            int readPos = ioIndex;
+            value = ioBuffer[readPos++];
+            if ((value & 0x80) == 0) return 1;
+            value &= 0x7F;
+            if (available == 1) throw EoF(this);
+
+            uint chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 7;
+            if ((chunk & 0x80) == 0) return 2;
+            if (available == 2) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 14;
+            if ((chunk & 0x80) == 0) return 3;
+            if (available == 3) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 21;
+            if ((chunk & 0x80) == 0) return 4;
+            if (available == 4) throw EoF(this);
+
+            chunk = ioBuffer[readPos];
+            value |= chunk << 28; // can only use 4 bits from this chunk
+            if ((chunk & 0xF0) == 0) return 5;
+
+            if (trimNegative // allow for -ve values
+                && (chunk & 0xF0) == 0xF0
+                && available >= 10
+                    && ioBuffer[++readPos] == 0xFF
+                    && ioBuffer[++readPos] == 0xFF
+                    && ioBuffer[++readPos] == 0xFF
+                    && ioBuffer[++readPos] == 0xFF
+                    && ioBuffer[++readPos] == 0x01)
+            {
+                return 10;
+            }
+            throw AddErrorData(new OverflowException(), this);
+        }
+
+        private uint ReadUInt32Variant(bool trimNegative)
+        {
+            int read = TryReadUInt32VariantWithoutMoving(trimNegative, out uint value);
+            if (read > 0)
+            {
+                ioIndex += read;
+                available -= read;
+                position64 += read;
+                return value;
+            }
+            throw EoF(this);
+        }
+
+        private bool TryReadUInt32Variant(out uint value)
+        {
+            int read = TryReadUInt32VariantWithoutMoving(false, out value);
+            if (read > 0)
+            {
+                ioIndex += read;
+                available -= read;
+                position64 += read;
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Reads an unsigned 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
+        /// </summary>
+        public uint ReadUInt32()
+        {
+            switch (wireType)
+            {
+                case WireType.Variant:
+                    return ReadUInt32Variant(false);
+                case WireType.Fixed32:
+                    if (available < 4) Ensure(4, true);
+                    position64 += 4;
+                    available -= 4;
+                    return ((uint)ioBuffer[ioIndex++])
+                        | (((uint)ioBuffer[ioIndex++]) << 8)
+                        | (((uint)ioBuffer[ioIndex++]) << 16)
+                        | (((uint)ioBuffer[ioIndex++]) << 24);
+                case WireType.Fixed64:
+                    ulong val = ReadUInt64();
+                    checked { return (uint)val; }
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Returns the position of the current reader (note that this is not necessarily the same as the position
+        /// in the underlying stream, if multiple readers are used on the same stream)
+        /// </summary>
+        public int Position { get { return checked((int)position64); } }
+
+        /// <summary>
+        /// Returns the position of the current reader (note that this is not necessarily the same as the position
+        /// in the underlying stream, if multiple readers are used on the same stream)
+        /// </summary>
+        public long LongPosition { get { return position64; } }
+        internal void Ensure(int count, bool strict)
+        {
+            Helpers.DebugAssert(available <= count, "Asking for data without checking first");
+            if (count > ioBuffer.Length)
+            {
+                BufferPool.ResizeAndFlushLeft(ref ioBuffer, count, ioIndex, available);
+                ioIndex = 0;
+            }
+            else if (ioIndex + count >= ioBuffer.Length)
+            {
+                // need to shift the buffer data to the left to make space
+                Buffer.BlockCopy(ioBuffer, ioIndex, ioBuffer, 0, available);
+                ioIndex = 0;
+            }
+            count -= available;
+            int writePos = ioIndex + available, bytesRead;
+            int canRead = ioBuffer.Length - writePos;
+            if (isFixedLength)
+            {   // throttle it if needed
+                if (dataRemaining64 < canRead) canRead = (int)dataRemaining64;
+            }
+            while (count > 0 && canRead > 0 && (bytesRead = source.Read(ioBuffer, writePos, canRead)) > 0)
+            {
+                available += bytesRead;
+                count -= bytesRead;
+                canRead -= bytesRead;
+                writePos += bytesRead;
+                if (isFixedLength) { dataRemaining64 -= bytesRead; }
+            }
+            if (strict && count > 0)
+            {
+                throw EoF(this);
+            }
+
+        }
+        /// <summary>
+        /// Reads a signed 16-bit integer from the stream: Variant, Fixed32, Fixed64, SignedVariant
+        /// </summary>
+        public short ReadInt16()
+        {
+            checked { return (short)ReadInt32(); }
+        }
+        /// <summary>
+        /// Reads an unsigned 16-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
+        /// </summary>
+        public ushort ReadUInt16()
+        {
+            checked { return (ushort)ReadUInt32(); }
+        }
+
+        /// <summary>
+        /// Reads an unsigned 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
+        /// </summary>
+        public byte ReadByte()
+        {
+            checked { return (byte)ReadUInt32(); }
+        }
+
+        /// <summary>
+        /// Reads a signed 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
+        /// </summary>
+        public sbyte ReadSByte()
+        {
+            checked { return (sbyte)ReadInt32(); }
+        }
+
+        /// <summary>
+        /// Reads a signed 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
+        /// </summary>
+        public int ReadInt32()
+        {
+            switch (wireType)
+            {
+                case WireType.Variant:
+                    return (int)ReadUInt32Variant(true);
+                case WireType.Fixed32:
+                    if (available < 4) Ensure(4, true);
+                    position64 += 4;
+                    available -= 4;
+                    return ((int)ioBuffer[ioIndex++])
+                        | (((int)ioBuffer[ioIndex++]) << 8)
+                        | (((int)ioBuffer[ioIndex++]) << 16)
+                        | (((int)ioBuffer[ioIndex++]) << 24);
+                case WireType.Fixed64:
+                    long l = ReadInt64();
+                    checked { return (int)l; }
+                case WireType.SignedVariant:
+                    return Zag(ReadUInt32Variant(true));
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+        private const long Int64Msb = ((long)1) << 63;
+        private const int Int32Msb = ((int)1) << 31;
+        private static int Zag(uint ziggedValue)
+        {
+            int value = (int)ziggedValue;
+            return (-(value & 0x01)) ^ ((value >> 1) & ~ProtoReader.Int32Msb);
+        }
+
+        private static long Zag(ulong ziggedValue)
+        {
+            long value = (long)ziggedValue;
+            return (-(value & 0x01L)) ^ ((value >> 1) & ~ProtoReader.Int64Msb);
+        }
+        /// <summary>
+        /// Reads a signed 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
+        /// </summary>
+        public long ReadInt64()
+        {
+            switch (wireType)
+            {
+                case WireType.Variant:
+                    return (long)ReadUInt64Variant();
+                case WireType.Fixed32:
+                    return ReadInt32();
+                case WireType.Fixed64:
+                    if (available < 8) Ensure(8, true);
+                    position64 += 8;
+                    available -= 8;
+
+#if NETCOREAPP2_1
+                    var result = System.Buffers.Binary.BinaryPrimitives.ReadInt64LittleEndian(ioBuffer.AsSpan(ioIndex, 8));
+
+                    ioIndex+= 8;
+
+                    return result;
+#else
+                    return ((long)ioBuffer[ioIndex++])
+                        | (((long)ioBuffer[ioIndex++]) << 8)
+                        | (((long)ioBuffer[ioIndex++]) << 16)
+                        | (((long)ioBuffer[ioIndex++]) << 24)
+                        | (((long)ioBuffer[ioIndex++]) << 32)
+                        | (((long)ioBuffer[ioIndex++]) << 40)
+                        | (((long)ioBuffer[ioIndex++]) << 48)
+                        | (((long)ioBuffer[ioIndex++]) << 56);
+#endif
+                case WireType.SignedVariant:
+                    return Zag(ReadUInt64Variant());
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+
+        private int TryReadUInt64VariantWithoutMoving(out ulong value)
+        {
+            if (available < 10) Ensure(10, false);
+            if (available == 0)
+            {
+                value = 0;
+                return 0;
+            }
+            int readPos = ioIndex;
+            value = ioBuffer[readPos++];
+            if ((value & 0x80) == 0) return 1;
+            value &= 0x7F;
+            if (available == 1) throw EoF(this);
+
+            ulong chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 7;
+            if ((chunk & 0x80) == 0) return 2;
+            if (available == 2) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 14;
+            if ((chunk & 0x80) == 0) return 3;
+            if (available == 3) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 21;
+            if ((chunk & 0x80) == 0) return 4;
+            if (available == 4) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 28;
+            if ((chunk & 0x80) == 0) return 5;
+            if (available == 5) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 35;
+            if ((chunk & 0x80) == 0) return 6;
+            if (available == 6) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 42;
+            if ((chunk & 0x80) == 0) return 7;
+            if (available == 7) throw EoF(this);
+
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 49;
+            if ((chunk & 0x80) == 0) return 8;
+            if (available == 8) throw EoF(this);
+
+            chunk = ioBuffer[readPos++];
+            value |= (chunk & 0x7F) << 56;
+            if ((chunk & 0x80) == 0) return 9;
+            if (available == 9) throw EoF(this);
+
+            chunk = ioBuffer[readPos];
+            value |= chunk << 63; // can only use 1 bit from this chunk
+
+            if ((chunk & ~(ulong)0x01) != 0) throw AddErrorData(new OverflowException(), this);
+            return 10;
+        }
+
+        private ulong ReadUInt64Variant()
+        {
+            int read = TryReadUInt64VariantWithoutMoving(out ulong value);
+            if (read > 0)
+            {
+                ioIndex += read;
+                available -= read;
+                position64 += read;
+                return value;
+            }
+            throw EoF(this);
+        }
+
+        private Dictionary<string, string> stringInterner;
+        private string Intern(string value)
+        {
+            if (value == null) return null;
+            if (value.Length == 0) return "";
+            if (stringInterner == null)
+            {
+                stringInterner = new Dictionary<string, string>
+                {
+                    { value, value }
+                };
+            }
+            else if (stringInterner.TryGetValue(value, out string found))
+            {
+                value = found;
+            }
+            else
+            {
+                stringInterner.Add(value, value);
+            }
+            return value;
+        }
+
+#if COREFX
+        static readonly Encoding encoding = Encoding.UTF8;
+#else
+        static readonly UTF8Encoding encoding = new UTF8Encoding();
+#endif
+        /// <summary>
+        /// Reads a string from the stream (using UTF8); supported wire-types: String
+        /// </summary>
+        public string ReadString()
+        {
+            if (wireType == WireType.String)
+            {
+                int bytes = (int)ReadUInt32Variant(false);
+                if (bytes == 0) return "";
+                if (available < bytes) Ensure(bytes, true);
+
+                string s = encoding.GetString(ioBuffer, ioIndex, bytes);
+
+                if (internStrings) { s = Intern(s); }
+                available -= bytes;
+                position64 += bytes;
+                ioIndex += bytes;
+                return s;
+            }
+            throw CreateWireTypeException();
+        }
+        /// <summary>
+        /// Throws an exception indication that the given value cannot be mapped to an enum.
+        /// </summary>
+        public void ThrowEnumException(Type type, int value)
+        {
+            string desc = type == null ? "<null>" : type.FullName;
+            throw AddErrorData(new ProtoException("No " + desc + " enum is mapped to the wire-value " + value.ToString()), this);
+        }
+
+        private Exception CreateWireTypeException()
+        {
+            return CreateException("Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354");
+        }
+
+        private Exception CreateException(string message)
+        {
+            return AddErrorData(new ProtoException(message), this);
+        }
+        /// <summary>
+        /// Reads a double-precision number from the stream; supported wire-types: Fixed32, Fixed64
+        /// </summary>
+        public
+#if !FEAT_SAFE
+ unsafe
+#endif
+ double ReadDouble()
+        {
+            switch (wireType)
+            {
+                case WireType.Fixed32:
+                    return ReadSingle();
+                case WireType.Fixed64:
+                    long value = ReadInt64();
+#if FEAT_SAFE
+                    return BitConverter.ToDouble(BitConverter.GetBytes(value), 0);
+#else
+                    return *(double*)&value;
+#endif
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Reads (merges) a sub-message from the stream, internally calling StartSubItem and EndSubItem, and (in between)
+        /// parsing the message in accordance with the model associated with the reader
+        /// </summary>
+        public static object ReadObject(object value, int key, ProtoReader reader)
+        {
+            return ReadTypedObject(value, key, reader, null);
+        }
+
+        internal static object ReadTypedObject(object value, int key, ProtoReader reader, Type type)
+        {
+            if (reader.model == null)
+            {
+                throw AddErrorData(new InvalidOperationException("Cannot deserialize sub-objects unless a model is provided"), reader);
+            }
+            SubItemToken token = ProtoReader.StartSubItem(reader);
+            if (key >= 0)
+            {
+                value = reader.model.Deserialize(key, value, reader);
+            }
+            else if (type != null && reader.model.TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false, null))
+            {
+                // ok
+            }
+            else
+            {
+                TypeModel.ThrowUnexpectedType(type);
+            }
+            ProtoReader.EndSubItem(token, reader);
+            return value;
+        }
+
+        /// <summary>
+        /// Makes the end of consuming a nested message in the stream; the stream must be either at the correct EndGroup
+        /// marker, or all fields of the sub-message must have been consumed (in either case, this means ReadFieldHeader
+        /// should return zero)
+        /// </summary>
+        public static void EndSubItem(SubItemToken token, ProtoReader reader)
+        {
+            if (reader == null) throw new ArgumentNullException("reader");
+            long value64 = token.value64;
+            switch (reader.wireType)
+            {
+                case WireType.EndGroup:
+                    if (value64 >= 0) throw AddErrorData(new ArgumentException("token"), reader);
+                    if (-(int)value64 != reader.fieldNumber) throw reader.CreateException("Wrong group was ended"); // wrong group ended!
+                    reader.wireType = WireType.None; // this releases ReadFieldHeader
+                    reader.depth--;
+                    break;
+                // case WireType.None: // TODO reinstate once reads reset the wire-type
+                default:
+                    if (value64 < reader.position64) throw reader.CreateException($"Sub-message not read entirely; expected {value64}, was {reader.position64}");
+                    if (reader.blockEnd64 != reader.position64 && reader.blockEnd64 != long.MaxValue)
+                    {
+                        throw reader.CreateException("Sub-message not read correctly");
+                    }
+                    reader.blockEnd64 = value64;
+                    reader.depth--;
+                    break;
+                    /*default:
+                        throw reader.BorkedIt(); */
+            }
+        }
+
+        /// <summary>
+        /// Begins consuming a nested message in the stream; supported wire-types: StartGroup, String
+        /// </summary>
+        /// <remarks>The token returned must be help and used when callining EndSubItem</remarks>
+        public static SubItemToken StartSubItem(ProtoReader reader)
+        {
+            if (reader == null) throw new ArgumentNullException("reader");
+            switch (reader.wireType)
+            {
+                case WireType.StartGroup:
+                    reader.wireType = WireType.None; // to prevent glitches from double-calling
+                    reader.depth++;
+                    return new SubItemToken((long)(-reader.fieldNumber));
+                case WireType.String:
+                    long len = (long)reader.ReadUInt64Variant();
+                    if (len < 0) throw AddErrorData(new InvalidOperationException(), reader);
+                    long lastEnd = reader.blockEnd64;
+                    reader.blockEnd64 = reader.position64 + len;
+                    reader.depth++;
+                    return new SubItemToken(lastEnd);
+                default:
+                    throw reader.CreateWireTypeException(); // throws
+            }
+        }
+
+        /// <summary>
+        /// Reads a field header from the stream, setting the wire-type and retuning the field number. If no
+        /// more fields are available, then 0 is returned. This methods respects sub-messages.
+        /// </summary>
+        public int ReadFieldHeader()
+        {
+            // at the end of a group the caller must call EndSubItem to release the
+            // reader (which moves the status to Error, since ReadFieldHeader must
+            // then be called)
+            if (blockEnd64 <= position64 || wireType == WireType.EndGroup) { return 0; }
+
+            if (TryReadUInt32Variant(out uint tag) && tag != 0)
+            {
+                wireType = (WireType)(tag & 7);
+                fieldNumber = (int)(tag >> 3);
+                if (fieldNumber < 1) throw new ProtoException("Invalid field in source data: " + fieldNumber.ToString());
+            }
+            else
+            {
+                wireType = WireType.None;
+                fieldNumber = 0;
+            }
+            if (wireType == ProtoBuf.WireType.EndGroup)
+            {
+                if (depth > 0) return 0; // spoof an end, but note we still set the field-number
+                throw new ProtoException("Unexpected end-group in source data; this usually means the source data is corrupt");
+            }
+            return fieldNumber;
+        }
+        /// <summary>
+        /// Looks ahead to see whether the next field in the stream is what we expect
+        /// (typically; what we've just finished reading - for example ot read successive list items)
+        /// </summary>
+        public bool TryReadFieldHeader(int field)
+        {
+            // check for virtual end of stream
+            if (blockEnd64 <= position64 || wireType == WireType.EndGroup) { return false; }
+
+            int read = TryReadUInt32VariantWithoutMoving(false, out uint tag);
+            WireType tmpWireType; // need to catch this to exclude (early) any "end group" tokens
+            if (read > 0 && ((int)tag >> 3) == field
+                && (tmpWireType = (WireType)(tag & 7)) != WireType.EndGroup)
+            {
+                wireType = tmpWireType;
+                fieldNumber = field;
+                position64 += read;
+                ioIndex += read;
+                available -= read;
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Get the TypeModel associated with this reader
+        /// </summary>
+        public TypeModel Model { get { return model; } }
+
+        /// <summary>
+        /// Compares the streams current wire-type to the hinted wire-type, updating the reader if necessary; for example,
+        /// a Variant may be updated to SignedVariant. If the hinted wire-type is unrelated then no change is made.
+        /// </summary>
+        public void Hint(WireType wireType)
+        {
+            if (this.wireType == wireType) { }  // fine; everything as we expect
+            else if (((int)wireType & 7) == (int)this.wireType)
+            {   // the underling type is a match; we're customising it with an extension
+                this.wireType = wireType;
+            }
+            // note no error here; we're OK about using alternative data
+        }
+
+        /// <summary>
+        /// Verifies that the stream's current wire-type is as expected, or a specialized sub-type (for example,
+        /// SignedVariant) - in which case the current wire-type is updated. Otherwise an exception is thrown.
+        /// </summary>
+        public void Assert(WireType wireType)
+        {
+            if (this.wireType == wireType) { }  // fine; everything as we expect
+            else if (((int)wireType & 7) == (int)this.wireType)
+            {   // the underling type is a match; we're customising it with an extension
+                this.wireType = wireType;
+            }
+            else
+            {   // nope; that is *not* what we were expecting!
+                throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Discards the data for the current field.
+        /// </summary>
+        public void SkipField()
+        {
+            switch (wireType)
+            {
+                case WireType.Fixed32:
+                    if (available < 4) Ensure(4, true);
+                    available -= 4;
+                    ioIndex += 4;
+                    position64 += 4;
+                    return;
+                case WireType.Fixed64:
+                    if (available < 8) Ensure(8, true);
+                    available -= 8;
+                    ioIndex += 8;
+                    position64 += 8;
+                    return;
+                case WireType.String:
+                    long len = (long)ReadUInt64Variant();
+                    if (len <= available)
+                    { // just jump it!
+                        available -= (int)len;
+                        ioIndex += (int)len;
+                        position64 += len;
+                        return;
+                    }
+                    // everything remaining in the buffer is garbage
+                    position64 += len; // assumes success, but if it fails we're screwed anyway
+                    len -= available; // discount anything we've got to-hand
+                    ioIndex = available = 0; // note that we have no data in the buffer
+                    if (isFixedLength)
+                    {
+                        if (len > dataRemaining64) throw EoF(this);
+                        // else assume we're going to be OK
+                        dataRemaining64 -= len;
+                    }
+                    ProtoReader.Seek(source, len, ioBuffer);
+                    return;
+                case WireType.Variant:
+                case WireType.SignedVariant:
+                    ReadUInt64Variant(); // and drop it
+                    return;
+                case WireType.StartGroup:
+                    int originalFieldNumber = this.fieldNumber;
+                    depth++; // need to satisfy the sanity-checks in ReadFieldHeader
+                    while (ReadFieldHeader() > 0) { SkipField(); }
+                    depth--;
+                    if (wireType == WireType.EndGroup && fieldNumber == originalFieldNumber)
+                    { // we expect to exit in a similar state to how we entered
+                        wireType = ProtoBuf.WireType.None;
+                        return;
+                    }
+                    throw CreateWireTypeException();
+                case WireType.None: // treat as explicit errorr
+                case WireType.EndGroup: // treat as explicit error
+                default: // treat as implicit error
+                    throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Reads an unsigned 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
+        /// </summary>
+        public ulong ReadUInt64()
+        {
+            switch (wireType)
+            {
+                case WireType.Variant:
+                    return ReadUInt64Variant();
+                case WireType.Fixed32:
+                    return ReadUInt32();
+                case WireType.Fixed64:
+                    if (available < 8) Ensure(8, true);
+                    position64 += 8;
+                    available -= 8;
+
+                    return ((ulong)ioBuffer[ioIndex++])
+                        | (((ulong)ioBuffer[ioIndex++]) << 8)
+                        | (((ulong)ioBuffer[ioIndex++]) << 16)
+                        | (((ulong)ioBuffer[ioIndex++]) << 24)
+                        | (((ulong)ioBuffer[ioIndex++]) << 32)
+                        | (((ulong)ioBuffer[ioIndex++]) << 40)
+                        | (((ulong)ioBuffer[ioIndex++]) << 48)
+                        | (((ulong)ioBuffer[ioIndex++]) << 56);
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+        /// <summary>
+        /// Reads a single-precision number from the stream; supported wire-types: Fixed32, Fixed64
+        /// </summary>
+        public
+#if !FEAT_SAFE
+ unsafe
+#endif
+ float ReadSingle()
+        {
+            switch (wireType)
+            {
+                case WireType.Fixed32:
+                    {
+                        int value = ReadInt32();
+#if FEAT_SAFE
+                        return BitConverter.ToSingle(BitConverter.GetBytes(value), 0);
+#else
+                        return *(float*)&value;
+#endif
+                    }
+                case WireType.Fixed64:
+                    {
+                        double value = ReadDouble();
+                        float f = (float)value;
+                        if (float.IsInfinity(f) && !double.IsInfinity(value))
+                        {
+                            throw AddErrorData(new OverflowException(), this);
+                        }
+                        return f;
+                    }
+                default:
+                    throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Reads a boolean value from the stream; supported wire-types: Variant, Fixed32, Fixed64
+        /// </summary>
+        /// <returns></returns>
+        public bool ReadBoolean()
+        {
+            switch (ReadUInt32())
+            {
+                case 0: return false;
+                case 1: return true;
+                default: throw CreateException("Unexpected boolean value");
+            }
+        }
+
+        private static readonly byte[] EmptyBlob = new byte[0];
+        /// <summary>
+        /// Reads a byte-sequence from the stream, appending them to an existing byte-sequence (which can be null); supported wire-types: String
+        /// </summary>
+        public static byte[] AppendBytes(byte[] value, ProtoReader reader)
+        {
+            if (reader == null) throw new ArgumentNullException(nameof(reader));
+            switch (reader.wireType)
+            {
+                case WireType.String:
+                    int len = (int)reader.ReadUInt32Variant(false);
+                    reader.wireType = WireType.None;
+                    if (len == 0) return value ?? EmptyBlob;
+                    int offset;
+                    if (value == null || value.Length == 0)
+                    {
+                        offset = 0;
+                        value = new byte[len];
+                    }
+                    else
+                    {
+                        offset = value.Length;
+                        byte[] tmp = new byte[value.Length + len];
+                        Buffer.BlockCopy(value, 0, tmp, 0, value.Length);
+                        value = tmp;
+                    }
+                    // value is now sized with the final length, and (if necessary)
+                    // contains the old data up to "offset"
+                    reader.position64 += len; // assume success
+                    while (len > reader.available)
+                    {
+                        if (reader.available > 0)
+                        {
+                            // copy what we *do* have
+                            Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, offset, reader.available);
+                            len -= reader.available;
+                            offset += reader.available;
+                            reader.ioIndex = reader.available = 0; // we've drained the buffer
+                        }
+                        //  now refill the buffer (without overflowing it)
+                        int count = len > reader.ioBuffer.Length ? reader.ioBuffer.Length : len;
+                        if (count > 0) reader.Ensure(count, true);
+                    }
+                    // at this point, we know that len <= available
+                    if (len > 0)
+                    {   // still need data, but we have enough buffered
+                        Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, offset, len);
+                        reader.ioIndex += len;
+                        reader.available -= len;
+                    }
+                    return value;
+                case WireType.Variant:
+                    return new byte[0];
+                default:
+                    throw reader.CreateWireTypeException();
+            }
+        }
+
+        //static byte[] ReadBytes(Stream stream, int length)
+        //{
+        //    if (stream == null) throw new ArgumentNullException("stream");
+        //    if (length < 0) throw new ArgumentOutOfRangeException("length");
+        //    byte[] buffer = new byte[length];
+        //    int offset = 0, read;
+        //    while (length > 0 && (read = stream.Read(buffer, offset, length)) > 0)
+        //    {
+        //        length -= read;
+        //    }
+        //    if (length > 0) throw EoF(null);
+        //    return buffer;
+        //}
+        private static int ReadByteOrThrow(Stream source)
+        {
+            int val = source.ReadByte();
+            if (val < 0) throw EoF(null);
+            return val;
+        }
+
+        /// <summary>
+        /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
+        /// reader to be created.
+        /// </summary>
+        public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber)
+            => ReadLengthPrefix(source, expectHeader, style, out fieldNumber, out int bytesRead);
+
+        /// <summary>
+        /// Reads a little-endian encoded integer. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static int DirectReadLittleEndianInt32(Stream source)
+        {
+            return ReadByteOrThrow(source)
+                | (ReadByteOrThrow(source) << 8)
+                | (ReadByteOrThrow(source) << 16)
+                | (ReadByteOrThrow(source) << 24);
+        }
+
+        /// <summary>
+        /// Reads a big-endian encoded integer. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static int DirectReadBigEndianInt32(Stream source)
+        {
+            return (ReadByteOrThrow(source) << 24)
+                 | (ReadByteOrThrow(source) << 16)
+                 | (ReadByteOrThrow(source) << 8)
+                 | ReadByteOrThrow(source);
+        }
+
+        /// <summary>
+        /// Reads a varint encoded integer. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static int DirectReadVarintInt32(Stream source)
+        {
+            int bytes = TryReadUInt64Variant(source, out ulong val);
+            if (bytes <= 0) throw EoF(null);
+            return checked((int)val);
+        }
+
+        /// <summary>
+        /// Reads a string (of a given lenth, in bytes) directly from the source into a pre-existing buffer. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static void DirectReadBytes(Stream source, byte[] buffer, int offset, int count)
+        {
+            int read;
+            if (source == null) throw new ArgumentNullException("source");
+            while (count > 0 && (read = source.Read(buffer, offset, count)) > 0)
+            {
+                count -= read;
+                offset += read;
+            }
+            if (count > 0) throw EoF(null);
+        }
+
+        /// <summary>
+        /// Reads a given number of bytes directly from the source. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static byte[] DirectReadBytes(Stream source, int count)
+        {
+            byte[] buffer = new byte[count];
+            DirectReadBytes(source, buffer, 0, count);
+            return buffer;
+        }
+
+        /// <summary>
+        /// Reads a string (of a given lenth, in bytes) directly from the source. An exception is thrown if the data is not all available.
+        /// </summary>
+        public static string DirectReadString(Stream source, int length)
+        {
+            byte[] buffer = new byte[length];
+            DirectReadBytes(source, buffer, 0, length);
+            return Encoding.UTF8.GetString(buffer, 0, length);
+        }
+
+        /// <summary>
+        /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
+        /// reader to be created.
+        /// </summary>
+        public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
+        {
+            if (style == PrefixStyle.None)
+            {
+                bytesRead = fieldNumber = 0;
+                return int.MaxValue; // avoid the long.maxvalue causing overflow
+            }
+            long len64 = ReadLongLengthPrefix(source, expectHeader, style, out fieldNumber, out bytesRead);
+            return checked((int)len64);
+        }
+
+        /// <summary>
+        /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
+        /// reader to be created.
+        /// </summary>
+        public static long ReadLongLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
+        {
+            fieldNumber = 0;
+            switch (style)
+            {
+                case PrefixStyle.None:
+                    bytesRead = 0;
+                    return long.MaxValue;
+                case PrefixStyle.Base128:
+                    ulong val;
+                    int tmpBytesRead;
+                    bytesRead = 0;
+                    if (expectHeader)
+                    {
+                        tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
+                        bytesRead += tmpBytesRead;
+                        if (tmpBytesRead > 0)
+                        {
+                            if ((val & 7) != (uint)WireType.String)
+                            { // got a header, but it isn't a string
+                                throw new InvalidOperationException();
+                            }
+                            fieldNumber = (int)(val >> 3);
+                            tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
+                            bytesRead += tmpBytesRead;
+                            if (bytesRead == 0)
+                            { // got a header, but no length
+                                throw EoF(null);
+                            }
+                            return (long)val;
+                        }
+                        else
+                        { // no header
+                            bytesRead = 0;
+                            return -1;
+                        }
+                    }
+                    // check for a length
+                    tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
+                    bytesRead += tmpBytesRead;
+                    return bytesRead < 0 ? -1 : (long)val;
+
+                case PrefixStyle.Fixed32:
+                    {
+                        int b = source.ReadByte();
+                        if (b < 0)
+                        {
+                            bytesRead = 0;
+                            return -1;
+                        }
+                        bytesRead = 4;
+                        return b
+                             | (ReadByteOrThrow(source) << 8)
+                             | (ReadByteOrThrow(source) << 16)
+                             | (ReadByteOrThrow(source) << 24);
+                    }
+                case PrefixStyle.Fixed32BigEndian:
+                    {
+                        int b = source.ReadByte();
+                        if (b < 0)
+                        {
+                            bytesRead = 0;
+                            return -1;
+                        }
+                        bytesRead = 4;
+                        return (b << 24)
+                            | (ReadByteOrThrow(source) << 16)
+                            | (ReadByteOrThrow(source) << 8)
+                            | ReadByteOrThrow(source);
+                    }
+                default:
+                    throw new ArgumentOutOfRangeException("style");
+            }
+        }
+
+        /// <returns>The number of bytes consumed; 0 if no data available</returns>
+        private static int TryReadUInt64Variant(Stream source, out ulong value)
+        {
+            value = 0;
+            int b = source.ReadByte();
+            if (b < 0) { return 0; }
+            value = (uint)b;
+            if ((value & 0x80) == 0) { return 1; }
+            value &= 0x7F;
+            int bytesRead = 1, shift = 7;
+            while (bytesRead < 9)
+            {
+                b = source.ReadByte();
+                if (b < 0) throw EoF(null);
+                value |= ((ulong)b & 0x7F) << shift;
+                shift += 7;
+                bytesRead++;
+
+                if ((b & 0x80) == 0) return bytesRead;
+            }
+            b = source.ReadByte();
+            if (b < 0) throw EoF(null);
+            if ((b & 1) == 0) // only use 1 bit from the last byte
+            {
+                value |= ((ulong)b & 0x7F) << shift;
+                return ++bytesRead;
+            }
+            throw new OverflowException();
+        }
+
+        internal static void Seek(Stream source, long count, byte[] buffer)
+        {
+            if (source.CanSeek)
+            {
+                source.Seek(count, SeekOrigin.Current);
+                count = 0;
+            }
+            else if (buffer != null)
+            {
+                int bytesRead;
+                while (count > buffer.Length && (bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
+                {
+                    count -= bytesRead;
+                }
+                while (count > 0 && (bytesRead = source.Read(buffer, 0, (int)count)) > 0)
+                {
+                    count -= bytesRead;
+                }
+            }
+            else // borrow a buffer
+            {
+                buffer = BufferPool.GetBuffer();
+                try
+                {
+                    int bytesRead;
+                    while (count > buffer.Length && (bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
+                    {
+                        count -= bytesRead;
+                    }
+                    while (count > 0 && (bytesRead = source.Read(buffer, 0, (int)count)) > 0)
+                    {
+                        count -= bytesRead;
+                    }
+                }
+                finally
+                {
+                    BufferPool.ReleaseBufferToPool(ref buffer);
+                }
+            }
+            if (count > 0) throw EoF(null);
+        }
+        internal static Exception AddErrorData(Exception exception, ProtoReader source)
+        {
+#if !CF && !PORTABLE
+            if (exception != null && source != null && !exception.Data.Contains("protoSource"))
+            {
+                exception.Data.Add("protoSource", string.Format("tag={0}; wire-type={1}; offset={2}; depth={3}",
+                    source.fieldNumber, source.wireType, source.position64, source.depth));
+            }
+#endif
+            return exception;
+        }
+
+        private static Exception EoF(ProtoReader source)
+        {
+            return AddErrorData(new EndOfStreamException(), source);
+        }
+
+        /// <summary>
+        /// Copies the current field into the instance as extension data
+        /// </summary>
+        public void AppendExtensionData(IExtensible instance)
+        {
+            if (instance == null) throw new ArgumentNullException(nameof(instance));
+            IExtension extn = instance.GetExtensionObject(true);
+            bool commit = false;
+            // unusually we *don't* want "using" here; the "finally" does that, with
+            // the extension object being responsible for disposal etc
+            Stream dest = extn.BeginAppend();
+            try
+            {
+                //TODO: replace this with stream-based, buffered raw copying
+                using (ProtoWriter writer = ProtoWriter.Create(dest, model, null))
+                {
+                    AppendExtensionField(writer);
+                    writer.Close();
+                }
+                commit = true;
+            }
+            finally { extn.EndAppend(dest, commit); }
+        }
+
+        private void AppendExtensionField(ProtoWriter writer)
+        {
+            //TODO: replace this with stream-based, buffered raw copying
+            ProtoWriter.WriteFieldHeader(fieldNumber, wireType, writer);
+            switch (wireType)
+            {
+                case WireType.Fixed32:
+                    ProtoWriter.WriteInt32(ReadInt32(), writer);
+                    return;
+                case WireType.Variant:
+                case WireType.SignedVariant:
+                case WireType.Fixed64:
+                    ProtoWriter.WriteInt64(ReadInt64(), writer);
+                    return;
+                case WireType.String:
+                    ProtoWriter.WriteBytes(AppendBytes(null, this), writer);
+                    return;
+                case WireType.StartGroup:
+                    SubItemToken readerToken = StartSubItem(this),
+                        writerToken = ProtoWriter.StartSubItem(null, writer);
+                    while (ReadFieldHeader() > 0) { AppendExtensionField(writer); }
+                    EndSubItem(readerToken, this);
+                    ProtoWriter.EndSubItem(writerToken, writer);
+                    return;
+                case WireType.None: // treat as explicit errorr
+                case WireType.EndGroup: // treat as explicit error
+                default: // treat as implicit error
+                    throw CreateWireTypeException();
+            }
+        }
+
+        /// <summary>
+        /// Indicates whether the reader still has data remaining in the current sub-item,
+        /// additionally setting the wire-type for the next field if there is more data.
+        /// This is used when decoding packed data.
+        /// </summary>
+        public static bool HasSubValue(ProtoBuf.WireType wireType, ProtoReader source)
+        {
+            if (source == null) throw new ArgumentNullException("source");
+            // check for virtual end of stream
+            if (source.blockEnd64 <= source.position64 || wireType == WireType.EndGroup) { return false; }
+            source.wireType = wireType;
+            return true;
+        }
+
+        internal int GetTypeKey(ref Type type)
+        {
+            return model.GetKey(ref type);
+        }
+
+        internal NetObjectCache NetCache => netCache;
+
+        internal Type DeserializeType(string value)
+        {
+            return TypeModel.DeserializeType(model, value);
+        }
+
+        internal void SetRootObject(object value)
+        {
+            netCache.SetKeyedObject(NetObjectCache.Root, value);
+            trapCount--;
+        }
+
+        /// <summary>
+        /// Utility method, not intended for public use; this helps maintain the root object is complex scenarios
+        /// </summary>
+        public static void NoteObject(object value, ProtoReader reader)
+        {
+            if (reader == null) throw new ArgumentNullException("reader");
+            if (reader.trapCount != 0)
+            {
+                reader.netCache.RegisterTrappedObject(value);
+                reader.trapCount--;
+            }
+        }
+
+        /// <summary>
+        /// Reads a Type from the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String
+        /// </summary>
+        public Type ReadType()
+        {
+            return TypeModel.DeserializeType(model, ReadString());
+        }
+
+        internal void TrapNextObject(int newObjectKey)
+        {
+            trapCount++;
+            netCache.SetKeyedObject(newObjectKey, null); // use null as a temp
+        }
+
+        internal void CheckFullyConsumed()
+        {
+            if (isFixedLength)
+            {
+                if (dataRemaining64 != 0) throw new ProtoException("Incorrect number of bytes consumed");
+            }
+            else
+            {
+                if (available != 0) throw new ProtoException("Unconsumed data left in the buffer; this suggests corrupt input");
+            }
+        }
+
+        /// <summary>
+        /// Merge two objects using the details from the current reader; this is used to change the type
+        /// of objects when an inheritance relationship is discovered later than usual during deserilazation.
+        /// </summary>
+        public static object Merge(ProtoReader parent, object from, object to)
+        {
+            if (parent == null) throw new ArgumentNullException("parent");
+            TypeModel model = parent.Model;
+            SerializationContext ctx = parent.Context;
+            if (model == null) throw new InvalidOperationException("Types cannot be merged unless a type-model has been specified");
+            using (var ms = new MemoryStream())
+            {
+                model.Serialize(ms, from, ctx);
+                ms.Position = 0;
+                return model.Deserialize(ms, to, null);
+            }
+        }
+
+        #region RECYCLER
+
+        internal static ProtoReader Create(Stream source, TypeModel model, SerializationContext context, int len)
+            => Create(source, model, context, (long)len);
+        /// <summary>
+        /// Creates a new reader against a stream
+        /// </summary>
+        /// <param name="source">The source stream</param>
+        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
+        /// <param name="context">Additional context about this serialization operation</param>
+        /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
+        public static ProtoReader Create(Stream source, TypeModel model, SerializationContext context = null, long length = TO_EOF)
+        {
+            ProtoReader reader = GetRecycled();
+            if (reader == null)
+            {
+#pragma warning disable CS0618
+                return new ProtoReader(source, model, context, length);
+#pragma warning restore CS0618
+            }
+            Init(reader, source, model, context, length);
+            return reader;
+        }
+
+#if !PLAT_NO_THREADSTATIC
+        [ThreadStatic]
+        private static ProtoReader lastReader;
+
+        private static ProtoReader GetRecycled()
+        {
+            ProtoReader tmp = lastReader;
+            lastReader = null;
+            return tmp;
+        }
+        internal static void Recycle(ProtoReader reader)
+        {
+            if (reader != null)
+            {
+                reader.Dispose();
+                lastReader = reader;
+            }
+        }
+#elif !PLAT_NO_INTERLOCKED
+        private static object lastReader;
+        private static ProtoReader GetRecycled()
+        {
+            return (ProtoReader)System.Threading.Interlocked.Exchange(ref lastReader, null);
+        }
+        internal static void Recycle(ProtoReader reader)
+        {
+            if(reader != null)
+            {
+                reader.Dispose();
+                System.Threading.Interlocked.Exchange(ref lastReader, reader);
+            }
+        }
+#else
+        private static readonly object recycleLock = new object();
+        private static ProtoReader lastReader;
+        private static ProtoReader GetRecycled()
+        {
+            lock(recycleLock)
+            {
+                ProtoReader tmp = lastReader;
+                lastReader = null;
+                return tmp;
+            }            
+        }
+        internal static void Recycle(ProtoReader reader)
+        {
+            if(reader != null)
+            {
+                reader.Dispose();
+                lock(recycleLock)
+                {
+                    lastReader = reader;
+                }
+            }
+        }
+#endif
+
+        #endregion
+    }
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio