/// <summary> /// Fetches a property by fieldName from the provided Server Object /// </summary> /// <param name="server">The server we are fetching the field value from</param> /// <param name="fieldName">the field value we want</param> /// <returns></returns> private static string GetField(GameServer server, string fieldName) { object value = server.GetType().GetProperty(fieldName).GetValue(server, null); if (value == null) { return(String.Empty); } else if (value is Boolean) { return((bool)value ? "1" : "0"); } else { return(value.ToString()); } }
/// <summary> /// Executed every 60 seconds per server (Every 3rd ping), the BF2 server sends a full list /// of data that describes its current state, and this method is used to parse that /// data, and update the server in the Servers list /// </summary> /// <param name="remote">The servers remote address</param> /// <param name="data">The data we must parse, sent by the server</param> /// <returns>Returns whether or not the server needs to be validated, so it can be seen in the Server Browser</returns> private bool ParseServerDetails(IPEndPoint remote, byte[] data) { string key = String.Format("{0}:{1}", remote.Address, remote.Port); // split by 000 (info/player separator) and 002 (players/teams separator) // the players/teams separator is really 00, but because 00 may also be used elsewhere (an empty value for example), we hardcode it to 002 // the 2 is the size of the teams, for BF2 this is always 2. string receivedData = Encoding.UTF8.GetString(data); string[] sections = receivedData.Split(new string[] { "\x00\x00\x00", "\x00\x00\x02" }, StringSplitOptions.None); if (sections.Length != 3 && !receivedData.EndsWith("\x00\x00")) { DebugLog.Write("Invalid Server Data Received From {0} :: {1}", key, sections[0]); return true; // true means we don't send back a response } // We only care about the server sections string serverVars = sections[0]; string[] serverVarsSplit = serverVars.Split(new string[] { "\x00" }, StringSplitOptions.None); // Write to debug log DebugLog.Write("Server Data Received From {0}", key); for (int i = 0; i < sections.Length; i++) DebugLog.Write(" DataString {0}: {1}", i, sections[i]); // Start a new Server Object, and assign its BF2 server properties GameServer server = new GameServer(remote); for (int i = 0; i < serverVarsSplit.Length - 1; i += 2) { // Fetch the property PropertyInfo property = server.GetType().GetProperty(serverVarsSplit[i]); if (property == null) continue; else if (property.Name == "hostname") { // strip consecutive whitespace from hostname property.SetValue(server, Regex.Replace(serverVarsSplit[i + 1], @"\s+", " ").Trim(), null); } else if (property.Name == "bf2_plasma") { property.SetValue(server, false, null); } else if (property.Name == "bf2_ranked") { // we're always a ranked server (helps for mods with a default bf2 main menu, and default filters wanting ranked servers) property.SetValue(server, true, null); } else if (property.Name == "bf2_pure") { // we're always a pure server property.SetValue(server, true, null); } else if (property.PropertyType == typeof(Boolean)) { // parse string to bool (values come in as 1 or 0) int value; if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value != 0, null); } } else if (property.PropertyType == typeof(Int32)) { // parse string to int int value; if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value, null); } } else if (property.PropertyType == typeof(Double)) { // parse string to double double value; if (Double.TryParse(serverVarsSplit[i + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value, null); } } else if (property.PropertyType == typeof(String)) { // parse string to string property.SetValue(server, serverVarsSplit[i + 1], null); } } // you've got to have all these properties in order for your server to be valid if (!String.IsNullOrWhiteSpace(server.hostname) && !String.IsNullOrWhiteSpace(server.gamevariant) && !String.IsNullOrWhiteSpace(server.gamever) && !String.IsNullOrWhiteSpace(server.gametype) && !String.IsNullOrWhiteSpace(server.mapname) && !String.IsNullOrWhiteSpace(server.gamename) && server.gamename.Equals("battlefield2", StringComparison.InvariantCultureIgnoreCase) && server.hostport > 1024 && server.hostport <= UInt16.MaxValue && server.maxplayers > 0) { // Determine if we need to send a challenge key to the server for validation GameServer oldServer; bool IsValidated = Servers.TryGetValue(key, out oldServer) && oldServer.IsValidated; DebugLog.Write("Server Data Parsed Successfully... Needs Validated: " + ((IsValidated) ? "false" : "true")); // Copy over the local lan fix if we already have in the past if (IsValidated) { server.AddressInfo.Address = oldServer.AddressInfo.Address; server.country = oldServer.country; } // Add / Update Server server.IsValidated = IsValidated; server.LastPing = DateTime.Now; server.LastRefreshed = DateTime.Now; Servers.AddOrUpdate(key, server, (k, old) => { return server; }); // Tell the requester if we are good to go return IsValidated; } // If we are here, the server information is partial/invalid. Return true to ignore server // until we can get some good data. DebugLog.Write("Data from {0} was partial. Assuming server switched game status. Complete data expected in 10 seconds.", key); return true; }
/// <summary> /// Executed every 60 seconds per server (Every 3rd ping), the BF2 server sends a full list /// of data that describes its current state, and this method is used to parse that /// data, and update the server in the Servers list /// </summary> /// <param name="remote">The servers remote address</param> /// <param name="data">The data we must parse, sent by the server</param> /// <returns>Returns whether or not the server needs to be validated, so it can be seen in the Server Browser</returns> private bool ParseServerDetails(IPEndPoint remote, byte[] data) { string key = String.Format("{0}:{1}", remote.Address, remote.Port); // split by 000 (info/player separator) and 002 (players/teams separator) // the players/teams separator is really 00, but because 00 may also be used elsewhere (an empty value for example), we hardcode it to 002 // the 2 is the size of the teams, for BF2 this is always 2. string receivedData = Encoding.UTF8.GetString(data); string[] sections = receivedData.Split(new string[] { "\x00\x00\x00", "\x00\x00\x02" }, StringSplitOptions.None); if (sections.Length != 3 && !receivedData.EndsWith("\x00\x00")) { DebugLog.Write("Invalid Server Data Received From {0} :: {1}", key, sections[0]); return(true); // true means we don't send back a response } // We only care about the server sections string serverVars = sections[0]; string[] serverVarsSplit = serverVars.Split(new string[] { "\x00" }, StringSplitOptions.None); // Write to debug log DebugLog.Write("Server Data Received From {0}", key); for (int i = 0; i < sections.Length; i++) { DebugLog.Write(" DataString {0}: {1}", i, sections[i]); } // Start a new Server Object, and assign its BF2 server properties GameServer server = new GameServer(remote); for (int i = 0; i < serverVarsSplit.Length - 1; i += 2) { // Fetch the property PropertyInfo property = server.GetType().GetProperty(serverVarsSplit[i]); if (property == null) { continue; } else if (property.Name == "hostname") { // strip consecutive whitespace from hostname property.SetValue(server, Regex.Replace(serverVarsSplit[i + 1], @"\s+", " ").Trim(), null); } else if (property.Name == "bf2_plasma") { property.SetValue(server, false, null); } else if (property.Name == "bf2_ranked") { // we're always a ranked server (helps for mods with a default bf2 main menu, and default filters wanting ranked servers) property.SetValue(server, true, null); } else if (property.Name == "bf2_pure") { // we're always a pure server property.SetValue(server, true, null); } else if (property.PropertyType == typeof(Boolean)) { // parse string to bool (values come in as 1 or 0) int value; if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value != 0, null); } } else if (property.PropertyType == typeof(Int32)) { // parse string to int int value; if (Int32.TryParse(serverVarsSplit[i + 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value, null); } } else if (property.PropertyType == typeof(Double)) { // parse string to double double value; if (Double.TryParse(serverVarsSplit[i + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { property.SetValue(server, value, null); } } else if (property.PropertyType == typeof(String)) { // parse string to string property.SetValue(server, serverVarsSplit[i + 1], null); } } // you've got to have all these properties in order for your server to be valid if (!String.IsNullOrWhiteSpace(server.hostname) && !String.IsNullOrWhiteSpace(server.gamevariant) && !String.IsNullOrWhiteSpace(server.gamever) && !String.IsNullOrWhiteSpace(server.gametype) && !String.IsNullOrWhiteSpace(server.mapname) && !String.IsNullOrWhiteSpace(server.gamename) && server.gamename.Equals("battlefield2", StringComparison.InvariantCultureIgnoreCase) && server.hostport > 1024 && server.hostport <= UInt16.MaxValue && server.maxplayers > 0) { // Determine if we need to send a challenge key to the server for validation GameServer oldServer; bool IsValidated = Servers.TryGetValue(key, out oldServer) && oldServer.IsValidated; DebugLog.Write("Server Data Parsed Successfully... Needs Validated: " + ((IsValidated) ? "false" : "true")); // Copy over the local lan fix if we already have in the past if (IsValidated) { server.AddressInfo.Address = oldServer.AddressInfo.Address; server.country = oldServer.country; } // Add / Update Server server.IsValidated = IsValidated; server.LastPing = DateTime.Now; server.LastRefreshed = DateTime.Now; Servers.AddOrUpdate(key, server, (k, old) => { return(server); }); // Tell the requester if we are good to go return(IsValidated); } // If we are here, the server information is partial/invalid. Return true to ignore server // until we can get some good data. DebugLog.Write("Data from {0} was partial. Assuming server switched game status. Complete data expected in 10 seconds.", key); return(true); }
/// <summary> /// Fetches a property by fieldName from the provided Server Object /// </summary> /// <param name="server">The server we are fetching the field value from</param> /// <param name="fieldName">the field value we want</param> /// <returns></returns> private static string GetField(GameServer server, string fieldName) { object value = server.GetType().GetProperty(fieldName).GetValue(server, null); if (value == null) return String.Empty; else if (value is Boolean) return (bool)value ? "1" : "0"; else return value.ToString(); }