gspylogin hinzugefügt
This commit is contained in:
commit
cf135322d8
20
gspylogin.sln
Normal file
20
gspylogin.sln
Normal 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
|
527
gspylogin/Database/DatabaseDriver.cs
Normal file
527
gspylogin/Database/DatabaseDriver.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
13
gspylogin/Database/DatabaseEngine.cs
Normal file
13
gspylogin/Database/DatabaseEngine.cs
Normal 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,
|
||||
}
|
||||
}
|
298
gspylogin/Database/GamespyDatabase.cs
Normal file
298
gspylogin/Database/GamespyDatabase.cs
Normal 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
28
gspylogin/Delegates.cs
Normal 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);
|
||||
}
|
32
gspylogin/Extentions/DateExtensions.cs
Normal file
32
gspylogin/Extentions/DateExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
12
gspylogin/Extentions/DbConnectException.cs
Normal file
12
gspylogin/Extentions/DbConnectException.cs
Normal 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) { }
|
||||
}
|
||||
}
|
28
gspylogin/Extentions/SocketExtensions.cs
Normal file
28
gspylogin/Extentions/SocketExtensions.cs
Normal 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
96
gspylogin/GamespyUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
17
gspylogin/Gpcm/ClientList.cs
Normal file
17
gspylogin/Gpcm/ClientList.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
541
gspylogin/Gpcm/GpcmClient.cs
Normal file
541
gspylogin/Gpcm/GpcmClient.cs
Normal 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
|
||||
}
|
||||
}
|
129
gspylogin/Gpcm/GpcmServer.cs
Normal file
129
gspylogin/Gpcm/GpcmServer.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
181
gspylogin/Gpsp/GpspClient.cs
Normal file
181
gspylogin/Gpsp/GpspClient.cs
Normal 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 "";
|
||||
}
|
||||
}
|
||||
}
|
81
gspylogin/Gpsp/GpspServer.cs
Normal file
81
gspylogin/Gpsp/GpspServer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
29
gspylogin/Logging/LogMessage.cs
Normal file
29
gspylogin/Logging/LogMessage.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
141
gspylogin/Logging/LogWritter.cs
Normal file
141
gspylogin/Logging/LogWritter.cs
Normal 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
164
gspylogin/LoginServer.cs
Normal 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
51
gspylogin/MainForm.cs
Normal 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
67
gspylogin/Program.cs
Normal 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!");
|
||||
}
|
||||
}
|
||||
}
|
36
gspylogin/Properties/AssemblyInfo.cs
Normal file
36
gspylogin/Properties/AssemblyInfo.cs
Normal 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")]
|
170
gspylogin/TcpClientStream.cs
Normal file
170
gspylogin/TcpClientStream.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
102
gspylogin/Utilities/Crc16.cs
Normal file
102
gspylogin/Utilities/Crc16.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
127
gspylogin/Utilities/Notify.cs
Normal file
127
gspylogin/Utilities/Notify.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
gspylogin/Utilities/Utils.cs
Normal file
75
gspylogin/Utilities/Utils.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
91
gspylogin/gspylogin.csproj
Normal file
91
gspylogin/gspylogin.csproj
Normal 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
8
gspylogin/settings.ini
Normal 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
BIN
lib/MySql.Data.dll
Normal file
Binary file not shown.
BIN
lib/System.Data.SQLite.dll
Normal file
BIN
lib/System.Data.SQLite.dll
Normal file
Binary file not shown.
5365
lib/System.Data.SQLite.xml
Normal file
5365
lib/System.Data.SQLite.xml
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user