private static async Task HandleIncomingMessage(Socket socket, int offset, int size) { try { if (size <= 0) { return; } using (MemoryStream stream = new MemoryStream(receiveBuffers[socket], offset, size)) { using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true)) { byte messageType = reader.ReadByte(); if (messageType == (byte)MessageType.RegisterServer) { Console.WriteLine("[Register] Started"); // Parse contract Dictionary <string, ContractValue> contractValues = new Dictionary <string, ContractValue>(); int valueCount = reader.ReadInt32(); for (int i = 0; i < valueCount; i++) { ulong nameHash = reader.ReadUInt64(); ContractType type = (ContractType)reader.ReadByte(); if (contracts.TryGetValue(nameHash, out ContractDefinition definition) && definition.Type == type) { object boxedValue = null; switch (definition.Type) { case ContractType.Int8: boxedValue = (long)reader.ReadSByte(); break; case ContractType.Int16: boxedValue = (long)reader.ReadInt16(); break; case ContractType.Int32: boxedValue = (long)reader.ReadInt32(); break; case ContractType.Int64: boxedValue = (long)reader.ReadInt32(); break; case ContractType.UInt8: boxedValue = (long)reader.ReadByte(); break; case ContractType.UInt16: boxedValue = (long)reader.ReadUInt16(); break; case ContractType.UInt32: boxedValue = (long)reader.ReadUInt32(); break; case ContractType.UInt64: boxedValue = (long)reader.ReadUInt64(); break; case ContractType.String: boxedValue = reader.ReadString(); break; case ContractType.Buffer: boxedValue = reader.ReadBytes(reader.ReadInt32()); break; case ContractType.Guid: boxedValue = new Guid(reader.ReadString()); break; case ContractType.Location: Geolocation geoLocation = new Geolocation(); geoLocation.type = reader.ReadString(); geoLocation.coordinates = new float[] { reader.ReadSingle(), reader.ReadSingle() }; boxedValue = geoLocation; break; } if (boxedValue != null) { contractValues.Add(definition.Name, new ContractValue() { Definition = definition, Value = boxedValue }); } } else { switch (type) { case ContractType.Int8: reader.ReadSByte(); break; case ContractType.Int16: reader.ReadInt16(); break; case ContractType.Int32: reader.ReadInt32(); break; case ContractType.Int64: reader.ReadInt32(); break; case ContractType.UInt8: reader.ReadByte(); break; case ContractType.UInt16: reader.ReadUInt16(); break; case ContractType.UInt32: reader.ReadUInt32(); break; case ContractType.UInt64: reader.ReadUInt64(); break; case ContractType.String: reader.ReadString(); break; case ContractType.Buffer: reader.ReadBytes(reader.ReadInt32()); break; case ContractType.Guid: reader.ReadString(); break; case ContractType.Location: reader.ReadString(); reader.ReadSingle(); reader.ReadSingle(); break; } } } // Contract validation, ensure all REQUIRED fields are present for (int i = 0; i < configuration.ServerContract.Length; i++) { if (configuration.ServerContract[i].Required) { if (!contractValues.TryGetValue(configuration.ServerContract[i].Name, out ContractValue contractValue) || contractValue.Definition.Type != configuration.ServerContract[i].Type) { // Failure, contract did not match using (MemoryStream outStream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(outStream, Encoding.UTF8, true)) { writer.Write((byte)MessageType.RegisterAck); writer.Write(new Guid().ToString()); writer.Write(false); } socket.BeginSend(outStream.GetBuffer(), 0, (int)outStream.Length, SocketFlags.None, (e) => { socket.EndSend(e); }, null); } Console.WriteLine("[Register] Registrar broke contract. Missing required field \"" + configuration.ServerContract[i].Name + "\" of type " + configuration.ServerContract[i].Type); return; } } } List <ContractValue> validatedValues = new List <ContractValue>(); // Remove all fields not part of contract for (int i = 0; i < configuration.ServerContract.Length; i++) { if (contractValues.TryGetValue(configuration.ServerContract[i].Name, out ContractValue contractValue) && contractValue.Definition.Type == configuration.ServerContract[i].Type) { validatedValues.Add(contractValue); } } // Create model for DB ServerModel server = new ServerModel() { Id = Guid.NewGuid().ToString(), LastPingTime = DateTime.UtcNow, Address = ((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6(), ContractData = new Dictionary <string, object>() }; // Add contract values to model for (int i = 0; i < validatedValues.Count; i++) { server.ContractData.Add(validatedValues[i].Definition.Name, validatedValues[i].Value); } if (configuration.VerbosePrints) { Console.WriteLine("[Register] Adding: " + JsonConvert.SerializeObject(server)); } else { Console.WriteLine("[Register] Adding 1 server"); } if (configuration.UseMongo) { // Insert model to DB await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").InsertOneAsync(server); } else { localModels.Add(server); } using (MemoryStream outStream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(outStream, Encoding.UTF8, true)) { writer.Write((byte)MessageType.RegisterAck); writer.Write(server.Id); writer.Write(true); } socket.BeginSend(outStream.GetBuffer(), 0, (int)outStream.Length, SocketFlags.None, (e) => { socket.EndSend(e); }, null); } } else if (messageType == (byte)MessageType.Query) { DateTime startTime = DateTime.Now; Console.WriteLine("[Query] Started"); string guid = reader.ReadString(); string query = reader.ReadString(); Console.WriteLine("[Query] Parsing"); JObject parsedQuery = JObject.Parse(query); List <ServerModel> serverModel = null; if (configuration.UseMongo) { Console.WriteLine("[Query] Creating mongo filter"); FilterDefinition <ServerModel> filter = Builders <ServerModel> .Filter.And(Builders <ServerModel> .Filter.Where(x => x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)), QueryParser.CreateFilter(new List <JToken>() { parsedQuery })); //FilterDefinition<ServerModel> filter = QueryParser.CreateFilter(new List<JToken>() { parsedQuery }); if (configuration.VerbosePrints) { Console.WriteLine("[Query] Executing mongo query \"" + mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").Find(filter) + "\""); } else { Console.WriteLine("[Query] Executing mongo query"); } serverModel = await(await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").FindAsync(filter)).ToListAsync(); } else { Console.WriteLine("[Query] Querying local"); serverModel = localModels.AsParallel().Where(x => x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout) && QueryParser.FilterLocalServers(new List <JToken>() { parsedQuery }, x)).ToList(); } Console.WriteLine("[Query] Found " + (serverModel == null ? 0 : serverModel.Count) + " results. Total query time: " + (DateTime.Now - startTime).TotalMilliseconds + " ms"); using (MemoryStream outStream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(outStream, Encoding.UTF8, true)) { writer.Write((byte)MessageType.QueryResponse); writer.Write(guid); writer.Write(serverModel.Count); for (int i = 0; i < serverModel.Count; i++) { writer.Write(serverModel[i].Id); writer.Write(serverModel[i].Address.MapToIPv6().GetAddressBytes()); writer.Write(serverModel[i].LastPingTime.ToBinary()); writer.Write(serverModel[i].ContractData.Count); foreach (KeyValuePair <string, object> pair in serverModel[i].ContractData) { writer.Write(pair.Key); writer.Write((byte)contracts[pair.Key.GetStableHash64()].Type); switch (contracts[pair.Key.GetStableHash64()].Type) { case ContractType.Int8: writer.Write((sbyte)(long)pair.Value); break; case ContractType.Int16: writer.Write((short)(long)pair.Value); break; case ContractType.Int32: writer.Write((int)(long)pair.Value); break; case ContractType.Int64: writer.Write((long)pair.Value); break; case ContractType.UInt8: writer.Write((byte)(long)pair.Value); break; case ContractType.UInt16: writer.Write((ushort)(long)pair.Value); break; case ContractType.UInt32: writer.Write((uint)(long)pair.Value); break; case ContractType.UInt64: writer.Write((ulong)(long)pair.Value); break; case ContractType.String: writer.Write((string)pair.Value); break; case ContractType.Buffer: writer.Write(((byte[])pair.Value).Length); writer.Write((byte[])pair.Value); break; case ContractType.Guid: writer.Write(((Guid)pair.Value).ToString()); break; case ContractType.Location: var geoLocation = (Geolocation)pair.Value; writer.Write(geoLocation.type); foreach (float element in geoLocation.coordinates) { writer.Write(element); } break; } } } } socket.BeginSend(outStream.GetBuffer(), 0, (int)outStream.Length, SocketFlags.None, (e) => { socket.EndSend(e); }, null); } } else if (messageType == (byte)MessageType.ServerAlive) { Console.WriteLine("[Alive] Started"); Guid guid = new Guid(reader.ReadString()); if (configuration.VerbosePrints) { Console.WriteLine("[Alive] Parsed from " + guid.ToString()); } else { Console.WriteLine("[Alive] Parsed"); } if (configuration.UseMongo) { // Find and validate address ownership FilterDefinition <ServerModel> filter = Builders <ServerModel> .Filter.And(Builders <ServerModel> .Filter.Where(x => x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)), Builders <ServerModel> .Filter.Eq(x => x.Address, ((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()), Builders <ServerModel> .Filter.Eq(x => x.Id, guid.ToString())); // Create update UpdateDefinition <ServerModel> update = Builders <ServerModel> .Update.Set(x => x.LastPingTime, DateTime.UtcNow); // Execute await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").FindOneAndUpdateAsync(filter, update); } else { ServerModel model = localModels.Find(x => x.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()) && x.Id == guid.ToString() && x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)); if (model != null) { model.LastPingTime = DateTime.UtcNow; } } } else if (messageType == (byte)MessageType.RemoveServer) { Console.WriteLine("[Remove] Started"); Guid guid = new Guid(reader.ReadString()); if (configuration.VerbosePrints) { Console.WriteLine("[Remove] Parsed from " + guid.ToString()); } else { Console.WriteLine("[Remove] Parsed"); } ServerModel model = null; if (configuration.UseMongo) { // Find and validate address ownership FilterDefinition <ServerModel> filter = Builders <ServerModel> .Filter.And(Builders <ServerModel> .Filter.Where(x => x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)), Builders <ServerModel> .Filter.Eq(x => x.Address, ((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()), Builders <ServerModel> .Filter.Eq(x => x.Id, guid.ToString())); // Execute model = await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").FindOneAndDeleteAsync(filter); } else { model = localModels.Find(x => x.Id == guid.ToString() && x.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6())); if (model != null) { localModels.Remove(model); } } if (model != null) { if (configuration.VerbosePrints) { Console.WriteLine("[Remove] Removed: " + JsonConvert.SerializeObject(model)); } else { Console.WriteLine("[Remove] Removed 1 element"); } } else { Console.WriteLine("[Remove] Not found"); } } else if (messageType == (byte)MessageType.UpdateServer) { Console.WriteLine("[Update] Started"); Guid guid = new Guid(reader.ReadString()); ServerModel result = null; if (configuration.UseMongo) { result = await(await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").FindAsync(x => x.Id == guid.ToString() && x.Address == ((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6() && x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout))).FirstOrDefaultAsync(); } else { result = localModels.Find(x => x.Id == guid.ToString() && x.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()) && x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)); } if (result != null) { // Parse contract Dictionary <string, ContractValue> contractValues = new Dictionary <string, ContractValue>(); int valueCount = reader.ReadInt32(); for (int i = 0; i < valueCount; i++) { ulong nameHash = reader.ReadUInt64(); ContractType type = (ContractType)reader.ReadByte(); if (contracts.TryGetValue(nameHash, out ContractDefinition definition) && definition.Type == type) { object boxedValue = null; switch (definition.Type) { case ContractType.Int8: boxedValue = (long)reader.ReadSByte(); break; case ContractType.Int16: boxedValue = (long)reader.ReadInt16(); break; case ContractType.Int32: boxedValue = (long)reader.ReadInt32(); break; case ContractType.Int64: boxedValue = (long)reader.ReadInt64(); break; case ContractType.UInt8: boxedValue = (long)reader.ReadByte(); break; case ContractType.UInt16: boxedValue = (long)reader.ReadUInt16(); break; case ContractType.UInt32: boxedValue = (long)reader.ReadUInt32(); break; case ContractType.UInt64: boxedValue = (long)reader.ReadUInt64(); break; case ContractType.String: boxedValue = reader.ReadString(); break; case ContractType.Buffer: boxedValue = reader.ReadBytes(reader.ReadInt32()); break; case ContractType.Guid: boxedValue = new Guid(reader.ReadString()); break; case ContractType.Location: Geolocation geoLocation = new Geolocation(); geoLocation.type = reader.ReadString(); geoLocation.coordinates = new float[] { reader.ReadSingle(), reader.ReadSingle() }; boxedValue = geoLocation; break; } if (boxedValue != null) { contractValues.Add(definition.Name, new ContractValue() { Definition = definition, Value = boxedValue }); } } else { switch (type) { case ContractType.Int8: reader.ReadSByte(); break; case ContractType.Int16: reader.ReadInt16(); break; case ContractType.Int32: reader.ReadInt32(); break; case ContractType.Int64: reader.ReadInt64(); break; case ContractType.UInt8: reader.ReadByte(); break; case ContractType.UInt16: reader.ReadUInt16(); break; case ContractType.UInt32: reader.ReadUInt32(); break; case ContractType.UInt64: reader.ReadUInt64(); break; case ContractType.String: reader.ReadString(); break; case ContractType.Buffer: reader.ReadBytes(reader.ReadInt32()); break; case ContractType.Guid: reader.ReadString(); break; case ContractType.Location: reader.ReadString(); reader.ReadSingle(); reader.ReadSingle(); break; } } } // Contract validation, ensure all REQUIRED fields are present for (int i = 0; i < configuration.ServerContract.Length; i++) { if (configuration.ServerContract[i].Required) { if (!contractValues.TryGetValue(configuration.ServerContract[i].Name, out ContractValue contractValue) || contractValue.Definition.Type != configuration.ServerContract[i].Type) { // Failure, contract did not match return; } } } List <ContractValue> validatedValues = new List <ContractValue>(); // Remove all fields not part of contract for (int i = 0; i < configuration.ServerContract.Length; i++) { if (contractValues.TryGetValue(configuration.ServerContract[i].Name, out ContractValue contractValue) && contractValue.Definition.Type == configuration.ServerContract[i].Type) { validatedValues.Add(contractValue); } } Dictionary <string, object> validatedLookupValues = new Dictionary <string, object>(); // Add contract values to model for (int i = 0; i < validatedValues.Count; i++) { validatedLookupValues.Add(validatedValues[i].Definition.Name, validatedValues[i].Value); } if (configuration.UseMongo) { // Find and validate address ownership FilterDefinition <ServerModel> filter = Builders <ServerModel> .Filter.And(Builders <ServerModel> .Filter.Where(x => x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)), Builders <ServerModel> .Filter.Eq(x => x.Address, ((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()), Builders <ServerModel> .Filter.Eq(x => x.Id, guid.ToString())); // Create update UpdateDefinition <ServerModel> update = Builders <ServerModel> .Update.Set(x => x.LastPingTime, DateTime.UtcNow).Set(x => x.ContractData, validatedLookupValues); // Insert model to DB await mongoClient.GetDatabase(configuration.MongoDatabase).GetCollection <ServerModel>("servers").FindOneAndUpdateAsync(filter, update); } else { ServerModel model = localModels.Find(x => x.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address.MapToIPv6()) && x.Id == guid.ToString() && x.LastPingTime >= DateTime.UtcNow.AddMilliseconds(-configuration.ServerTimeout)); model.LastPingTime = DateTime.UtcNow; model.ContractData = validatedLookupValues; } } } else if (messageType == (byte)MessageType.ContractCheck) { Console.WriteLine("[ContractCheck] Started"); string guid = reader.ReadString(); int contractCount = reader.ReadInt32(); WeakContractDefinition[] remoteContracts = new WeakContractDefinition[contractCount]; for (int i = 0; i < contractCount; i++) { remoteContracts[i] = new WeakContractDefinition() { Name = reader.ReadString(), Type = (ContractType)reader.ReadByte() }; } using (MemoryStream outStream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(outStream, Encoding.UTF8, true)) { writer.Write((byte)MessageType.ContractResponse); writer.Write(guid); writer.Write(ContractDefinition.IsCompatible(remoteContracts, contracts.Select(x => x.Value).ToArray())); } socket.BeginSend(outStream.GetBuffer(), 0, (int)outStream.Length, SocketFlags.None, (e) => { socket.EndSend(e); }, null); } } } } } catch (Exception e) { Console.WriteLine(e); } }