gspylogin hinzugefügt

This commit is contained in:
BlubbFish 2015-11-15 23:51:29 +00:00
commit cf135322d8
29 changed files with 8429 additions and 0 deletions

20
gspylogin.sln Normal file
View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gspylogin", "gspylogin\gspylogin.csproj", "{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,527 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SQLite;
using MySql;
using MySql.Data.Common;
using MySql.Data.MySqlClient;
namespace BF2Statistics.Database
{
public class DatabaseDriver : IDisposable
{
/// <summary>
/// Current DB Engine
/// </summary>
public DatabaseEngine DatabaseEngine { get; protected set; }
/// <summary>
/// The database connection
/// </summary>
public DbConnection Connection { get; protected set; }
/// <summary>
/// Returns whether the Database connection is open
/// </summary>
public bool IsConnected
{
get { return (Connection.State == ConnectionState.Open); }
}
/// <summary>
/// Returns the current conenction state of the database
/// </summary>
public ConnectionState State
{
get { return Connection.State; }
}
/// <summary>
/// Gets the number of queries ran by this instance
/// </summary>
public int NumQueries = 0;
/// <summary>
/// Random, yes... But its used here when building queries dynamically
/// </summary>
protected static char Comma = ',';
/// <summary>
/// Indicates whether the disposed method was called
/// </summary>
protected bool IsDisposed = false;
/// <summary>
/// Constructor
/// </summary>
/// <param name="Engine">The string name, for the GetDatabaseEngine() method</param>
/// <param name="Host">The Database server IP Address</param>
/// <param name="Port">The Database server Port Number</param>
/// <param name="DatabaseName">The name of the database</param>
/// <param name="User">A username, with database privliages</param>
/// <param name="Pass">The password to the User</param>
public DatabaseDriver(string Engine, string Host, int Port, string DatabaseName, string User, string Pass)
{
// Set class variables, and create a new connection builder
this.DatabaseEngine = GetDatabaseEngine(Engine);
DbConnectionStringBuilder Builder;
// Establish the connection
if (this.DatabaseEngine == DatabaseEngine.Sqlite)
{
// Create the connection
Builder = new SQLiteConnectionStringBuilder();
Builder.Add("Data Source", Path.Combine(Program.RootPath, DatabaseName + ".sqlite3"));
Connection = new SQLiteConnection(Builder.ConnectionString);
}
else if (this.DatabaseEngine == DatabaseEngine.Mysql)
{
// Create the connection
Builder = new MySqlConnectionStringBuilder();
Builder.Add("Server", Host);
Builder.Add("Port", Port);
Builder.Add("User ID", User);
Builder.Add("Password", Pass);
Builder.Add("Database", DatabaseName);
Builder.Add("Convert Zero Datetime", "true");
Connection = new MySqlConnection(Builder.ConnectionString);
}
else
{
throw new Exception("Invalid Database type.");
}
}
/// <summary>
/// Destructor
/// </summary>
~DatabaseDriver()
{
Dispose();
}
/// <summary>
/// Disposes the DB connection
/// </summary>
public void Dispose()
{
if(Connection != null && !IsDisposed)
{
try
{
Connection.Close();
Connection.Dispose();
}
catch (ObjectDisposedException) { }
IsDisposed = true;
}
}
/// <summary>
/// Opens the database connection
/// </summary>
public void Connect()
{
if (Connection.State != ConnectionState.Open)
{
try
{
Connection.Open();
}
catch (Exception e)
{
throw new DbConnectException("Unable to etablish database connection", e);
}
}
}
/// <summary>
/// Closes the connection to the database
/// </summary>
public void Close()
{
try {
if (Connection.State != ConnectionState.Closed)
Connection.Close();
}
catch (ObjectDisposedException) { }
}
/// <summary>
/// Creates a new command to be executed on the database
/// </summary>
/// <param name="QueryString"></param>
public DbCommand CreateCommand(string QueryString)
{
if (DatabaseEngine == Database.DatabaseEngine.Sqlite)
return new SQLiteCommand(QueryString, Connection as SQLiteConnection);
else
return new MySqlCommand(QueryString, Connection as MySqlConnection);
}
/// <summary>
/// Creates a DbParameter using the current Database engine's Parameter object
/// </summary>
/// <returns></returns>
public DbParameter CreateParam()
{
if (DatabaseEngine == Database.DatabaseEngine.Sqlite)
return (new SQLiteParameter() as DbParameter);
else
return (new MySqlParameter() as DbParameter);
}
/// <summary>
/// Queries the database, and returns a result set
/// </summary>
/// <param name="Sql">The SQL Statement to run on the database</param>
/// <returns></returns>
public List<Dictionary<string, object>> Query(string Sql)
{
return this.Query(Sql, new List<DbParameter>());
}
/// <summary>
/// Queries the database, and returns a result set
/// </summary>
/// <param name="Sql">The SQL Statement to run on the database</param>
/// <param name="Items">Additional parameters are parameter values for the query.
/// The first parameter replaces @P0, second @P1 etc etc.
/// </param>
/// <returns></returns>
public List<Dictionary<string, object>> Query(string Sql, params object[] Items)
{
List<DbParameter> Params = new List<DbParameter>(Items.Length);
for (int i = 0; i < Items.Length; i++)
{
DbParameter Param = this.CreateParam();
Param.ParameterName = "@P" + i;
Param.Value = Items[i];
Params.Add(Param);
}
return this.Query(Sql, Params);
}
/// <summary>
/// Queries the database, and returns a result set
/// </summary>
/// <param name="Sql">The SQL Statement to run on the database</param>
/// <param name="Params">A list of sql params to add to the command</param>
/// <returns></returns>
public List<Dictionary<string, object>> Query(string Sql, List<DbParameter> Params)
{
// Create our Rows result
List<Dictionary<string, object>> Rows = new List<Dictionary<string, object>>();
// Increase Query Count
NumQueries++;
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
{
// Add params
foreach (DbParameter Param in Params)
Command.Parameters.Add(Param);
// Execute the query
using (DbDataReader Reader = Command.ExecuteReader())
{
// If we have rows, add them to the list
if (Reader.HasRows)
{
// Add each row to the rows list
while (Reader.Read())
{
Dictionary<string, object> Row = new Dictionary<string, object>(Reader.FieldCount);
for (int i = 0; i < Reader.FieldCount; ++i)
Row.Add(Reader.GetName(i), Reader.GetValue(i));
Rows.Add(Row);
}
}
// Cleanup
Reader.Close();
}
}
// Return Rows
return Rows;
}
/// <summary>
/// Queries the database, and returns 1 row at a time until all rows are returned
/// </summary>
/// <param name="Sql">The SQL Statement to run on the database</param>
/// <returns></returns>
public IEnumerable<Dictionary<string, object>> QueryReader(string Sql)
{
// Increase Query Count
NumQueries++;
// Create the SQL Command, and execute the reader
using (DbCommand Command = this.CreateCommand(Sql))
using (DbDataReader Reader = Command.ExecuteReader())
{
// If we have rows, add them to the list
if (Reader.HasRows)
{
// Add each row to the rows list
while (Reader.Read())
{
Dictionary<string, object> Row = new Dictionary<string, object>(Reader.FieldCount);
for (int i = 0; i < Reader.FieldCount; ++i)
Row.Add(Reader.GetName(i), Reader.GetValue(i));
yield return Row;
}
}
// Cleanup
Reader.Close();
}
}
/// <summary>
/// Executes a command, and returns 1 row at a time until all rows are returned
/// </summary>
/// <param name="Command">The database command to execute the reader on</param>
/// <returns></returns>
public IEnumerable<Dictionary<string, object>> QueryReader(DbCommand Command)
{
// Increase Query Count
NumQueries++;
// Execute the query
using (Command)
using (DbDataReader Reader = Command.ExecuteReader())
{
// If we have rows, add them to the list
if (Reader.HasRows)
{
// Add each row to the rows list
while (Reader.Read())
{
Dictionary<string, object> Row = new Dictionary<string, object>(Reader.FieldCount);
for (int i = 0; i < Reader.FieldCount; ++i)
Row.Add(Reader.GetName(i), Reader.GetValue(i));
yield return Row;
}
}
// Cleanup
Reader.Close();
}
}
/// <summary>
/// Executes a command, and returns the resulting rows
/// </summary>
/// <param name="Command">The database command to execute the reader on</param>
/// <returns></returns>
public List<Dictionary<string, object>> ExecuteReader(DbCommand Command)
{
// Execute the query
List<Dictionary<string, object>> Rows = new List<Dictionary<string, object>>();
// Increase Query Count
NumQueries++;
using (Command)
using (DbDataReader Reader = Command.ExecuteReader())
{
// If we have rows, add them to the list
if (Reader.HasRows)
{
// Add each row to the rows list
while (Reader.Read())
{
Dictionary<string, object> Row = new Dictionary<string, object>(Reader.FieldCount);
for (int i = 0; i < Reader.FieldCount; ++i)
Row.Add(Reader.GetName(i), Reader.GetValue(i));
Rows.Add(Row);
}
}
// Cleanup
Reader.Close();
}
// Return Rows
return Rows;
}
/// <summary>
/// Executes a statement on the database (Update, Delete, Insert)
/// </summary>
/// <param name="Sql">The SQL statement to be executes</param>
/// <returns>Returns the number of rows affected by the statement</returns>
public int Execute(string Sql)
{
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
return Command.ExecuteNonQuery();
}
/// <summary>
/// Executes a statement on the database (Update, Delete, Insert)
/// </summary>
/// <param name="Sql">The SQL statement to be executed</param>
/// <param name="Params">A list of Sqlparameters</param>
/// <returns>Returns the number of rows affected by the statement</returns>
public int Execute(string Sql, List<DbParameter> Params)
{
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
{
// Increase Query Count
NumQueries++;
// Add params
foreach (DbParameter Param in Params)
Command.Parameters.Add(Param);
// Execute command, and dispose of the command
return Command.ExecuteNonQuery();
}
}
/// <summary>
/// Executes a statement on the database (Update, Delete, Insert)
/// </summary>
/// <param name="Sql">The SQL statement to be executed</param>
/// <param name="Items">Additional parameters are parameter values for the query.
/// The first parameter replaces @P0, second @P1 etc etc.
/// </param>
/// <returns>Returns the number of rows affected by the statement</returns>
public int Execute(string Sql, params object[] Items)
{
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
{
// Add params
for (int i = 0; i < Items.Length; i++)
{
DbParameter Param = this.CreateParam();
Param.ParameterName = "@P" + i;
Param.Value = Items[i];
Command.Parameters.Add(Param);
}
// Increase Query Count
NumQueries++;
// Execute command, and dispose of the command
return Command.ExecuteNonQuery();
}
}
/// <summary>
/// Executes the query, and returns the first column of the first row in the result
/// set returned by the query. Additional columns or rows are ignored.
/// </summary>
/// <param name="Sql">The SQL statement to be executed</param>
/// <returns></returns>
public object ExecuteScalar(string Sql)
{
// Increase Query Count
NumQueries++;
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
return Command.ExecuteScalar();
}
/// <summary>
/// Executes the query, and returns the first column of the first row in the result
/// set returned by the query. Additional columns or rows are ignored.
/// </summary>
/// <param name="Sql">The SQL statement to be executed</param>
/// <param name="Params">A list of Sqlparameters</param>
/// <returns></returns>
public object ExecuteScalar(string Sql, List<DbParameter> Params)
{
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
{
// Increase Query Count
NumQueries++;
// Add params
foreach (DbParameter Param in Params)
Command.Parameters.Add(Param);
// Execute command, and dispose of the command
return Command.ExecuteScalar();
}
}
/// <summary>
/// Executes the query, and returns the first column of the first row in the result
/// set returned by the query. Additional columns or rows are ignored.
/// </summary>
/// <param name="Sql">The SQL statement to be executed</param>
/// <param name="Items"></param>
/// <returns></returns>
public object ExecuteScalar(string Sql, params object[] Items)
{
// Create the SQL Command
using (DbCommand Command = this.CreateCommand(Sql))
{
// Add params
for (int i = 0; i < Items.Length; i++)
{
DbParameter Param = this.CreateParam();
Param.ParameterName = "@P" + i;
Param.Value = Items[i];
Command.Parameters.Add(Param);
}
// Increase Query Count
NumQueries++;
// Execute command, and dispose of the command
return Command.ExecuteScalar();
}
}
/// <summary>
/// Begins a new database transaction
/// </summary>
/// <returns></returns>
public DbTransaction BeginTransaction()
{
return Connection.BeginTransaction();
}
/// <summary>
/// Begins a new database transaction
/// </summary>
/// <param name="Level"></param>
/// <returns></returns>
public DbTransaction BeginTransaction(IsolationLevel Level)
{
return Connection.BeginTransaction(Level);
}
/// <summary>
/// Converts a database string name to a DatabaseEngine type.
/// </summary>
/// <param name="Name"></param>
/// <returns></returns>
public static DatabaseEngine GetDatabaseEngine(string Name)
{
return ((DatabaseEngine)Enum.Parse(typeof(DatabaseEngine), Name, true));
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics.Database
{
public enum DatabaseEngine
{
Sqlite,
Mysql,
}
}

View File

@ -0,0 +1,298 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics.Database
{
/// <summary>
/// A class to provide common tasks against the Gamespy Login Database
/// </summary>
public class GamespyDatabase : DatabaseDriver, IDisposable
{
/// <summary>
/// Constructor
/// </summary>
public GamespyDatabase() :
base(
MainForm.Config.GamespyDBEngine,
MainForm.Config.GamespyDBHost,
MainForm.Config.GamespyDBPort,
MainForm.Config.GamespyDBName,
MainForm.Config.GamespyDBUser,
MainForm.Config.GamespyDBPass
)
{
// Try and Reconnect
try
{
Connect();
// Try to get the database version
try
{
if (base.Query("SELECT dbver FROM _version LIMIT 1").Count == 0)
throw new Exception();
}
catch
{
// If an exception is thrown, table doesnt exist... fresh install
if (DatabaseEngine == DatabaseEngine.Sqlite)
base.Execute(Utils.GetResourceAsString("BF2Statistics.SQL.SQLite.Gamespy.sql"));
else
base.Execute(Utils.GetResourceAsString("BF2Statistics.SQL.MySQL.Gamespy.sql"));
}
}
catch (Exception)
{
if (Connection != null)
Connection.Dispose();
throw;
}
}
/// <summary>
/// Destructor
/// </summary>
~GamespyDatabase()
{
if (!IsDisposed)
base.Dispose();
}
/// <summary>
/// Fetches an account from the gamespy database
/// </summary>
/// <param name="Nick">The user's Nick</param>
/// <returns></returns>
public Dictionary<string, object> GetUser(string Nick)
{
// Fetch the user
var Rows = base.Query("SELECT * FROM accounts WHERE name=@P0", Nick);
return (Rows.Count == 0) ? null : Rows[0];
}
/// <summary>
/// Fetches an account from the gamespy database
/// </summary>
/// <param name="Pid">The account player ID</param>
/// <returns></returns>
public Dictionary<string, object> GetUser(int Pid)
{
// Fetch the user
var Rows = base.Query("SELECT * FROM accounts WHERE id=@P0", Pid);
return (Rows.Count == 0) ? null : Rows[0];
}
/// <summary>
/// Fetches an account from the gamespy database
/// </summary>
/// <param name="Email">Account email</param>
/// <param name="Password">Account Password</param>
/// <returns></returns>
public Dictionary<string, object> GetUser(string Email, string Password)
{
var Rows = base.Query("SELECT * FROM accounts WHERE email=@P0 AND password=@P1", Email, Password);
return (Rows.Count == 0) ? null : Rows[0];
}
/// <summary>
/// Returns a list of player names that are similar to the passed parameter
/// </summary>
/// <param name="Nick"></param>
/// <returns></returns>
public List<string> GetUsersLike(string Nick)
{
// Generate our return list
List<string> List = new List<string>();
var Rows = base.Query("SELECT name FROM accounts WHERE name LIKE @P0", "%" + Nick + "%");
foreach (Dictionary<string, object> Account in Rows)
List.Add(Account["name"].ToString());
return List;
}
/// <summary>
/// Returns wether an account exists from the provided Nick
/// </summary>
/// <param name="Nick"></param>
/// <returns></returns>
public bool UserExists(string Nick)
{
// Fetch the user
return (base.Query("SELECT id FROM accounts WHERE name=@P0", Nick).Count != 0);
}
/// <summary>
/// Returns wether an account exists from the provided Account Id
/// </summary>
/// <param name="PID"></param>
/// <returns></returns>
public bool UserExists(int PID)
{
// Fetch the user
return (base.Query("SELECT name FROM accounts WHERE id=@P0", PID).Count != 0);
}
/// <summary>
/// Creates a new Gamespy Account
/// </summary>
/// <remarks>Used by the login server when a create account request is made</remarks>
/// <param name="Nick">The Account Name</param>
/// <param name="Pass">The Account Password</param>
/// <param name="Email">The Account Email Address</param>
/// <param name="Country">The Country Code for this Account</param>
/// <returns>A bool indicating whether the account was created sucessfully</returns>
public bool CreateUser(string Nick, string Pass, string Email, string Country)
{
int Pid = 0;
// Attempt to connect to stats database, and get a PID from there
/*try
{
// try see if the player ID exists in the stats database
using (StatsDatabase Db = new StatsDatabase())
{
// NOTE: online account names in the stats DB start with a single space!
var Row = Db.Query("SELECT id FROM player WHERE upper(name) = upper(@P0)", " " + Nick);
Pid = (Row.Count == 0) ? GenerateAccountId() : Int32.Parse(Row[0]["id"].ToString());
}
}
catch
{*/
Pid = GenerateAccountId();
//}
// Create the user in the database
int Rows = base.Execute("INSERT INTO accounts(id, name, password, email, country) VALUES(@P0, @P1, @P2, @P3, @P4)",
Pid, Nick, Pass, Email, Country
);
return (Rows != 0);
}
/// <summary>
/// Generates a new Account Id
/// </summary>
/// <returns></returns>
private int GenerateAccountId()
{
var Row = base.Query("SELECT COALESCE(MAX(id), 500000000) AS max FROM accounts");
int max = Int32.Parse(Row[0]["max"].ToString()) + 1;
return (max < 500000000) ? 500000000 : max;
}
/// <summary>
/// Creates a new Gamespy Account
/// </summary>
/// <remarks>Only used in the Gamespy Account Creation Form</remarks>
/// <param name="Pid">The Profile Id to assign this account</param>
/// <param name="Nick">The Account Name</param>
/// <param name="Pass">The Account Password</param>
/// <param name="Email">The Account Email Address</param>
/// <param name="Country">The Country Code for this Account</param>
/// <returns>A bool indicating whether the account was created sucessfully</returns>
public bool CreateUser(int Pid, string Nick, string Pass, string Email, string Country)
{
// Make sure the user doesnt exist!
if (UserExists(Pid))
throw new Exception("Account ID is already taken!");
else if(UserExists(Nick))
throw new Exception("Account username is already taken!");
// Create the user in the database
int Rows = base.Execute("INSERT INTO accounts(id, name, password, email, country) VALUES(@P0, @P1, @P2, @P3, @P4)",
Pid, Nick, Pass, Email, Country
);
return (Rows != 0);
}
/// <summary>
/// Updates an Accounts Country Code
/// </summary>
/// <param name="Nick"></param>
/// <param name="Country"></param>
public void UpdateUser(string Nick, string Country)
{
base.Execute("UPDATE accounts SET country=@P0 WHERE name=@P1", Nick, Country);
}
/// <summary>
/// Updates an Account's information by ID
/// </summary>
/// <param name="Id">The Current Account ID</param>
/// <param name="NewPid">New Account ID</param>
/// <param name="NewNick">New Account Name</param>
/// <param name="NewPassword">New Account Password</param>
/// <param name="NewEmail">New Account Email Address</param>
public void UpdateUser(int Id, int NewPid, string NewNick, string NewPassword, string NewEmail)
{
base.Execute("UPDATE accounts SET id=@P0, name=@P1, password=@P2, email=@P3 WHERE id=@P4",
NewPid, NewNick, NewPassword, NewEmail, Id);
}
/// <summary>
/// Deletes a Gamespy Account
/// </summary>
/// <param name="Nick"></param>
/// <returns></returns>
public int DeleteUser(string Nick)
{
return base.Execute("DELETE FROM accounts WHERE name=@P0", Nick);
}
/// <summary>
/// Deletes a Gamespy Account
/// </summary>
/// <param name="Nick"></param>
/// <returns></returns>
public int DeleteUser(int Pid)
{
return base.Execute("DELETE FROM accounts WHERE id=@P0", Pid);
}
/// <summary>
/// Fetches a Gamespy Account id from an account name
/// </summary>
/// <param name="Nick"></param>
/// <returns></returns>
public int GetPID(string Nick)
{
var Rows = base.Query("SELECT id FROM accounts WHERE name=@P0", Nick);
return (Rows.Count == 0) ? 0 : Int32.Parse(Rows[0]["id"].ToString());
}
/// <summary>
/// Sets the Account (Player) Id for an account by Name
/// </summary>
/// <param name="Nick">The account Nick we are setting the new Pid for</param>
/// <param name="Pid">The new Pid</param>
/// <returns></returns>
public int SetPID(string Nick, int Pid)
{
// If no user exists, return code -1
if (!UserExists(Nick))
return -1;
// If the Pid already exists, return -2
if (UserExists(Pid))
return -2;
// If PID is false, the PID is not taken
int Success = base.Execute("UPDATE accounts SET id=@P0 WHERE name=@P1", Pid, Nick);
return (Success > 0) ? 1 : 0;
}
/// <summary>
/// Returns the number of accounts in the database
/// </summary>
/// <returns></returns>
public int GetNumAccounts()
{
var Row = base.Query("SELECT COUNT(id) AS count FROM accounts");
return Int32.Parse(Row[0]["count"].ToString());
}
}
}

28
gspylogin/Delegates.cs Normal file
View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BF2Statistics.Gamespy;
namespace BF2Statistics
{
public delegate void ShutdownEventHandler();
public delegate void StartupEventHandler();
public delegate void ConnectionUpdate(object sender);
public delegate void AspRequest();
public delegate void SnapshotProccessed();
public delegate void SnapshotRecieved(bool Proccessed);
public delegate void DataRecivedEvent(string Message);
public delegate void ConnectionClosed();
public delegate void GpspConnectionClosed(GpspClient client);
public delegate void GpcmConnectionClosed(GpcmClient client);
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics
{
public static class DateExtensions
{
/// <summary>
/// Returns the current Unix Timestamp
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public static int ToUnixTimestamp(this DateTime target)
{
return (int)(target - new DateTime(1970, 1, 1, 0, 0, 0, target.Kind)).TotalSeconds;
}
/// <summary>
/// Converts a timestamp to a DataTime
/// </summary>
/// <param name="target"></param>
/// <param name="timestamp"></param>
/// <returns></returns>
public static DateTime ToDateTime(this DateTime target, int timestamp)
{
DateTime Date = new DateTime(1970, 1, 1, 0, 0, 0, target.Kind);
return Date.AddSeconds(timestamp);
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics
{
public class DbConnectException : Exception
{
public DbConnectException(string Message, Exception Inner) : base(Message, Inner) { }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Net.Sockets
{
public static class SocketExtensions
{
/// <summary>
/// Determines if the connection to the machine on the other side of the socket is still active.
/// Serves the same purpose as TcpClient.Connected and TcpListener.Connected.
/// </summary>
/// <param name="iSocket"></param>
/// <returns>A bool of whether the remote client is still connected.</returns>
public static bool IsConnected(this Socket iSocket)
{
try
{
return !(iSocket.Poll(1, SelectMode.SelectRead) && iSocket.Available == 0);
}
catch
{
return false;
}
}
}
}

96
gspylogin/GamespyUtils.cs Normal file
View File

@ -0,0 +1,96 @@
using System;
using System.Text;
namespace BF2Statistics.Gamespy
{
public static class GamespyUtils
{
/// <summary>
/// Encodes a password to Gamespy format
/// </summary>
/// <param name="Password"></param>
/// <returns></returns>
public static string EncodePassword(string Password)
{
// Get password string as UTF8 String, Convert to Base64
byte[] PasswordBytes = Encoding.UTF8.GetBytes(Password);
string Pass = Convert.ToBase64String(GsPassEncode(PasswordBytes));
// Convert Standard Base64 to Gamespy Base 64
StringBuilder builder = new StringBuilder(Pass);
builder.Replace('=', '_');
builder.Replace('+', '[');
builder.Replace('/', ']');
return builder.ToString();
}
/// <summary>
/// Decodes a Gamespy encoded password
/// </summary>
/// <param name="Password"></param>
/// <returns></returns>
public static string DecodePassword(string Password)
{
// Convert Gamespy Base64 to Standard Base 64
StringBuilder builder = new StringBuilder(Password);
builder.Replace('_', '=');
builder.Replace('[', '+');
builder.Replace(']', '/');
// Decode passsword
byte[] PasswordBytes = Convert.FromBase64String(builder.ToString());
return Encoding.UTF8.GetString(GsPassEncode(PasswordBytes));
}
/// <summary>
/// Gamespy's XOR method to encrypt and decrypt a password
/// </summary>
/// <param name="pass"></param>
/// <returns></returns>
public static byte[] GsPassEncode(byte[] pass)
{
int a = 0;
int num = 0x79707367; // gspy
for (int i = 0; i < pass.Length; ++i)
{
num = Gslame(num);
a = num % 0xFF;
pass[i] ^= (byte)a;
}
return pass;
}
/// <summary>
/// Not exactly sure what this does, but i know its used to
/// reverse the encryption and decryption of a string
/// </summary>
/// <param name="num"></param>
/// <returns></returns>
private static int Gslame(int num)
{
int c = (num >> 16) & 0xffff;
int a = num & 0xffff;
c *= 0x41a7;
a *= 0x41a7;
a += ((c & 0x7fff) << 16);
if (a < 0)
{
a &= 0x7fffffff;
a++;
}
a += (c >> 15);
if (a < 0)
{
a &= 0x7fffffff;
a++;
}
return a;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics.Gamespy
{
class ClientList : EventArgs
{
public List<GpcmClient> Clients;
public ClientList(List<GpcmClient> Clients)
{
this.Clients = Clients;
}
}
}

View File

@ -0,0 +1,541 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Security.Cryptography;
using BF2Statistics.Utilities;
namespace BF2Statistics.Gamespy
{
/// <summary>
/// Gamespy Client Manager
/// This class is used to proccess the client login process,
/// create new user accounts, and fetch profile information
/// <remarks>gpcm.gamespy.com</remarks>
/// </summary>
public class GpcmClient : IDisposable
{
#region Variables
/// <summary>
/// The current step of the login proccess
/// </summary>
private int Step = 0;
/// <summary>
/// The connected clients Nick
/// </summary>
public string ClientNick { get; protected set; }
/// <summary>
/// The connected clients Player Id
/// </summary>
public int ClientPID { get; protected set; }
/// <summary>
/// Clients table data from the gamespy database
/// </summary>
private Dictionary<string, object> User;
/// <summary>
/// The users session key
/// </summary>
private ushort SessionKey;
/// <summary>
/// The Clients generated proof string, used for checking password validity.
/// This is used as part of the hash used to "prove" to the client
/// that the password in our database matches what the user enters
/// </summary>
private string ClientChallengeKey;
/// <summary>
/// The Servers challange key, sent when the client first connects.
/// This is used as part of the hash used to "proove" to the client
/// that the password in our database matches what the user enters
/// </summary>
private string ServerChallengeKey;
/// <summary>
/// The Clients response key. The is the expected Hash value for the server
/// when generating the client proof string
/// </summary>
private string ClientResponseKey;
/// <summary>
/// Variable that determines if the client is disconnected,
/// and this object can be cleared from memory
/// </summary>
public bool Disposed { get; protected set; }
/// <summary>
/// The clients socket network stream
/// </summary>
private TcpClientStream Stream;
/// <summary>
/// The TcpClient for our connection
/// </summary>
private TcpClient Connection;
/// <summary>
/// The TcpClient's Endpoint
/// </summary>
private EndPoint ClientEP;
/// <summary>
/// The Connected Clients IpAddress
/// </summary>
public IPAddress IpAddress = null;
/// <summary>
/// A random... random
/// </summary>
private Random RandInstance = new Random((int)DateTime.Now.Ticks);
/// <summary>
/// Our CRC16 object for generating Checksums
/// </summary>
protected static Crc16 Crc;
/// <summary>
/// Our MD5 Object
/// </summary>
protected static MD5 CreateMD5;
/// <summary>
/// A literal backslash
/// </summary>
private const char Backslash = '\\';
/// <summary>
/// Array of characters used in generating a signiture
/// </summary>
private static char[] AlphaChars = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
/// <summary>
/// An array of Alpha Numeric characters used in generating a random string
/// </summary>
private static char[] AlphaNumChars = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
/// <summary>
/// Array of Hex cahracters, with no leading 0
/// </summary>
private static char[] HexChars = {
'1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'
};
/// <summary>
/// Gamespy password hash table (Byte To Hex)
/// </summary>
private static string[] BtoH = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
};
/// <summary>
/// An Event that is fired when the client successfully logs in.
/// </summary>
public static event ConnectionUpdate OnSuccessfulLogin;
/// <summary>
/// Event fired when that remote connection logs out, or
/// the socket gets disconnected. This event will not fire
/// unless OnSuccessfulLogin event was fired first.
/// </summary>
public static event GpcmConnectionClosed OnDisconnect;
#endregion Variables
/// <summary>
/// Static Construct. Builds the Crc table uesd in the login process
/// </summary>
static GpcmClient()
{
Crc = new Crc16(Crc16Mode.Standard);
CreateMD5 = MD5.Create();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="Client">The Tcp Client connection</param>
public GpcmClient(TcpClient Client)
{
// Set default variable values
ClientNick = "Connecting...";
Connection = Client;
ClientEP = Connection.Client.RemoteEndPoint;
IpAddress = ((IPEndPoint)ClientEP).Address;
Disposed = false;
// Create our Client Stream
Stream = new TcpClientStream(Client);
Stream.DataReceived += new DataRecivedEvent(Stream_DataReceived);
Stream.OnDisconnect += new ConnectionClosed(Stream_OnDisconnect);
// Log Incoming Connections
LoginServer.Log("[GPCM] Client Connected: {0}", ClientEP);
// Start by sending the server challenge
SendServerChallenge();
}
/// <summary>
/// Destructor
/// </summary>
~GpcmClient()
{
if (!Disposed)
this.Dispose();
}
/// <summary>
/// Closes the connection, and disposes of the client object
/// </summary>
public void Dispose()
{
// If connection is still alive, disconnect user
if (Connection.Client.IsConnected())
{
Stream.IsClosing = true;
Connection.Close();
}
// Call disconnect event
if(OnDisconnect != null)
OnDisconnect(this);
// Preapare to be unloaded from memory
this.Disposed = true;
// Log
LoginServer.Log("[GPCM] Client Disconnected: {0}", ClientEP);
}
/// <summary>
/// Logs the client out of the game client, and closes the stream
/// </summary>
public void LogOut()
{
LoginServer.Database.Execute("UPDATE accounts SET session=0 WHERE id=" + ClientPID);
Dispose();
}
#region Stream Callbacks
/// <summary>
/// Main listner loop. Keeps an open stream between the client and server while
/// the client is logged in / playing
/// </summary>
private void Stream_DataReceived(string message)
{
// Read client message, and parse it into key value pairs
string[] recieved = message.TrimStart(Backslash).Split(Backslash);
Dictionary<string, string> Recv = ConvertToKeyValue(recieved);
// Switch by task
switch (recieved[0])
{
case "newuser":
HandleNewUser(Recv);
Step++;
break;
case "login":
ProcessLogin(Recv);
Step++;
break;
case "getprofile":
if (Step < 2)
{
SendProfile(false);
Step++;
}
else
SendProfile(true);
break;
case "updatepro":
UpdateUser(Recv);
Step++;
break;
case "logout":
LogOut();
break;
default:
LoginServer.Log("Unkown Message Passed: {0}", message);
break;
}
}
/// <summary>
/// Main loop for handling the client stream
/// </summary>
private void Stream_OnDisconnect()
{
LogOut();
}
#endregion Stream Callbacks
#region Login Steps
/// <summary>
/// This method starts off by sending a random string 10 characters
/// in length, known as the Server challenge key. This is used by
/// the client to return a client challenge key, which is used
/// to validate login information later.
/// </summary>
public void SendServerChallenge()
{
// First we need to create a random string the length of 10 characters
StringBuilder Temp = new StringBuilder();
for (int i = 0; i < 10; i++)
Temp.Append(AlphaChars[RandInstance.Next(AlphaChars.Length)]);
// Next we send the client the challenge key
ServerChallengeKey = Temp.ToString();
Stream.Send("\\lc\\1\\challenge\\{0}\\id\\1\\final\\", ServerChallengeKey);
}
/// <summary>
/// This method verifies the login information sent by
/// the client, and returns encrypted data for the client
/// to verify as well
/// </summary>
public void ProcessLogin(Dictionary<string, string> Recv)
{
// Set instance variables now that we know who's connected
ClientNick = Recv["uniquenick"];
ClientChallengeKey = Recv["challenge"];
ClientResponseKey = Recv["response"];
// Get user data from database
try
{
User = LoginServer.Database.GetUser(ClientNick);
if (User == null)
{
Stream.Send("\\error\\\\err\\265\\fatal\\\\errmsg\\The uniquenick provided is incorrect!\\id\\1\\final\\");
Dispose();
return;
}
}
catch
{
Dispose();
return;
}
// Set client PID var
ClientPID = Int32.Parse(User["id"].ToString());
// Use the GenerateProof method to compare with the "response" value. This validates the given password
if (ClientResponseKey == GenerateProof(ClientChallengeKey, ServerChallengeKey))
{
// Create session key
SessionKey = Crc.ComputeChecksum(ClientNick);
// Password is correct
Stream.Send(
"\\lc\\2\\sesskey\\{0}\\proof\\{1}\\userid\\{2}\\profileid\\{2}\\uniquenick\\{3}\\lt\\{4}__\\id\\1\\final\\",
SessionKey,
GenerateProof(ServerChallengeKey, ClientChallengeKey),
ClientPID,
ClientNick,
GenerateRandomString(22) // Generate LT whatever that is (some sort of random string, 22 chars long)
);
// Call successful login event
OnSuccessfulLogin(this);
LoginServer.Database.Execute("UPDATE accounts SET session=@P0, lastip=@P1 WHERE id=@P2", SessionKey, IpAddress, ClientPID);
}
else
{
// Password is incorrect with database value
Stream.Send("\\error\\\\err\\260\\fatal\\\\errmsg\\The password provided is incorrect.\\id\\1\\final\\");
Dispose();
}
}
/// <summary>
/// This method is called when the client requests for the Account profile
/// </summary>
/// <param name="retrieve">Determines the return ID</param>
private void SendProfile(bool retrieve)
{
Stream.Send(
"\\pi\\\\profileid\\{0}\\nick\\{1}\\userid\\{0}\\email\\{2}\\sig\\{3}\\uniquenick\\{1}\\pid\\0\\firstname\\\\lastname\\" +
"\\countrycode\\{4}\\birthday\\16844722\\lon\\0.000000\\lat\\0.000000\\loc\\\\id\\{5}\\final\\",
User["id"], ClientNick, User["email"], GenerateSig(), User["country"], (retrieve ? "5" : "2")
);
}
#endregion Steps
#region User Methods
/// <summary>
/// Whenever the "newuser" command is recieved, this method is called to
/// add the new users information into the database
/// </summary>
/// <param name="Recv">Array of parms sent by the server</param>
private void HandleNewUser(Dictionary<string, string> Recv)
{
string Nick = Recv["nick"];
string Email = Recv["email"];
// Make sure the user doesnt exist already
try
{
if (LoginServer.Database.UserExists(Nick))
{
Stream.Send("\\error\\\\err\\516\\fatal\\\\errmsg\\This account name is already in use!\\id\\1\\final\\");
Dispose();
return;
}
}
catch
{
Dispose();
return;
}
// We need to decode the Gamespy specific encoding for the password
string Password = GamespyUtils.DecodePassword(Recv["passwordenc"]);
bool result = LoginServer.Database.CreateUser(Nick, Password, Email, "US");
User = LoginServer.Database.GetUser(Nick);
// Fetch the user to make sure we are good
if (!result || User == null)
{
Stream.Send("\\error\\\\err\\516\\fatal\\\\errmsg\\Error creating account!\\id\\1\\final\\");
Dispose();
return;
}
Stream.Send("\\nur\\\\userid\\{0}\\profileid\\{0}\\id\\1\\final\\", User["id"]);
}
/// <summary>
/// Updates the Users Country code when sent by the client
/// </summary>
/// <param name="recv">Array of information sent by the server</param>
private void UpdateUser(Dictionary<string, string> Recv)
{
// Set clients country code
try {
LoginServer.Database.UpdateUser(ClientNick, Recv["countrycode"]);
}
catch {
Dispose();
}
}
#endregion
#region Misc Methods
/// <summary>
/// Converts a recived parameter array from the client string to a keyValue pair dictionary
/// </summary>
/// <param name="parts">The array of data from the client</param>
/// <returns></returns>
private Dictionary<string, string> ConvertToKeyValue(string[] parts)
{
Dictionary<string, string> Dic = new Dictionary<string, string>();
for (int i = 2; i < parts.Length; i += 2)
Dic.Add(parts[i], parts[i + 1]);
return Dic;
}
/// <summary>
/// Generates an encrypted reponse to return to the client, which verifies
/// the clients account information, and login info
/// </summary>
/// <param name="challenge1">First challenge key</param>
/// <param name="challenge2">Second challenge key</param>
/// <returns>Encrypted account info / Login verification</returns>
private string GenerateProof(string challenge1, string challenge2)
{
// Prepare variables
StringBuilder Response = new StringBuilder();
StringBuilder PassHash = new StringBuilder();
byte[] Md5Hash;
// Convert MD5 password bytes to hex characters
Md5Hash = CreateMD5.ComputeHash(Encoding.ASCII.GetBytes(User["password"].ToString()));
foreach (byte b in Md5Hash)
PassHash.Append(BtoH[b]);
// Generate our string to be hashed
string pHash = PassHash.ToString();
PassHash.Append(' ', 48); // 48 spaces
PassHash.Append(String.Concat(ClientNick, challenge1, challenge2, pHash));
// Create our final MD5 hash, and convert all the bytes to hex yet again
Md5Hash = CreateMD5.ComputeHash(Encoding.ASCII.GetBytes(PassHash.ToString()));
foreach (byte b in Md5Hash)
Response.Append(BtoH[b]);
return Response.ToString();
}
/// <summary>
/// Generates a random alpha-numeric string
/// </summary>
/// <param name="length">The lenght of the string to be generated</param>
/// <returns></returns>
private string GenerateRandomString(int length)
{
StringBuilder Response = new StringBuilder();
for(int i = 0; i < length; i++)
Response.Append(AlphaNumChars[RandInstance.Next(62)]);
return Response.ToString();
}
/// <summary>
/// Generates a random signature
/// </summary>
/// <returns></returns>
private string GenerateSig()
{
StringBuilder Response = new StringBuilder();
for (int length = 0; length < 32; length++)
Response.Append(HexChars[RandInstance.Next(14)]);
return Response.ToString();
}
#endregion
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace BF2Statistics.Gamespy
{
class GpcmServer
{
/// <summary>
/// Our GPSP Server Listener Socket
/// </summary>
private static TcpListener Listener;
/// <summary>
/// List of connected clients
/// </summary>
private static List<GpcmClient> Clients = new List<GpcmClient>();
/// <summary>
/// An event called everytime a client connects, or disconnects from the server
/// </summary>
public static event EventHandler OnClientsUpdate;
public GpcmServer()
{
// Attempt to bind to port 29900
Listener = new TcpListener(IPAddress.Any, 29900);
Listener.Start();
// Create a new thread to accept the connection
Listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
// Enlist for events
GpcmClient.OnSuccessfulLogin += new ConnectionUpdate(GpcmClient_OnSuccessfulLogin);
GpcmClient.OnDisconnect += new GpcmConnectionClosed(GpcmClient_OnDisconnect);
}
/// <summary>
/// Shutsdown the GPSP server and socket
/// </summary>
public void Shutdown()
{
// Stop updating client checks
Listener.Stop();
// Unregister events so we dont get a shit ton of calls
GpcmClient.OnDisconnect -= new GpcmConnectionClosed(GpcmClient_OnDisconnect);
// Disconnected all connected clients
foreach (GpcmClient C in Clients)
C.Dispose(); // Donot call logout here!
// Update Connected Clients in the Database
LoginServer.Database.Execute("UPDATE accounts SET session=0");
Clients.Clear();
}
/// <summary>
/// Returns the number of connected clients
/// </summary>
/// <returns></returns>
public int NumClients()
{
return Clients.Count;
}
/// <summary>
/// Forces the logout of a connected client
/// </summary>
/// <param name="Pid">The account ID</param>
/// <returns></returns>
public bool ForceLogout(int Pid)
{
foreach (GpcmClient C in Clients)
{
if (C.ClientPID == Pid)
{
C.LogOut();
return true;
}
}
return false;
}
/// <summary>
/// Accepts a TcpClient
/// </summary>
/// <param name="ar"></param>
private void AcceptClient(IAsyncResult ar)
{
// End the operation and display the received data on
// the console.
try
{
// Hurry up and get ready to accept another client
TcpClient Client = Listener.EndAcceptTcpClient(ar);
Listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
// Convert the TcpClient to a GpcmClient, which will handle the client login info
Clients.Add(new GpcmClient(Client));
}
catch { }
}
/// <summary>
/// Callback for a successful login
/// </summary>
/// <param name="sender"></param>
private void GpcmClient_OnSuccessfulLogin(object sender)
{
OnClientsUpdate(this, new ClientList(Clients));
}
/// <summary>
/// Callback for when a connection had disconnected
/// </summary>
/// <param name="client">The client object whom is disconnecting</param>
private void GpcmClient_OnDisconnect(GpcmClient client)
{
// Remove client, and call OnUpdate Event
Clients.Remove(client);
OnClientsUpdate(this, new ClientList(Clients));
}
}
}

View File

@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace BF2Statistics.Gamespy
{
public class GpspClient : IDisposable
{
/// <summary>
/// Indicates whether this object is disposed
/// </summary>
public bool Disposed { get; protected set; }
/// <summary>
/// Connection TcpClient Stream
/// </summary>
private TcpClientStream Stream;
/// <summary>
/// The Tcp Client
/// </summary>
private TcpClient Client;
/// <summary>
/// The TcpClient's Endpoint
/// </summary>
private EndPoint ClientEP;
/// <summary>
/// Event fired when the connection is closed
/// </summary>
public static event GpspConnectionClosed OnDisconnect;
/// <summary>
/// Constructor
/// </summary>
/// <param name="client"></param>
public GpspClient(TcpClient client)
{
// Set disposed to false!
this.Disposed = false;
// Set the client variable
this.Client = client;
this.ClientEP = client.Client.RemoteEndPoint;
LoginServer.Log("[GPSP] Client Connected: {0}", ClientEP);
// Init a new client stream class
Stream = new TcpClientStream(client);
Stream.DataReceived += new DataRecivedEvent(Stream_DataReceived);
Stream.OnDisconnect += new ConnectionClosed(Stream_OnDisconnect);
}
/// <summary>
/// Destructor
/// </summary>
~GpspClient()
{
if(!Disposed)
this.Dispose();
}
/// <summary>
/// Dispose method to be called by the server
/// </summary>
public void Dispose()
{
// If connection is still alive, disconnect user
if (Client.Client.IsConnected())
{
Stream.IsClosing = true;
Client.Close();
}
// Call disconnect event
if (OnDisconnect != null)
OnDisconnect(this);
// Preapare to be unloaded from memory
this.Disposed = true;
// Log
LoginServer.Log("[GPSP] Client Disconnected: {0}", ClientEP);
}
/// <summary>
/// ECallback for when when the client stream is disconnected
/// </summary>
protected void Stream_OnDisconnect()
{
Dispose();
}
/// <summary>
/// Callback for when a message has been recieved by the connected client
/// </summary>
public void Stream_DataReceived(string message)
{
// Parse input message
string[] recv = message.Split('\\');
if (recv.Length == 1)
return;
switch (recv[1])
{
case "nicks":
SendGPSP(recv);
break;
case "check":
SendCheck(recv);
break;
}
}
/// <summary>
/// This method is requested by the client whenever an accounts existance needs validated
/// </summary>
/// <param name="recv"></param>
private void SendGPSP(string[] recv)
{
// Try to get user data from database
Dictionary<string, object> ClientData;
try
{
ClientData = LoginServer.Database.GetUser(GetParameterValue(recv, "email"), GetParameterValue(recv, "pass"));
if (ClientData == null)
{
Stream.Send("\\nr\\0\\ndone\\\\final\\");
return;
}
}
catch
{
Dispose();
return;
}
Stream.Send("\\nr\\1\\nick\\{0}\\uniquenick\\{0}\\ndone\\\\final\\", ClientData["name"]);
}
/// <summary>
/// This is the primary method for fetching an accounts BF2 PID
/// </summary>
/// <param name="recv"></param>
private void SendCheck(string[] recv)
{
try {
Stream.Send("\\cur\\0\\pid\\{0}\\final\\", LoginServer.Database.GetPID(GetParameterValue(recv, "nick")));
}
catch
{
Dispose();
return;
}
}
/// <summary>
/// A simple method of getting the value of the passed parameter key,
/// from the returned array of data from the client
/// </summary>
/// <param name="parts">The array of data from the client</param>
/// <param name="parameter">The parameter</param>
/// <returns>The value of the paramenter key</returns>
private string GetParameterValue(string[] parts, string parameter)
{
bool next = false;
foreach (string part in parts)
{
if (next)
return part;
else if (part == parameter)
next = true;
}
return "";
}
}
}

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace BF2Statistics.Gamespy
{
class GpspServer
{
/// <summary>
/// Our GPSP Server Listener Socket
/// </summary>
private TcpListener Listener;
/// <summary>
/// List of connected clients
/// </summary>
public List<GpspClient> Clients = new List<GpspClient>();
public GpspServer()
{
// Attempt to bind to port 29901
Listener = new TcpListener(IPAddress.Any, 29901);
Listener.Start();
// Register for disconnect
GpspClient.OnDisconnect += new GpspConnectionClosed(GpspClient_OnDisconnect);
// Create a new thread to accept the connection
Listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
}
/// <summary>
/// Shutsdown the GPSP server and socket
/// </summary>
public void Shutdown()
{
// Stop updating client checks
Listener.Stop();
// Unregister events so we dont get a shit ton of calls
GpspClient.OnDisconnect -= new GpspConnectionClosed(GpspClient_OnDisconnect);
// Disconnected all connected clients
foreach (GpspClient C in Clients)
C.Dispose();
// clear clients
Clients.Clear();
}
/// <summary>
/// Accepts a TcpClient
/// </summary>
/// <param name="ar"></param>
private void AcceptClient(IAsyncResult ar)
{
// End the operation and display the received data on
// the console.
try
{
TcpClient Client = Listener.EndAcceptTcpClient(ar);
Listener.BeginAcceptTcpClient(new AsyncCallback(AcceptClient), null);
Clients.Add(new GpspClient(Client));
}
catch { }
}
/// <summary>
/// Callback for when a connection had disconnected
/// </summary>
/// <param name="sender">The client object whom is disconnecting</param>
private void GpspClient_OnDisconnect(GpspClient client)
{
Clients.Remove(client);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace BF2Statistics.Logging
{
class LogMessage
{
public string Message;
public int LogTimestamp { get; protected set; }
public DateTime LogTime { get; protected set; }
public LogMessage(string Message)
{
this.Message = Message;
this.LogTimestamp = DateTime.UtcNow.ToUnixTimestamp();
this.LogTime = DateTime.Now;
}
public LogMessage(string Message, params object[] Items)
{
this.Message = String.Format(Message, Items);
this.LogTimestamp = DateTime.UtcNow.ToUnixTimestamp();
this.LogTime = DateTime.Now;
}
}
}

View File

@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Timers;
namespace BF2Statistics.Logging
{
public class LogWritter
{
/// <summary>
/// Our Queue of log messages to be written
/// </summary>
private Queue<LogMessage> LogQueue;
/// <summary>
/// Full path to the log file
/// </summary>
private FileInfo LogFile;
/// <summary>
/// Our Timer object for writing to the log file
/// </summary>
private Timer LogTimer;
/// <summary>
/// Creates a new Log Writter, Appending all messages to a logfile
/// </summary>
/// <param name="FileLocation">The location of the logfile. If the file doesnt exist,
/// It will be created.</param>
public LogWritter(string FileLocation) : this(FileLocation, false) { }
/// <summary>
/// Creates a new Log Writter instance
/// </summary>
/// <param name="FileLocation">The location of the logfile. If the file doesnt exist,
/// It will be created.</param>
/// <param name="Truncate">If set to true and the logfile is over 1MB, it will be truncated to 0 length</param>
public LogWritter(string FileLocation, bool Truncate)
{
this.createFolder(FileLocation);
LogFile = new FileInfo(FileLocation);
LogQueue = new Queue<LogMessage>();
// Test that we are able to open and write to the file
using (FileStream stream = LogFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
// If the file is over 1MB, and we want to truncate big files
if (Truncate && LogFile.Length > 1048576)
{
stream.SetLength(0);
stream.Flush();
}
}
// Start a log timer, and auto write new logs every 3 seconds
LogTimer = new Timer(3000);
LogTimer.Elapsed += new ElapsedEventHandler(LogTimer_Elapsed);
LogTimer.Start();
}
private void createFolder(string FileLocation)
{
if (!File.Exists(FileLocation))
{
string folder = Path.GetDirectoryName(Path.GetFullPath(FileLocation));
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
}
}
/// <summary>
/// Event Fired every itnerval, which flushes the log
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LogTimer_Elapsed(object sender, ElapsedEventArgs e)
{
FlushLog();
}
/// <summary>
/// Adds a message to the queue, to be written to the log file
/// </summary>
/// <param name="message">The message to write to the log</param>
public void Write(string message)
{
// Lock the queue while writing to prevent contention for the log file
LogMessage logEntry = new LogMessage(message);
lock (LogQueue)
{
// Push to the Queue
LogQueue.Enqueue(logEntry);
}
}
/// <summary>
/// Adds a message to the queue, to be written to the log file
/// </summary>
/// <param name="message">The message to write to the log</param>
public void Write(string message, params object[] items)
{
Write(String.Format(message, items));
}
/// <summary>
/// Flushes the Queue to the physical log file
/// </summary>
private void FlushLog()
{
// Only log if we have a queue
if (LogQueue.Count > 0)
{
using (FileStream fs = LogFile.Open(FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter log = new StreamWriter(fs))
{
while (LogQueue.Count > 0)
{
LogMessage entry = LogQueue.Dequeue();
String message = String.Format("[{0}]\t{1}", entry.LogTime, entry.Message);
Console.WriteLine(message);
log.WriteLine(message);
}
}
}
}
/// <summary>
/// Destructor. Make sure we flush!
/// </summary>
~LogWritter()
{
LogTimer.Stop();
LogTimer.Dispose();
FlushLog();
}
}
}

164
gspylogin/LoginServer.cs Normal file
View File

@ -0,0 +1,164 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
//using System.Windows.Forms;
using BF2Statistics.Database;
using BF2Statistics.Logging;
namespace BF2Statistics.Gamespy
{
/// <summary>
/// The Login Server is used to emulate the Official Gamespy Login servers,
/// and provide players the ability to create fake "Online" accounts.
/// </summary>
public class LoginServer
{
/// <summary>
/// Returns whether the login server is running or not
/// </summary>
protected static bool isRunning = false;
/// <summary>
/// Returns whether the login server is running or not
/// </summary>
public static bool IsRunning
{
get { return isRunning; }
}
/// <summary>
/// Gamespy GpCm Server Object
/// </summary>
private static GpcmServer CmServer;
/// <summary>
/// The Gamespy GpSp Server Object
/// </summary>
private static GpspServer SpServer;
/// <summary>
/// The Gamespy Database Object
/// </summary>
public static GamespyDatabase Database;
/// <summary>
/// The Login Server Log Writter
/// </summary>
private static LogWritter Logger;
/// <summary>
/// Event that is fired when the login server is started
/// </summary>
public static event StartupEventHandler OnStart;
/// <summary>
/// Event that is fired when the login server is shutdown
/// </summary>
public static event ShutdownEventHandler OnShutdown;
/// <summary>
/// Event fires to update the client list
/// </summary>
public static event EventHandler OnUpdate;
static LoginServer()
{
// Create our log file, and register for events
Logger = new LogWritter(Path.Combine(Program.RootPath, "Logs", "LoginServer.log"), true);
GpcmServer.OnClientsUpdate += new EventHandler(CmServer_OnUpdate);
}
/// <summary>
/// Starts the Login Server listeners, and begins accepting new connections
/// </summary>
public static void Start()
{
// Make sure we arent already running!
if (isRunning) return;
// Start the DB Connection
Database = new GamespyDatabase();
// Bind gpcm server on port 29900
int port = 29900;
try
{
CmServer = new GpcmServer();
port++;
SpServer = new GpspServer();
}
catch (Exception E)
{
Notify.Show(
"Failed to Start Login Server!",
"Error binding to port " + port + ": " + Environment.NewLine + E.Message,
AlertType.Warning
);
Database.Dispose();
throw;
}
// Let the client know we are ready for connections
isRunning = true;
OnStart();
}
/// <summary>
/// Event fired when an account logs in or out
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void CmServer_OnUpdate(object sender, EventArgs e)
{
OnUpdate(sender, e);
}
/// <summary>
/// Shutsdown the Login Server listeners and stops accepting new connections
/// </summary>
public static void Shutdown()
{
// Shutdown Login Servers
CmServer.Shutdown();
SpServer.Shutdown();
// Close the database connection
Database.Dispose();
// Trigger the OnShutdown Event
OnShutdown();
// Update status
isRunning = false;
}
/// <summary>
/// Forces the logout of a connected client
/// </summary>
/// <param name="Pid"></param>
/// <returns></returns>
public static bool ForceLogout(int Pid)
{
return (IsRunning) ? CmServer.ForceLogout(Pid) : false;
}
/// <summary>
/// This method is used to store a message in the console.log file
/// </summary>
/// <param name="message">The message to be written to the log file</param>
public static void Log(string message)
{
Logger.Write(message);
}
/// <summary>
/// This method is used to store a message in the console.log file
/// </summary>
/// <param name="message">The message to be written to the log file</param>
public static void Log(string message, params object[] items)
{
Logger.Write(String.Format(message, items));
}
}
}

51
gspylogin/MainForm.cs Normal file
View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BF2Statistics
{
class MainForm
{
//public static Settings Config = Settings.Default;
public static class SysIcon
{
public static void ShowBalloonTip(int time, string mess, string text, object icon) {
Console.WriteLine(time + " " + mess + " " + text);
}
}
public static class Config
{
private static BlubbFish.Utils.InIReader i = BlubbFish.Utils.InIReader.getInstance("settings.ini");
public static string GamespyDBEngine
{
get { return i.getValue("general", "GamespyDBEngine"); }
}
public static string GamespyDBHost
{
get { return i.getValue("general", "GamespyDBHost"); }
}
public static int GamespyDBPort
{
get { return Int32.Parse(i.getValue("general", "GamespyDBPort")); }
}
public static string GamespyDBName
{
get { return i.getValue("general", "GamespyDBName"); }
}
public static string GamespyDBUser
{
get { return i.getValue("general", "GamespyDBUser"); }
}
public static string GamespyDBPass
{
get { return i.getValue("general", "GamespyDBPass"); }
}
public static bool DebugStream
{
get { return Boolean.Parse(i.getValue("general", "DebugStream")); }
}
}
}
}

67
gspylogin/Program.cs Normal file
View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BF2Statistics.Gamespy;
namespace BF2Statistics
{
static class Program
{
private static int PeakClients = 0;
public static readonly string RootPath = "";//Application.StartupPath;
static void Main(string[] args)
{
LoginServer.OnStart += new StartupEventHandler(LoginServer_OnStart);
LoginServer.OnShutdown += new ShutdownEventHandler(LoginServer_OnShutdown);
LoginServer.OnUpdate += new EventHandler(LoginServer_OnUpdate);
LoginServer.Start();
System.Threading.Thread t = new System.Threading.Thread(Input);
while (LoginServer.IsRunning)
{
System.Threading.Thread.Sleep(100);
if ((t.ThreadState == System.Threading.ThreadState.Stopped ||
t.ThreadState == System.Threading.ThreadState.Unstarted) && LoginServer.IsRunning)
{
t = new System.Threading.Thread(Input);
t.Start();
}
}
}
private static void Input()
{
string inp = Console.ReadLine().Replace("\r","").Replace("\n","").Trim();
if (inp.ToLower() == "exit")
{
LoginServer.Shutdown();
}
}
static void LoginServer_OnShutdown()
{
Console.WriteLine("Server Stopped!");
}
static void LoginServer_OnUpdate(object sender, EventArgs e)
{
// DO processing in this thread
StringBuilder SB = new StringBuilder();
List<GpcmClient> Clients = ((ClientList)e).Clients;
if (PeakClients < Clients.Count)
PeakClients = Clients.Count;
foreach (GpcmClient C in Clients)
SB.AppendFormat(" {0} ({1}) - {2}{3}", C.ClientNick, C.ClientPID, C.IpAddress, Environment.NewLine);
Console.WriteLine("Clients: " + Clients.Count + " Peak: " + PeakClients);
Console.WriteLine(SB.ToString());
}
static void LoginServer_OnStart()
{
Console.WriteLine("Server Started!");
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("gspylogin")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("gspylogin")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("8afa9350-e7b2-42c2-95e8-b382004a746c")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,170 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using BF2Statistics.Logging;
namespace BF2Statistics.Gamespy
{
class TcpClientStream
{
/// <summary>
/// The current clients stream
/// </summary>
private TcpClient Client;
/// <summary>
/// Clients NetworkStream
/// </summary>
private NetworkStream Stream;
/// <summary>
/// Write all data sent/recieved to the stream log?
/// </summary>
private bool Debugging;
/// <summary>
/// If set to true, we will not contiue listening anymore
/// </summary>
public bool IsClosing = false;
/// <summary>
/// StreamLog Object
/// </summary>
private static LogWritter StreamLog = new LogWritter(Path.Combine(Program.RootPath, "Logs", "Stream.log"));
/// <summary>
/// Our message buffer
/// </summary>
private byte[] buffer = new byte[2048];
/// <summary>
/// Our remote message from the buffer, converted to a string
/// </summary>
private StringBuilder Message = new StringBuilder();
/// <summary>
/// Event fired when a completed message has been recieved
/// </summary>
public event DataRecivedEvent DataReceived;
/// <summary>
/// Event fire when the remote connection is closed
/// </summary>
public event ConnectionClosed OnDisconnect;
/// <summary>
/// Constructor
/// </summary>
/// <param name="client"></param>
public TcpClientStream(TcpClient client)
{
this.Client = client;
this.Stream = client.GetStream();
this.Debugging = MainForm.Config.DebugStream;
Stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), Stream);
}
/// <summary>
/// Callback for BeginRead. This method handles the message parsing
/// </summary>
/// <param name="ar"></param>
private void ReadCallback(IAsyncResult ar)
{
// End the Async Read
int bytesRead = 0;
try
{
bytesRead = Stream.EndRead(ar);
}
catch (IOException e)
{
// If we got an IOException, client connection is lost
if (Client.Client.IsConnected())
Log("ERROR: IOException Thrown during read: " + e.Message);
}
catch (ObjectDisposedException) { } // Fired when a the login server is shutown
// Force disconnect (Specifically for Gpsp, whom will spam null bytes)
if (bytesRead == 0)
{
OnDisconnect(); // Parent is responsible for closing the connection
return;
}
// Add message to buffer
Message.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
// If we have no more data, then the message is complete
if (!Stream.DataAvailable)
{
// Debugging
if (Debugging)
Log("Port {0} Recieves: {1}", ((IPEndPoint)Client.Client.LocalEndPoint).Port, Message.ToString());
// tell our parent that we recieved a message
DataReceived(Message.ToString());
Message = new StringBuilder();
}
// Begin a new Read
if (!IsClosing)
Stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), Stream);
}
/// <summary>
/// Writes a message to the client stream
/// </summary>
/// <param name="message">The complete message to be sent to the client</param>
public void Send(string message)
{
if (Debugging)
Log("Port {0} Sends: {1}", ((IPEndPoint)Client.Client.LocalEndPoint).Port, message);
this.Send(Encoding.ASCII.GetBytes(message));
}
/// <summary>
/// Writes a message to the client stream
/// </summary>
/// <param name="message">The complete message to be sent to the client</param>
public void Send(string message, params object[] items)
{
message = String.Format(message, items);
if (Debugging)
Log("Port {0} Sends: {1}", ((IPEndPoint)Client.Client.LocalEndPoint).Port, message);
this.Send(Encoding.ASCII.GetBytes(message));
}
/// <summary>
/// Writes a message to the client stream
/// </summary>
/// <param name="bytes">An array of bytes to send to the stream</param>
public void Send(byte[] bytes)
{
Stream.Write(bytes, 0, bytes.Length);
}
/// <summary>
/// Writes a message to the stream log
/// </summary>
/// <param name="message"></param>
private static void Log(string message)
{
StreamLog.Write(message);
}
/// <summary>
/// Writes a message to the stream log
/// </summary>
/// <param name="message"></param>
private static void Log(string message, params object[] items)
{
StreamLog.Write(String.Format(message, items));
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BF2Statistics.Utilities
{
public enum Crc16Mode : ushort
{
Standard = 0xA001,
CCITT = 4129,
CCITTKermit = 0x8408
}
/// <summary>
/// Credits go to http://www.sanity-free.com/134/standard_crc_16_in_csharp.html
/// </summary>
public class Crc16
{
/// <summary>
/// The Crc16 Table
/// </summary>
public ushort[] CrcTable
{
get;
protected set;
}
public Crc16()
{
// Build Standard Crc16 Table
BuildCrcTable(0xA001);
}
public Crc16(ushort polynomial)
{
BuildCrcTable(polynomial);
}
public Crc16(Crc16Mode Mode)
{
BuildCrcTable((ushort)Mode);
}
/// <summary>
/// Calculates the Checksum for the input string
/// </summary>
/// <param name="Input"></param>
/// <returns></returns>
public ushort ComputeChecksum(string Input)
{
return ComputeChecksum(Encoding.ASCII.GetBytes(Input));
}
/// <summary>
/// Calculates the Checksum for the given bytes
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public ushort ComputeChecksum(byte[] bytes)
{
ushort crc = 0;
for (int i = 0; i < bytes.Length; ++i)
{
//byte index = (byte)(crc ^ bytes[i]);
//crc = (ushort)((crc >> 8) ^ CrcTable[index]);
crc = (ushort)(CrcTable[(bytes[i] ^ crc) & 0xFF] ^ (crc >> 8));
// crc = (ushort)((crc << 8) ^ CrcTable[((crc >> 8) ^ (0xff & bytes[i]))]);
}
return crc;
}
/// <summary>
/// Builds the Crc table programatically with the given polynomial
/// </summary>
/// <param name="polynomial"></param>
private void BuildCrcTable(ushort polynomial)
{
ushort value;
ushort temp;
// Build standard Crc16 Table
CrcTable = new ushort[256];
for (ushort i = 0; i < 256; ++i)
{
value = 0;
temp = i;
for (byte j = 0; j < 8; ++j)
{
if (((value ^ temp) & 0x0001) != 0)
value = (ushort)((value >> 1) ^ polynomial);
else
value >>= 1;
temp >>= 1;
}
CrcTable[i] = value;
}
}
}
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using System.Windows.Forms;
using System.Threading;
namespace BF2Statistics
{
public enum AlertType
{
Info, Success, Warning
}
/// <summary>
/// Notify is a class that queues and shows Alert "toast" messages
/// to the user, which spawn just above the task bar, in the lower
/// right hand side of the screen
/// </summary>
class Notify
{
/// <summary>
/// A queue of alerts to display. Alerts are added here to prevent
/// too many alerts from showing at once
/// </summary>
protected static Queue<NotifyOptions> Alerts = new Queue<NotifyOptions>();
/// <summary>
/// Returns the number of open / active alerts
/// </summary>
public static bool AlertsShowing { get; protected set; }
/// <summary>
/// Gets or Sets the default dock time of an alert, if one is not specified in
/// the ShowAlert method
/// </summary>
public static int DefaultDockTime = 3000;
/// <summary>
/// Static Constructor
/// </summary>
static Notify()
{
AlertsShowing = false;
//MainForm.SysIcon.BalloonTipClosed += new EventHandler(AlertClosed);
}
/// <summary>
/// Displays a generic Toast message with the Info icon
/// </summary>
/// <param name="Message"></param>
/// <param name="SubText"></param>
public static void Show(string Message, string SubText)
{
Alerts.Enqueue(new NotifyOptions(Message, SubText, AlertType.Info));
CheckAlerts();
}
/// <summary>
/// Shows a custom Toast message with the specified icon
/// </summary>
/// <param name="Message"></param>
/// <param name="SubText"></param>
/// <param name="Type"></param>
public static void Show(string Message, string SubText, AlertType Type)
{
Alerts.Enqueue(new NotifyOptions(Message, SubText, Type));
CheckAlerts();
}
/// <summary>
/// This method is called internally to determine if a new alert
/// can be shown from the alert queue.
/// </summary>
protected static void CheckAlerts()
{
if (!AlertsShowing && Alerts.Count > 0)
{
AlertsShowing = true;
NotifyOptions Opt = Alerts.Dequeue();
//MainForm.Instance.Invoke((Action)delegate
//{
MainForm.SysIcon.ShowBalloonTip(DefaultDockTime, Opt.Message, Opt.SubText, null);
//});
}
}
/// <summary>
/// Event is fired whenever a Toast alert is closed. This method is responsible
/// for displaying the next queued alert message
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void AlertClosed(object sender, EventArgs e)
{
AlertsShowing = false;
CheckAlerts();
}
/// <summary>
/// An internal structure used to descibe the details on an Alert message
/// to be displayed in the future
/// </summary>
internal struct NotifyOptions
{
public string Message;
public string SubText;
//public ToolTipIcon Icon;
public NotifyOptions(string Message, string SubText, AlertType Type)
{
this.Message = Message;
this.SubText = SubText;
switch (Type)
{
default:
//Icon = ToolTipIcon.Info;
break;
case AlertType.Warning:
//Icon = ToolTipIcon.Warning;
break;
}
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using System.Net;
namespace BF2Statistics
{
public static class Utils
{
/// <summary>
/// Gets the string contents of an embedded resource
/// </summary>
/// <param name="ResourceName"></param>
/// <returns></returns>
public static string GetResourceAsString(string ResourceName)
{
string Result = "";
using (Stream ResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ResourceName))
using (StreamReader Reader = new StreamReader(ResourceStream))
Result = Reader.ReadToEnd();
return Result;
}
/// <summary>
/// Gets the lines of a resource file
/// </summary>
/// <param name="ResourceName"></param>
/// <returns></returns>
public static string[] GetResourceFileLines(string ResourceName)
{
List<string> Lines = new List<string>();
using (Stream ResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ResourceName))
using (StreamReader Reader = new StreamReader(ResourceStream))
while(!Reader.EndOfStream)
Lines.Add(Reader.ReadLine());
return Lines.ToArray();
}
/// <summary>
/// Converts a Timespan of seconds into Hours, Minutes, and Seconds
/// </summary>
/// <param name="seconds">Seconds to convert</param>
/// <returns></returns>
public static string Sec2hms(int seconds)
{
TimeSpan t = TimeSpan.FromSeconds(seconds);
StringBuilder SB = new StringBuilder();
char[] trim = new char[] { ',', ' ' };
int Hours = t.Hours;
// If we have more then 24 hours, then we need to
// convert the days to hours
if (t.Days > 0)
Hours += t.Days * 24;
// Format
if (Hours > 0)
SB.AppendFormat("{0} Hours, ", Hours);
if (t.Minutes > 0)
SB.AppendFormat("{0} Minutes, ", t.Minutes);
if (t.Seconds > 0)
SB.AppendFormat("{0} Seconds, ", t.Seconds);
return SB.ToString().TrimEnd(trim);
}
}
}

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{ADFD42CE-4E2E-42F2-94BC-634AAE32A7BD}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>gspylogin</RootNamespace>
<AssemblyName>gspylogin</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MySql.Data">
<HintPath>..\lib\MySql.Data.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SQLite">
<HintPath>..\lib\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Utils">
<HintPath>..\..\Utils\Utils\bin\Debug\Utils.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Database\DatabaseDriver.cs" />
<Compile Include="Database\DatabaseEngine.cs" />
<Compile Include="Database\GamespyDatabase.cs" />
<Compile Include="Delegates.cs" />
<Compile Include="Extentions\DateExtensions.cs" />
<Compile Include="Extentions\DbConnectException.cs" />
<Compile Include="Extentions\SocketExtensions.cs" />
<Compile Include="GamespyUtils.cs" />
<Compile Include="Gpcm\ClientList.cs" />
<Compile Include="Gpcm\GpcmClient.cs" />
<Compile Include="Gpcm\GpcmServer.cs" />
<Compile Include="Gpsp\GpspClient.cs" />
<Compile Include="Gpsp\GpspServer.cs" />
<Compile Include="Logging\LogMessage.cs" />
<Compile Include="Logging\LogWritter.cs" />
<Compile Include="LoginServer.cs" />
<Compile Include="MainForm.cs" />
<Compile Include="Utilities\Crc16.cs" />
<Compile Include="Utilities\Notify.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TcpClientStream.cs" />
<Compile Include="Utilities\Utils.cs" />
</ItemGroup>
<ItemGroup>
<None Include="settings.ini">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

8
gspylogin/settings.ini Normal file
View File

@ -0,0 +1,8 @@
[general]
GamespyDBEngine=Mysql
GamespyDBHost=localhost
GamespyDBPort=3306
GamespyDBName=gamespy
GamespyDBUser=root
GamespyDBPass=mafia
DebugStream=True

BIN
lib/MySql.Data.dll Normal file

Binary file not shown.

BIN
lib/System.Data.SQLite.dll Normal file

Binary file not shown.

5365
lib/System.Data.SQLite.xml Normal file

File diff suppressed because it is too large Load Diff