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;
}