ITransaction BeginSourceTransaction(long BankAccountK, Money Amount, string Message) { IBankAccount bankAccount = GetBankAccount(BankAccountK); ITransaction sourceTran = new XmlTransaction(bankAccount); sourceTran.Flags = Journal.BankAccountTransactionFlags.FundsAvailable; sourceTran.TransactionDateUtc = DateTime.UtcNow; sourceTran.Amount = (Amount * (-1)); if (!string.IsNullOrEmpty(Message)) { sourceTran.Message = Message; } return(bankAccount.AddTransaction(sourceTran)); }
ITransaction FinishEndTransaction(long SourceBankTransactionKey, IBankAccount ToAccount, Money Amount, string Message) { ITransaction destTran = new XmlTransaction(ToAccount); destTran.BankAccountFK = ToAccount.BankAccountK; destTran.Flags = Journal.BankAccountTransactionFlags.FundsAvailable; destTran.TransactionDateUtc = DateTime.UtcNow; destTran.Amount = Amount; destTran.BankAccountTransactionFK = SourceBankTransactionKey; if (!string.IsNullOrEmpty(Message)) { destTran.Message = Message; } return(ToAccount.AddTransaction(destTran)); }
public async Task SquashJournalAsync() { int bankAccountCount = BankAccounts.Count(); bool responsibleForTurningBackupsBackOn = false; Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(81, "seconomy xml: beginning Squash")); if (SEconomyInstance.RunningJournal.BackupsEnabled == true) { SEconomyInstance.RunningJournal.BackupsEnabled = false; responsibleForTurningBackupsBackOn = true; } for (int i = 0; i < bankAccountCount; i++) { IBankAccount account = BankAccounts.ElementAtOrDefault(i); if (account == null) { continue; } // Add the squished summary ITransaction squash = new XmlTransaction(account) { Amount = account.Transactions.Sum(x => x.Amount), Flags = BankAccountTransactionFlags.FundsAvailable | BankAccountTransactionFlags.Squashed, TransactionDateUtc = DateTime.UtcNow, Message = "Transaction squash" }; account.Transactions.Clear(); account.AddTransaction(squash); } //abandon the old journal and assign the squashed one Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(82, "re-syncing online accounts.")); foreach (TSPlayer player in TShockAPI.TShock.Players) { IBankAccount account = null; if (player == null || SEconomyPlugin.Instance == null || (account = SEconomyPlugin.Instance.GetBankAccount(player)) == null) { return; } Console.WriteLine("re-syncing {0}", player.Name); await account.SyncBalanceAsync(); } await SaveJournalAsync(); if (responsibleForTurningBackupsBackOn) { /* * the backups could already have been disabled by something else. * We don't want to be the ones turning it back on */ SEconomyInstance.RunningJournal.BackupsEnabled = true; } }
public bool LoadJournal() { MemoryStream uncompressedData; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; JournalLoadingPercentChangedEventArgs finalArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Verify" }; ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Using XML journal - {0}", path); try { byte[] fileData = new byte[0]; initPoint: try { fileData = File.ReadAllBytes(path); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.DarkCyan; if (ex is System.IO.FileNotFoundException || ex is System.IO.DirectoryNotFoundException) { TShock.Log.ConsoleInfo(" * It appears you do not have a journal yet, one will be created for you."); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } else if (ex is System.Security.SecurityException) { TShock.Log.ConsoleError(" * Access denied to the journal file. Check permissions."); } else { TShock.Log.ConsoleError(" * Loading your journal failed: " + ex.Message); } } try { uncompressedData = GZipDecompress(fileData); } catch { TShock.Log.ConsoleError(" * Decompression failed."); return(false); } if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } try { Hashtable bankAccountMap = new Hashtable(); Hashtable transactionMap = new Hashtable(); //You can't use XDocument.Parse when you have an XDeclaration for some even dumber reason than the write //issue, XDocument has to be constructed from a .net 3.5 XmlReader in this case //or you get a parse exception complaining about literal content. using (XmlTextReader xmlStream = new XmlTextReader(uncompressedData)) { XDocument doc = XDocument.Load(xmlStream); var bankAccountList = doc.XPathSelectElements("/Journal/BankAccounts/BankAccount"); int bankAccountCount = bankAccountList.Count(); int i = 0; int oldPercent = 0; foreach (XElement elem in bankAccountList) { long bankAccountK = 0L; double percentComplete = (double)i / (double)bankAccountCount * 100; var account = new XmlBankAccount(this) { Description = elem.Attribute("Description").Value, UserAccountName = elem.Attribute("UserAccountName").Value, WorldID = long.Parse(elem.Attribute("WorldID").Value), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), elem.Attribute("Flags").Value) }; if (!long.TryParse(elem.Attribute("BankAccountK").Value, out bankAccountK)) { //we've overwritten the old bank account key, add it to the old/new map bankAccountK = Interlocked.Increment(ref _bankAccountSeed); bankAccountMap.Add(elem.Attribute("BankAccountK").Value, bankAccountK); } account.BankAccountK = bankAccountK; //parse transactions under this node if (elem.Element("Transactions") != null) { foreach (XElement txElement in elem.Element("Transactions").Elements("Transaction")) { long transactionK = 0L; long transactionFK = 0; long amount = long.Parse(txElement.Attribute("Amount").Value); DateTime transactionDate; BankAccountTransactionFlags flags; long.TryParse(txElement.Attribute("BankAccountTransactionK").Value, out transactionK); long.TryParse(txElement.Attribute("BankAccountTransactionFK").Value, out transactionFK); DateTime.TryParse(txElement.Attribute("TransactionDateUtc").Value, out transactionDate); Enum.TryParse <BankAccountTransactionFlags>(txElement.Attribute("Flags").Value, out flags); //ignore orphaned transactions var trans = new XmlTransaction(account) { Amount = amount, BankAccountTransactionK = transactionK, BankAccountTransactionFK = transactionFK, TransactionDateUtc = transactionDate, Flags = flags }; trans.Message = txElement.Attribute("Message") != null?txElement.Attribute("Message").Value : null; account.AddTransaction(trans); } } account.SyncBalance(); if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } Interlocked.Increment(ref i); bankAccounts.Add(account); } Interlocked.Exchange(ref _bankAccountSeed, bankAccounts.Count() > 0 ? bankAccounts.Max(sum => sum.BankAccountK) : 0); //delete transactions with duplicate IDs var qAccounts = from summary in bankAccounts group summary by summary.BankAccountK into g where g.Count() > 1 select new { name = g.Key, count = g.Count() }; long[] duplicateAccounts = qAccounts.Select(pred => pred.name).ToArray(); int removedAccounts = bankAccounts.RemoveAll(pred => duplicateAccounts.Contains(pred.BankAccountK)); if (removedAccounts > 0) { TShock.Log.Warn("seconomy journal: removed " + removedAccounts + " accounts with duplicate IDs."); } //transactions in the old schema. int tranCount = doc.XPathSelectElements("/Journal/Transactions/Transaction").Count(); i = 0; //reset index foreach (XElement elem in doc.XPathSelectElements("/Journal/Transactions/Transaction")) { //Parallel.ForEach(doc.XPathSelectElements("/Journal/Transactions/Transaction"), (elem) => { double percentComplete = (double)i / (double)tranCount * 100; long bankAccountFK = 0L; long transactionK = 0L; long amount = long.Parse(elem.Attribute("Amount").Value); if (!long.TryParse(elem.Attribute("BankAccountFK").Value, out bankAccountFK)) { if (bankAccountMap.ContainsKey(elem.Attribute("BankAccountFK").Value)) { Interlocked.Exchange(ref bankAccountFK, (long)bankAccountMap[elem.Attribute("BankAccountFK").Value]); } } IBankAccount bankAccount = GetBankAccount(bankAccountFK); long.TryParse(elem.Attribute("BankAccountTransactionK").Value, out transactionK); //ignore orphaned transactions if (bankAccount != null) { var trans = new XmlTransaction(bankAccount) { Amount = amount, BankAccountTransactionK = transactionK }; if (elem.Attribute("BankAccountTransactionFK") != null) { trans.CustomValues.Add(XmlTransaction.kXmlTransactionOldTransactonFK, elem.Attribute("BankAccountTransactionFK").Value); } trans.Message = elem.Attribute("Message") != null?elem.Attribute("Message").Value : null; bankAccount.AddTransaction(trans); transactionMap.Add(elem.Attribute("BankAccountTransactionK").Value, trans.BankAccountTransactionK); } Interlocked.Increment(ref i); } int txCount = Transactions.Count(); int x = 0; finalArgs.Percent = 0; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, finalArgs); } foreach (IBankAccount account in bankAccounts) { foreach (XmlTransaction trans in account.Transactions) { double pcc = (double)x / (double)txCount * 100; //assigns the transactionK according to the hashmap of the old key stored as custom values if (trans.CustomValues.ContainsKey(XmlTransaction.kXmlTransactionOldTransactonFK)) { object value = transactionMap[trans.CustomValues[XmlTransaction.kXmlTransactionOldTransactonFK]]; trans.BankAccountTransactionFK = value != null ? (long)value : -1L; } if (oldPercent != (int)pcc) { if (JournalLoadingPercentChanged != null) { finalArgs.Percent = (int)pcc; JournalLoadingPercentChanged(this, finalArgs); oldPercent = (int)pcc; } } Interlocked.Increment(ref x); trans.CustomValues.Clear(); trans.CustomValues = null; } } bankAccountMap = null; transactionMap = null; // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); var accountCount = bankAccounts.Count; Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("\r\n Journal clean: {0} accounts and {1} transactions.", accountCount, Transactions.Count()); Console.ResetColor(); } } catch (Exception ex) { ConsoleEx.WriteAtEnd(2, ConsoleColor.Red, "[{0}]\r\n", SEconomyPlugin.Locale.StringOrDefault(79, "corrupt")); TShock.Log.ConsoleError(ex.ToString()); Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(80, "Your transaction journal appears to be corrupt and transactions have been lost.\n\nYou will start with a clean journal.\nYour old journal file has been move to SEconomy.journal.xml.gz.corrupt")); File.Move(path, path + "." + DateTime.Now.ToFileTime().ToString() + ".corrupt"); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } } finally { Console.WriteLine(); } return(true); }
ITransaction FinishEndTransaction(long SourceBankTransactionKey, IBankAccount ToAccount, Money Amount, string Message) { ITransaction destTran = new XmlTransaction(ToAccount); destTran.BankAccountFK = ToAccount.BankAccountK; destTran.Flags = Journal.BankAccountTransactionFlags.FundsAvailable; destTran.TransactionDateUtc = DateTime.UtcNow; destTran.Amount = Amount; destTran.BankAccountTransactionFK = SourceBankTransactionKey; if (!string.IsNullOrEmpty(Message)) { destTran.Message = Message; } return ToAccount.AddTransaction(destTran); }
ITransaction BeginSourceTransaction(long BankAccountK, Money Amount, string Message) { IBankAccount bankAccount = GetBankAccount(BankAccountK); ITransaction sourceTran = new XmlTransaction(bankAccount); sourceTran.Flags = Journal.BankAccountTransactionFlags.FundsAvailable; sourceTran.TransactionDateUtc = DateTime.UtcNow; sourceTran.Amount = (Amount * (-1)); if (!string.IsNullOrEmpty(Message)) { sourceTran.Message = Message; } return bankAccount.AddTransaction(sourceTran); }
public async Task SquashJournalAsync() { int bankAccountCount = BankAccounts.Count(); bool responsibleForTurningBackupsBackOn = false; Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(81, "seconomy xml: beginning Squash")); if (SEconomyInstance.RunningJournal.BackupsEnabled == true) { SEconomyInstance.RunningJournal.BackupsEnabled = false; responsibleForTurningBackupsBackOn = true; } for (int i = 0; i < bankAccountCount; i++) { IBankAccount account = BankAccounts.ElementAtOrDefault(i); if (account == null) { continue; } // Add the squished summary ITransaction squash = new XmlTransaction(account) { Amount = account.Transactions.Sum(x => x.Amount), Flags = BankAccountTransactionFlags.FundsAvailable | BankAccountTransactionFlags.Squashed, TransactionDateUtc = DateTime.UtcNow, Message = "Transaction squash" }; account.Transactions.Clear(); account.AddTransaction(squash); } //abandon the old journal and assign the squashed one Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(82, "re-syncing online accounts.")); foreach (TSPlayer player in TShockAPI.TShock.Players) { IBankAccount account = null; if (player == null || SEconomyPlugin.Instance == null || (account = SEconomyPlugin.Instance.GetBankAccount(player)) == null) { return; } Console.WriteLine("re-syncing {0}", player.Name); await account.SyncBalanceAsync(); } await SaveJournalAsync(); if (responsibleForTurningBackupsBackOn) { /* * the backups could already have been disabled by something else. * We don't want to be the ones turning it back on */ SEconomyInstance.RunningJournal.BackupsEnabled = true; } }
public bool LoadJournal() { MemoryStream uncompressedData; JournalLoadingPercentChangedEventArgs parsingArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Loading" }; JournalLoadingPercentChangedEventArgs finalArgs = new JournalLoadingPercentChangedEventArgs() { Label = "Verify" }; ConsoleEx.WriteLineColour(ConsoleColor.Cyan, " Using XML journal - {0}", path); try { byte[] fileData = new byte[0]; initPoint: try { fileData = File.ReadAllBytes(path); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.DarkCyan; if (ex is System.IO.FileNotFoundException || ex is System.IO.DirectoryNotFoundException) { TShock.Log.ConsoleInfo(" * It appears you do not have a journal yet, one will be created for you."); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } else if (ex is System.Security.SecurityException) { TShock.Log.ConsoleError(" * Access denied to the journal file. Check permissions."); } else { TShock.Log.ConsoleError(" * Loading your journal failed: " + ex.Message); } } try { uncompressedData = GZipDecompress(fileData); } catch { TShock.Log.ConsoleError(" * Decompression failed."); return false; } if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } try { Hashtable bankAccountMap = new Hashtable(); Hashtable transactionMap = new Hashtable(); //You can't use XDocument.Parse when you have an XDeclaration for some even dumber reason than the write //issue, XDocument has to be constructed from a .net 3.5 XmlReader in this case //or you get a parse exception complaining about literal content. using (XmlTextReader xmlStream = new XmlTextReader(uncompressedData)) { XDocument doc = XDocument.Load(xmlStream); var bankAccountList = doc.XPathSelectElements("/Journal/BankAccounts/BankAccount"); int bankAccountCount = bankAccountList.Count(); int i = 0; int oldPercent = 0; foreach (XElement elem in bankAccountList) { long bankAccountK = 0L; double percentComplete = (double)i / (double)bankAccountCount * 100; var account = new XmlBankAccount(this) { Description = elem.Attribute("Description").Value, UserAccountName = elem.Attribute("UserAccountName").Value, WorldID = long.Parse(elem.Attribute("WorldID").Value), Flags = (BankAccountFlags)Enum.Parse(typeof(BankAccountFlags), elem.Attribute("Flags").Value) }; if (!long.TryParse(elem.Attribute("BankAccountK").Value, out bankAccountK)) { //we've overwritten the old bank account key, add it to the old/new map bankAccountK = Interlocked.Increment(ref _bankAccountSeed); bankAccountMap.Add(elem.Attribute("BankAccountK").Value, bankAccountK); } account.BankAccountK = bankAccountK; //parse transactions under this node if (elem.Element("Transactions") != null) { foreach (XElement txElement in elem.Element("Transactions").Elements("Transaction")) { long transactionK = 0L; long transactionFK = 0; long amount = long.Parse(txElement.Attribute("Amount").Value); DateTime transactionDate; BankAccountTransactionFlags flags; long.TryParse(txElement.Attribute("BankAccountTransactionK").Value, out transactionK); long.TryParse(txElement.Attribute("BankAccountTransactionFK").Value, out transactionFK); DateTime.TryParse(txElement.Attribute("TransactionDateUtc").Value, out transactionDate); Enum.TryParse<BankAccountTransactionFlags>(txElement.Attribute("Flags").Value, out flags); //ignore orphaned transactions var trans = new XmlTransaction(account) { Amount = amount, BankAccountTransactionK = transactionK, BankAccountTransactionFK = transactionFK, TransactionDateUtc = transactionDate, Flags = flags }; trans.Message = txElement.Attribute("Message") != null ? txElement.Attribute("Message").Value : null; account.AddTransaction(trans); } } account.SyncBalance(); if (oldPercent != (int)percentComplete) { parsingArgs.Percent = (int)percentComplete; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, parsingArgs); } oldPercent = (int)percentComplete; } Interlocked.Increment(ref i); bankAccounts.Add(account); } Interlocked.Exchange(ref _bankAccountSeed, bankAccounts.Count() > 0 ? bankAccounts.Max(sum => sum.BankAccountK) : 0); //delete transactions with duplicate IDs var qAccounts = from summary in bankAccounts group summary by summary.BankAccountK into g where g.Count() > 1 select new { name = g.Key, count = g.Count() }; long[] duplicateAccounts = qAccounts.Select(pred => pred.name).ToArray(); int removedAccounts = bankAccounts.RemoveAll(pred => duplicateAccounts.Contains(pred.BankAccountK)); if (removedAccounts > 0) { TShock.Log.Warn("seconomy journal: removed " + removedAccounts + " accounts with duplicate IDs."); } //transactions in the old schema. int tranCount = doc.XPathSelectElements("/Journal/Transactions/Transaction").Count(); i = 0; //reset index foreach (XElement elem in doc.XPathSelectElements("/Journal/Transactions/Transaction")) { //Parallel.ForEach(doc.XPathSelectElements("/Journal/Transactions/Transaction"), (elem) => { double percentComplete = (double)i / (double)tranCount * 100; long bankAccountFK = 0L; long transactionK = 0L; long amount = long.Parse(elem.Attribute("Amount").Value); if (!long.TryParse(elem.Attribute("BankAccountFK").Value, out bankAccountFK)) { if (bankAccountMap.ContainsKey(elem.Attribute("BankAccountFK").Value)) { Interlocked.Exchange(ref bankAccountFK, (long)bankAccountMap[elem.Attribute("BankAccountFK").Value]); } } IBankAccount bankAccount = GetBankAccount(bankAccountFK); long.TryParse(elem.Attribute("BankAccountTransactionK").Value, out transactionK); //ignore orphaned transactions if (bankAccount != null) { var trans = new XmlTransaction(bankAccount) { Amount = amount, BankAccountTransactionK = transactionK }; if (elem.Attribute("BankAccountTransactionFK") != null) { trans.CustomValues.Add(XmlTransaction.kXmlTransactionOldTransactonFK, elem.Attribute("BankAccountTransactionFK").Value); } trans.Message = elem.Attribute("Message") != null ? elem.Attribute("Message").Value : null; bankAccount.AddTransaction(trans); transactionMap.Add(elem.Attribute("BankAccountTransactionK").Value, trans.BankAccountTransactionK); } Interlocked.Increment(ref i); } int txCount = Transactions.Count(); int x = 0; finalArgs.Percent = 0; if (JournalLoadingPercentChanged != null) { JournalLoadingPercentChanged(this, finalArgs); } foreach (IBankAccount account in bankAccounts) { foreach (XmlTransaction trans in account.Transactions) { double pcc = (double)x / (double)txCount * 100; //assigns the transactionK according to the hashmap of the old key stored as custom values if (trans.CustomValues.ContainsKey(XmlTransaction.kXmlTransactionOldTransactonFK)) { object value = transactionMap[trans.CustomValues[XmlTransaction.kXmlTransactionOldTransactonFK]]; trans.BankAccountTransactionFK = value != null ? (long)value : -1L; } if (oldPercent != (int)pcc) { if (JournalLoadingPercentChanged != null) { finalArgs.Percent = (int)pcc; JournalLoadingPercentChanged(this, finalArgs); oldPercent = (int)pcc; } } Interlocked.Increment(ref x); trans.CustomValues.Clear(); trans.CustomValues = null; } } bankAccountMap = null; transactionMap = null; // CleanJournal(PurgeOptions.RemoveOrphanedAccounts | PurgeOptions.RemoveZeroBalanceAccounts); var accountCount = bankAccounts.Count; Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("\r\n Journal clean: {0} accounts and {1} transactions.", accountCount, Transactions.Count()); Console.ResetColor(); } } catch (Exception ex) { ConsoleEx.WriteAtEnd(2, ConsoleColor.Red, "[{0}]\r\n", SEconomyPlugin.Locale.StringOrDefault(79, "corrupt")); TShock.Log.ConsoleError(ex.ToString()); Console.WriteLine(SEconomyPlugin.Locale.StringOrDefault(80, "Your transaction journal appears to be corrupt and transactions have been lost.\n\nYou will start with a clean journal.\nYour old journal file has been move to SEconomy.journal.xml.gz.corrupt")); File.Move(path, path + "." + DateTime.Now.ToFileTime().ToString() + ".corrupt"); SaveJournal(); //yes there are valid uses for goto, don't judge me fool goto initPoint; } } finally { Console.WriteLine(); } return true; }