/// <summary> /// Polls the simulator, this is where new threads are spun up to simulate different parts of the architecture. /// </summary> public void Poll() { // While polling threads, we count them as well // so we can decide if we want to spawn any more later. int arbitratorThreadCount = 0; int listeningArbitratorThreadCount = 0; int clientThreadCount = 0; // Lost database connection? Reconnect it. if (m_running == true && m_aborting == false) { if (m_databaseConnection == null || m_databaseConnection.Connected == false) { SetupDBConnection().Wait(); if (m_databaseConnection == null || m_databaseConnection.Connected == false) { return; } } } // Poll simulation threads. foreach (SimulatorThread thread in m_threads.ToArray()) { if (thread.IsRunning == false) { Logger.Info("Simulator thread #{0} has now finished.", LoggerVerboseLevel.High, thread.ThreadID); m_threads.Remove(thread); } else if (thread.IsAborting == false) { if (thread is ArbitratorSimulatorThread) { ArbitratorSimulatorThread arbitrator = thread as ArbitratorSimulatorThread; if (arbitrator.Service != null && arbitrator.Service.IsListening == true) { listeningArbitratorThreadCount++; } arbitratorThreadCount++; } else if (thread is ClientSimulatorThread) { clientThreadCount++; } } } // Have we stopped running or are we trying to abort? if (m_aborting == true || m_running == false) { if (m_threads.Count <= 0) { if (m_aborting == true) { // Dispose of database connection. if (m_databaseConnection != null) { m_databaseConnection.DisconnectAsync().Wait(); m_databaseConnection = null; } Logger.Info("All simulator threads have now terminated.", LoggerVerboseLevel.Normal); } m_running = false; m_aborting = false; return; } return; } // Paused? if (m_paused == true) { return; } // Spawn off new arbitrators. if (arbitratorThreadCount < m_settings.ArbitratorCount) { if (SpawnArbitrator() == true) { Logger.Info("Spawned new arbitrator thread ({0}/{1} arbitrators running).", LoggerVerboseLevel.High, arbitratorThreadCount + 1, m_settings.ArbitratorCount); } } // Spawn off new game clients. if (clientThreadCount < m_settings.ClientCount && listeningArbitratorThreadCount > 0) { if (Environment.TickCount > m_clientSpawnTimer) { if (SpawnClient() == true) { Logger.Info("Spawned new client thread ({0}/{1} clients running).", LoggerVerboseLevel.High, clientThreadCount + 1, m_settings.ClientCount); } m_clientSpawnTimer = Environment.TickCount + RandomHelper.RandomInstance.Next(m_settings.ClientConnectIntervalMin, m_settings.ClientConnectIntervalMax); } } // Load current network state. if (Environment.TickCount > m_loadNetworkStateTimer) { LoadNetworkState(); m_loadNetworkStateTimer = Environment.TickCount + m_settings.SimulatorReloadClientInterval; } }
/// <summary> /// Validates database settings to make sure we can connect to it and its setup correctly. /// </summary> private bool ValidateDatabase() { DBConnection conn = new DBConnection(); Logger.Info("Attempting to connect to database on " + m_simulationSettings.DatabaseHostname + ":" + m_simulationSettings.DatabasePort, LoggerVerboseLevel.Normal); Task<bool> task = conn.ConnectAsync(m_simulationSettings.DatabaseHostname, m_simulationSettings.DatabasePort, m_simulationSettings.DatabaseName, m_simulationSettings.DatabaseUsername, m_simulationSettings.DatabasePassword); task.Wait(); if (task.Result == true) { Logger.Info("Connected successfully, database information is valid.", LoggerVerboseLevel.Normal); conn.DisconnectAsync().Wait(); return true; } else { Logger.Error("Failed to connect to database.", LoggerVerboseLevel.Normal); return false; } }
/// <summary> /// Sets up the database connection. /// </summary> /// <returns>Returns true if successful, else false.</returns> private async Task<bool> SetupDBConnection() { bool result = false; if (m_databaseConnection == null) { Logger.Info("Attempting to connect to database on " + m_settings.DatabaseHostname + ":" + m_settings.DatabasePort + " ...", LoggerVerboseLevel.High); m_databaseConnection = new DBConnection(); result = await m_databaseConnection.ConnectAsync(m_settings.DatabaseHostname, m_settings.DatabasePort, m_settings.DatabaseName, m_settings.DatabaseUsername, m_settings.DatabasePassword); } else { Logger.Error("Lost database connection, attempting to setup connection again ...", LoggerVerboseLevel.Normal); result = await m_databaseConnection.ReconnectAsync(); } if (result == false) { Logger.Error("Failed to connect to database on " + m_settings.DatabaseHostname + ":" + m_settings.DatabasePort, LoggerVerboseLevel.Normal); } else { Logger.Info("Successfully connected to database.", LoggerVerboseLevel.High); } return result; }
/// <summary> /// Serializes this user account into the database. /// </summary> /// <param name="database">Database to serialize into.</param> public void Serialize(DBConnection database) { DBResults results = null; results = database.Query(@"SELECT id FROM {0} WHERE username='******'", Settings.DB_TABLE_ACCOUNTS, StringHelper.Escape(m_username)); byte[] persistentState = m_persistent_state.Serialize(); // Already exists? if (results.RowsAffected > 0) { results = database.QueryParameterized(@"UPDATE {0} SET username='******', password='******', email='{3}', last_login_timestamp=UNIX_TIMESTAMP(), persistent_state=@parameter_1 WHERE username='******'", new object[] { persistentState }, Settings.DB_TABLE_ACCOUNTS, StringHelper.Escape(m_username.ToLower()), StringHelper.Escape(m_password.ToLower()), StringHelper.Escape(m_email.ToLower()), StringHelper.Escape(m_username.ToLower())); } // New account? else { results = database.QueryParameterized(@"INSERT INTO {0} (username, password, email, last_login_timestamp, persistent_state) VALUES ('{1}', '{2}', '{3}', UNIX_TIMESTAMP(), @parameter_1)", new object [] { persistentState }, Settings.DB_TABLE_ACCOUNTS, StringHelper.Escape(m_username.ToLower()), StringHelper.Escape(m_password.ToLower()), StringHelper.Escape(m_email.ToLower())); } }
/// <summary> /// Loads a user account that has the given username from the database. /// </summary> /// <param name="database">Database to load username from.</param> /// <param name="username">Username of account to load.</param> /// <returns>Account loaded, or null if one dosen't exist.</returns> public static UserAccount LoadByUsername(DBConnection database, string username) { DBResults results = database.Query(@"SELECT id, username, password, email, last_login_timestamp, persistent_state FROM {0} WHERE LOWER(`username`)='{1}'", Settings.DB_TABLE_ACCOUNTS, StringHelper.Escape(username.ToLower())); if (results.RowsAffected > 0) { DBRow row = results[0]; UserAccount account = new UserAccount(); account.m_id = (int)row["id"]; account.m_username = row["username"].ToString(); account.m_password = row["password"].ToString(); account.m_email = row["email"].ToString(); account.m_last_login_timestamp = row["last_login_timestamp"] == null ? 0 : (int)row["last_login_timestamp"]; account.m_persistent_state = new UserAccountPersistentState((byte[])row["persistent_state"]); return account; } else { return null; } }
/// <summary> /// Create a user account with the given information. /// </summary> /// <param name="settings">Settings used to initialize this account.</param> /// <param name="database">Database to load username from.</param> /// <param name="username">Username of account to load.</param> /// <returns>Account loaded, or null if one dosen't exist.</returns> public static UserAccount CreateAccount(Settings settings, DBConnection database, string username, string password, string email) { DBResults results = database.Query(@"SELECT id FROM {0} WHERE LOWER(`username`)='{1}'", Settings.DB_TABLE_ACCOUNTS, StringHelper.Escape(username.ToLower())); if (results.RowsAffected <= 0) { UserAccount account = new UserAccount(); account.m_id = (int)results.LastInsertID; account.m_username = username; account.m_password = password; account.m_email = email; account.m_last_login_timestamp = 0; account.m_persistent_state = new UserAccountPersistentState(); account.LoadDefaults(settings); account.Serialize(database); return account; } else { return null; } }
/// <summary> /// Initializes this arbitrator, making initial connections to database and getting ready /// to begin accepting connections. /// </summary> /// <returns>True if successful, otherwise false. If false is returned then the Run/Deinit methods are never called.</returns> public bool Initialize() { Logger.Info("Begining setup of arbitrator server ...", LoggerVerboseLevel.High); m_listenConnection = null; m_databaseConnection = null; // Setup database connection. SetupDBConnection().Wait(); if (m_databaseConnection == null || m_databaseConnection.Connected == false) { Logger.Error("Failed to setup arbitrator, could not initialize database connection.", LoggerVerboseLevel.Normal); return false; } // Setup listening connection. SetupConnection().Wait(); if (m_listenConnection == null || m_listenConnection.Listening == false) { Logger.Error("Failed to setup arbitrator, could not initialize listen connection.", LoggerVerboseLevel.Normal); return false; } // Register arbitrator in database. RegisterArbitrator(); Logger.Info("Setup arbitrator server successfully.", LoggerVerboseLevel.High); return true; }