//This method is called when callExtension is used from SQF: //"Arma2Net.Unmanaged" callExtension "Arma2NetMySQLCommand ..." public override string Invoke(string args, int maxResultSize) { //if we haven't setup the database connection and such yet, this will do it Startup.StartupConnection(); IList <object> arguments; if (Format.TrySqfAsCollection(args, out arguments) && arguments.Count == 2 && arguments[0] != null && arguments[1] != null) { string database = arguments[0] as string; string mysql_command = arguments[1] as string; Logger.addMessage(Logger.LogType.Info, "Received - Database: " + database + " SQL Query: " + mysql_command.ToString()); if (SQL.dbs.SQLProviderExists(database)) { IEnumerable <string[][]> returned = SQL.dbs.getSQLProvider(database).RunCommand(mysql_command, maxResultSize); return(Format.ObjectAsSqf(returned)); } else { Logger.addMessage(Logger.LogType.Warning, "The database: " + database + " is not loaded in through the Databases.txt file."); } //Logger.addMessage(Logger.LogType.Info, "Returning false object"); return(Format.ObjectAsSqf(false)); } else { Logger.addMessage(Logger.LogType.Error, "The number and/or format of the arguments passed in doesn't match."); throw new ArgumentException(); } }
public DatabaseObject(params string[] values) { //Constructor type = values[0]; databasename = values[1]; if (values.Length == 6) { ipaddress = values[2]; port = values[3]; username = values[4]; password = values[5]; } if (type == "mysql") { sql_connection = new MySQL(); sql_connection.OpenConnection(getMySQLFormattedConnectionString()); } else if (type == "sqlite") { sql_connection = new SQLite(); sql_connection.OpenConnection(databasename); } else { Logger.addMessage(Logger.LogType.Error, "Database type does not match one of the supported types."); } }
public override IEnumerable <string[][]> RunProcedure(string procedure, string[] parameters, int maxResultSize) { if (connection == null) { OpenConnection(connectString); } //Logger.addMessage(Logger.LogType.Info, "Started RunProcedure"); if (connection != null && connection.State == System.Data.ConnectionState.Open && procedure != null) { MySqlCommand command = GetCommand(procedure); if (parameters != null) // could have a procedure with no inputs { Logger.addMessage(Logger.LogType.Info, "Parsing parameters..."); for (int i = 0; i < parameters.Length; i++) { //separate out parameter name and value string[] split = parameters[i].Split('='); if (split.Length != 2) { Logger.addMessage(Logger.LogType.Warning, "Couldn't parse procedure, split didn't work."); } //Logger.addMessage(Logger.LogType.Info, "Adding parameter key:value " + split[0].ToString() + ":" + split[1].ToString()); command.Parameters.AddWithValue(split[0], (object)split[1]); } } yield return(RunOnDatabase(command, maxResultSize)); } //Logger.addMessage(Logger.LogType.Info, "yield breaking in RunProcedure"); yield break; }
public static void Unload() { Logger.addMessage(Logger.LogType.Info, "Unloading plugin."); SQL.dbs.shutdown(); Logger.Stop(); Startup.started_up = false; }
private void checkConnectionThread(object obj) { //This thread runs every 5 minutes and checks to see when the last time we ran a query //If it's been over 30 minutes, we close down the MySQL connection while (true) { if (connection != null && connection.State == System.Data.ConnectionState.Open) { TimeSpan ts = DateTime.Now - lastQuery; if (ts.Minutes > 30) { //over 30 minutes, close it down Logger.addMessage(Logger.LogType.Info, "30 minutes of inactivity have passed. Closing down MySQL connection."); CloseConnection(); break; } } //http://www.dotnetperls.com/sleep //TODO: according to the above link, sleep does not use CPU cycles //we need to make sure this is not significantly eating into CPU resources! Thread.Sleep((60 * 5) * 1000); } //Logger.addMessage(Logger.LogType.Info, "checkConnectionThread closing down."); //threads cannot be started up again, so we null this out, it will be opened back up later staleConnectionThread = null; }
public override void CloseConnection() { if (sqlite_connection != null) { try { sqlite_connection.Close(); sqlite_connection = null; } catch (Exception ex) { Logger.addMessage(Logger.LogType.Error, "Unable to close SQLite connection." + ex.ToString()); } } }
public override void OpenConnection(string connectionString) { // if there is no connection if (connection == null) { connection = new MySqlConnection(connectionString); } // if there is a connection and it is a different connection string else if (connection.ConnectionString != connectionString) { // close old connection and create with the new connection string CloseConnection(); connection = new MySqlConnection(connectionString); } //save the connection string, we'll need this if the check thread ever closes down the connection connectString = connectionString; //update the last time a query has been run lastQuery = DateTime.Now; //start up thread that checks to see how long we've gone since running a command //we run into problems if the MySQL connection is kept open for significantly long periods of time if (staleConnectionThread == null) { staleConnectionThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(checkConnectionThread)); staleConnectionThread.Name = "staleConnectionThread"; staleConnectionThread.Start(); } else if (staleConnectionThread.ThreadState != System.Threading.ThreadState.Running) { staleConnectionThread.Start(); } while (connection.State != System.Data.ConnectionState.Open) { try { //Logger.addMessage(Logger.LogType.Info, "Opening MySQL connection."); connection.Open(); } catch (Exception ex) { Logger.addMessage(Logger.LogType.Info, "Unable to open connection to MySQL database, trying again in 10 seconds." + ex.ToString()); Thread.Sleep(10000); } } }
public override void OpenConnection(string databasename) { try { // if there is no connection if (sqlite_connection == null) { Logger.addMessage(Logger.LogType.Info, "SQLite folder location: " + sqliteDatabaseLocation); sqlite_connection = new SQLiteConnection("Data Source=" + sqliteDatabaseLocation + databasename + ".sqlite"); } sqlite_connection.Open(); } catch (Exception ex) { Logger.addMessage(Logger.LogType.Error, "Unable to open connection to SQLite database." + ex.ToString()); } }
//This method is called when callExtension is used from SQF: //"Arma2Net.Unmanaged" callExtension "Arma2NetMySQL ..." public override string Invoke(string args, int maxResultSize) { //if we haven't setup the database connection and such yet, this will do it Startup.StartupConnection(); IList <object> arguments; if (Format.TrySqfAsCollection(args, out arguments) && arguments.Count >= 2 && arguments[0] != null && arguments[1] != null) { string database = arguments[0] as string; string procedure = arguments[1] as string; string parameters = arguments[2] as string; //strip out [] characters at the beginning and end if (parameters[0].ToString() == "[" && parameters[parameters.Length - 1].ToString() == "]") { parameters = parameters.Substring(1, parameters.Length - 2); } List <string> split = new List <string>(); if (parameters != null) { split = parameters.Split(',').ToList <string>(); } Logger.addMessage(Logger.LogType.Info, "Received - Database: " + database + " Procedure: " + procedure + " Parameters: " + parameters.ToString()); if (SQL.dbs.SQLProviderExists(database)) { IEnumerable <string[][]> returned = SQL.dbs.getSQLProvider(database).RunProcedure(procedure, split.ToArray(), maxResultSize); return(Format.ObjectAsSqf(returned)); } else { Logger.addMessage(Logger.LogType.Warning, "The database: " + database + " is not loaded in through the Databases.txt file."); } //Logger.addMessage(Logger.LogType.Info, "Returning false object"); return(Format.ObjectAsSqf(false)); } else { Logger.addMessage(Logger.LogType.Error, "The number and/or format of the arguments passed in doesn't match."); throw new ArgumentException(); } }
//AsyncAddIn - when you want to pass data from the game and immediately return null // then, subsequent checks by the game check to see if the data can be returned. //On the SQF side, this means that we can only do one call at a time... //This method is called when callExtension is used from SQF: //"Arma2Net.Unmanaged" callExtension "Arma2NetMySQLCommandAsync ..." public override string InvokeAsync(string args, int maxResultSize, CancellationToken token) { //if we haven't setup the database connection and such yet, this will do it Startup.StartupConnection(); IList <object> arguments; if (Format.TrySqfAsCollection(args, out arguments) && arguments.Count == 2 && arguments[0] != null && arguments[1] != null) { string database = arguments[0] as string; string mysql_command = arguments[1] as string; Logger.addMessage(Logger.LogType.Info, "Received - Database: " + database + " SQL Query: " + mysql_command.ToString()); if (SQL.dbs.SQLProviderExists(database)) { IEnumerable <string[][]> returned = SQL.dbs.getSQLProvider(database).RunCommand(mysql_command, maxResultSize); //the following is needed because we need to return something even if there is nothing to return //for example, an SQL DELETE call will go off and return "" //however, because on the SQF side, we check for this in a while loop so we know the database process has completed, we can //just return an empty array if (returned.ToString() == "") { return(Format.ObjectAsSqf("[]")); } return(Format.ObjectAsSqf(returned)); } else { Logger.addMessage(Logger.LogType.Warning, "The database: " + database + " is not loaded in through the Databases.txt file."); } //Logger.addMessage(Logger.LogType.Info, "Returning false object"); return(Format.ObjectAsSqf(false)); } else { Logger.addMessage(Logger.LogType.Error, "The number and/or format of the arguments passed in doesn't match."); throw new ArgumentException(); } }
public static void StartupConnection() { if (started_up == false) { //create appdata folder if it doesn't already exist var appDataLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Arma2NETMySQL"); //check to see if the Arma2NETMySQL folder exists, if not create it if (!System.IO.Directory.Exists(appDataLocation)) { System.IO.Directory.CreateDirectory(appDataLocation); } //Start up logging logger_object = new Logger(); Logger.addMessage(Logger.LogType.Info, "Logging started in directory: " + Logger.getLogDir()); Logger.addMessage(Logger.LogType.Info, "Arma2NETMySQL Plugin Started."); //Use AssemblyInfo.cs version number //Holy cow this is confusing... //http://stackoverflow.com/questions/909555/how-can-i-get-the-assembly-file-version //http://all-things-pure.blogspot.com/2009/09/assembly-version-file-version-product.html //http://stackoverflow.com/questions/64602/what-are-differences-between-assemblyversion-assemblyfileversion-and-assemblyin Logger.addMessage(Logger.LogType.Info, "Version number: " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()); //Utils.FileVersion returns the current version that is running //Utils.Version returns the API version, when this changes, all plugins need to be recompiled //This version call doesn't always seem to play nicely (it causes errors where it can't find the managed.dll) //Logger.addMessage(Logger.LogType.Info, "Compiled against Arma2NET Version: " + Utils.FileVersion); //Load in Databases.txt file //This also sets up the SQLProvider associated with the database Logger.addMessage(Logger.LogType.Info, "Loading databases..."); SQL.dbs = new Databases(); //set mutex so we know we've started everything up started_up = true; } }
public override IEnumerable <string[][]> RunCommand(string sql_command, int maxResultSize) { //SQLite example code: //http://www.dreamincode.net/forums/topic/157830-using-sqlite-with-c%23/ DataTable dt = new DataTable(); try { SQLiteCommand mycommand = new SQLiteCommand(sqlite_connection); mycommand.CommandText = sql_command; SQLiteDataReader reader = mycommand.ExecuteReader(); dt.Load(reader); reader.Close(); } catch (Exception ex) { Logger.addMessage(Logger.LogType.Error, "Unable to run SQLite command." + ex.ToString()); } //convert datatable to array string[][] string_array = new string[dt.Rows.Count][]; for (int i = 0; i < dt.Rows.Count; i++) { DataRow row = dt.Rows[i]; string_array[i] = row.ItemArray.Select(j => j.ToString()).ToArray(); } if (validLength(string_array, maxResultSize) == false) { yield return(new string[][] { new[] { "TooLong" } }); } else { yield return(string_array); } }
private string[][] RunOnDatabase(MySqlCommand command, int maxResultSize) { MySqlDataReader reader = null; //update the last time a query has been run lastQuery = DateTime.Now; Boolean mysql_error = false; try { //Logger.addMessage(Logger.LogType.Info, "Executing mysql command."); reader = command.ExecuteReader(); } catch (Exception sql_ex) { //Catch any MySQL errors (bad procedure name, missing/malformed parameters, etc.) and just return false //Can't use yield in a try/catch so we'll return later... mysql_error = true; Logger.addMessage(Logger.LogType.Warning, "MySQL error. " + sql_ex.ToString()); } using (reader) { if (mysql_error == false) { List <List <string> > to_return = new List <List <string> >(); int max_array_rows = 0; while (reader.Read()) { List <string> inner_data = new List <string>(); for (int i = 0; i < reader.FieldCount; i++) { //We need to check for a null column entry here, otherwise, things explode //http://stackoverflow.com/questions/1772025/sql-data-reader-handling-null-column-values string value = ""; if (!reader.IsDBNull(i)) { value = reader.GetString(i); } inner_data.Add(value); //Logger.addMessage(Logger.LogType.Info, "Row value is: " + value); } to_return.Add(inner_data); max_array_rows++; } reader.Close(); //convert into the array which we'll have to pass back string[][] string_array = new string[max_array_rows][]; for (int i = 0; i < max_array_rows; i++) { string_array[i] = to_return[i].ToArray(); } if (validLength(string_array, maxResultSize) == false) { return(new string[][] { new[] { "TooLong" } }); } else { return(string_array); } } } //Logger.addMessage(Logger.LogType.Info, "Returning error from RunProcedure"); return(new string[][] { new[] { "Error" } }); }
public Databases() { //Constructor //Load the database names, ip, port, usernames, passwords, and so on from file string line; string databasesFileLocation = null; //check the Arma2 root directory first if (File.Exists("Databases.txt")) { databasesFileLocation = Path.GetFullPath("Databases.txt"); } else { Logger.addMessage(Logger.LogType.Warning, "Unable to find the Databases.txt file here: " + Path.GetFullPath("Databases.txt")); databasesFileLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Arma2NETMySQL/Databases.txt"); if (!File.Exists(databasesFileLocation)) { Logger.addMessage(Logger.LogType.Error, "Unable to find the Databases.txt here: " + databasesFileLocation); } } Logger.addMessage(Logger.LogType.Info, "Databases.txt file loading in from: " + databasesFileLocation); StreamReader sr = File.OpenText(databasesFileLocation); line = sr.ReadLine(); while (line != null) { //Make sure it's not a comment if (!line.StartsWith("#")) { //Separate out the information string[] split = line.Split(','); if (split.Length == 6) { split[0] = split[0].ToLower(); Logger.addMessage(Logger.LogType.Info, "Type: " + split[0] + " Database: " + split[1] + " IPAddress: " + split[2] + " Port: " + split[3] + " Username: "******" Password: NotShownForSecurityReasons"); DatabaseObject temp = new DatabaseObject(new string[6] { split[0], split[1], split[2], split[3], split[4], split[5] }); databaseList.Add(temp); } else if (split.Length == 2) { split[0] = split[0].ToLower(); Logger.addMessage(Logger.LogType.Info, "Type: " + split[0] + " Database: " + split[1]); DatabaseObject temp = new DatabaseObject(new string[2] { split[0], split[1] }); databaseList.Add(temp); } else if (line.Contains(",")) { Logger.addMessage(Logger.LogType.Error, "Unable to parse line: " + line + " in Databases.txt file."); } } line = sr.ReadLine(); } sr.Close(); }
public override IEnumerable <string[][]> RunProcedure(string procedure, string[] parameters, int maxResultSize) { Logger.addMessage(Logger.LogType.Error, "SQLite cannot run stored procedures."); return(null); }