From a42d196c3c1f23d310be82e13a50fc50c7a04f6d Mon Sep 17 00:00:00 2001 From: Jkibbels Date: Sat, 9 Nov 2024 23:21:21 -0500 Subject: [PATCH] [BANKING] Some tested code that finally works for banking stuff. Much to go still, but more honed in. So much to do :( --- .../BankMgr/AccountNumberGenerator.java | 22 +- .../keeblarcraft/BankMgr/BankManager.java | 102 +++++- .../BankMgr/IndividualAccount.java | 24 +- .../keeblarcraft/BankMgr/IndividualBank.java | 240 +++++++++++--- .../keeblarcraft/Commands/BankCommands.java | 304 ++++++------------ .../keeblarcraft/ConfigMgr/ConfigManager.java | 1 + .../java/jesse/keeblarcraft/Keeblarcraft.java | 4 + 7 files changed, 429 insertions(+), 268 deletions(-) diff --git a/src/main/java/jesse/keeblarcraft/BankMgr/AccountNumberGenerator.java b/src/main/java/jesse/keeblarcraft/BankMgr/AccountNumberGenerator.java index 02f9894..fcd8187 100644 --- a/src/main/java/jesse/keeblarcraft/BankMgr/AccountNumberGenerator.java +++ b/src/main/java/jesse/keeblarcraft/BankMgr/AccountNumberGenerator.java @@ -28,16 +28,24 @@ public class AccountNumberGenerator { // Callers of this function are required to verify the given number is unique. public static String GenerateNewAccountNumber(String symbol, Integer routingNumber, Integer accountType, String username) { - String generatedAccountNumber = symbol + "-" + routingNumber + "-" + accountType + "-"; + String generatedAccountNumber = symbol + "-" + routingNumber + "-" + accountType; // Block to translate part of username into number format for (int i = 0; i < 3; i++) { if (username.length() >= 3 && username.charAt(i) <= 255) { // 255 is largest ASCII value. Might be a sloppy check; can always test later (if I find someone with non ASCII) - generatedAccountNumber += (int) username.charAt(i); + Integer asciiValueOfLetter = (int) username.charAt(i); + String strValueOfAscii = asciiValueOfLetter.toString(); + strValueOfAscii = strValueOfAscii.substring(0, 1); + + generatedAccountNumber += strValueOfAscii; } else if (username.charAt(i) <= 255) { // Case where length is less than 3 but is still in ASCII table - generatedAccountNumber += (int) username.charAt(i); + Integer asciiValueOfLetter = (int) username.charAt(i); + String strValueOfAscii = asciiValueOfLetter.toString(); + strValueOfAscii = strValueOfAscii.substring(0, 1); + + generatedAccountNumber += strValueOfAscii; } else { // Case where the length is less than 3 and is not standard ASCII - generatedAccountNumber += '0'; + generatedAccountNumber += "0"; } } @@ -59,11 +67,11 @@ public class AccountNumberGenerator { return Integer.parseInt(accountId.substring(5, 9)); } - public static Integer GetAccountNumberFromId(String accountId) { - return Integer.parseInt(accountId.substring(10, accountId.length())); + public static String GetAccountNumberFromId(String accountId) { + return accountId.substring(10); } public static Integer GetAccountTypeFromId(String accountId) { - return (int) accountId.charAt(10); + return ((int) accountId.charAt(10)) - 48; // ASCII 0 starts at 48. One must subtract 48 to be correct. } } diff --git a/src/main/java/jesse/keeblarcraft/BankMgr/BankManager.java b/src/main/java/jesse/keeblarcraft/BankMgr/BankManager.java index 8d0d634..3051980 100644 --- a/src/main/java/jesse/keeblarcraft/BankMgr/BankManager.java +++ b/src/main/java/jesse/keeblarcraft/BankMgr/BankManager.java @@ -1,30 +1,110 @@ package jesse.keeblarcraft.BankMgr; import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Map.Entry; + import jesse.keeblarcraft.ConfigMgr.ConfigManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; // The bank manager takes care of routing any and all transactions throughout the server. // It is a singleton object that is active throughout the mods lifetime and will cache players accounts -// when they log in to avoid constant look-ups through JSON (TODO for this). -// -public class BankManager { +// when they log in to avoid constant look-ups through JSON. +public final class BankManager { + private static BankManager static_inst; + + public static BankManager GetInstance() { + if (static_inst == null) { + static_inst = new BankManager(); + } + return static_inst; + } + + private static Integer KEEBLARCRAFT_SERVER_BANK_ID = 1000; + ConfigManager config = new ConfigManager(); + private HashMap banks = new HashMap(); + private HashMap bankNameFastMap = new HashMap(); - // Contains all account information for a given bank - private class BankAccountInformation { + public BankManager() {} + public void InitializeBanks() { + banks.put(KEEBLARCRAFT_SERVER_BANK_ID, new IndividualBank(Integer.toString(KEEBLARCRAFT_SERVER_BANK_ID), "KeeblarcraftGlobal")); + + // Initialize fast map + for (Entry bank : banks.entrySet()) { + bankNameFastMap.put(bank.getValue().GetBankName(), bank.getValue().GetRoutingNumber()); + } } - // Structure of all the players banking information across all banks. - private class PlayerFinances { + public List GetAllBankNames() { + List names = new ArrayList(); - // Key = Bank UUID - // Value = The account information for THIS player - HashMap allBanks; + // Iterate through all banks in the list to get their names + for (Entry bank : banks.entrySet()) { + names.add(bank.getValue().GetBankName()); + } + return names; } - public BankManager() { + public IndividualBank GetBankByRoutingNumber(Integer number) { + IndividualBank bank = null; + if (banks.containsKey(number)) { + bank = banks.get(number); + } + return bank; + } + public IndividualBank GetBankByName(String name) { + IndividualBank bank = null; + System.out.println("GetBankByName called with value " + name); + if (bankNameFastMap.containsKey(name)) { + System.out.println("Value of bank with name is " + bankNameFastMap.get(name)); + System.out.println("Banks map size is " + banks.size()); + bank = banks.get(bankNameFastMap.get(name)); + } + + System.out.println("Returning bank information"); + return bank; + } + + public void InitiateBankAccountCreation(String bankIdentifier, ServerPlayerEntity player, String accountType) { + Boolean success = false; + System.out.println("initiating bank creation"); + boolean defaultServerBank = bankIdentifier == null || bankIdentifier == ""; + System.out.println("value of bankIdentifier is " + defaultServerBank); + + System.out.println("The player name is " + player.getDisplayName().getString()); + + // DEBUG + + System.out.println("BANK NAME: " + banks.get(KEEBLARCRAFT_SERVER_BANK_ID).GetBankName()); + System.out.println("BANK BALANCE: " + banks.get(KEEBLARCRAFT_SERVER_BANK_ID).GetBankBalance()); + + // DEBUG END + + // Create an account via the server-owned bank account + if (defaultServerBank) { + success = banks.get(KEEBLARCRAFT_SERVER_BANK_ID).CreateAccount(player.getUuidAsString(), player.getDisplayName().getString(), accountType); + } else { + System.out.println("Creating bank on non-server owned bank"); + // Create an account via a specified bank identifier + Integer routingNumber = Integer.parseInt(bankIdentifier); + + if (banks.containsKey(routingNumber)) { + banks.get(routingNumber).CreateAccount(player.getUuidAsString(), player.getDisplayName().getString(), accountType); + } else { + player.sendMessage(Text.of("That bank does not exist")); + } + } + + if (success) { + player.sendMessage(Text.of("The banking operation was successful and your banking information has been updated")); + } else { + player.sendMessage(Text.of("The banking operating FAILED. You may need to visit the bank for more information!")); + } } } diff --git a/src/main/java/jesse/keeblarcraft/BankMgr/IndividualAccount.java b/src/main/java/jesse/keeblarcraft/BankMgr/IndividualAccount.java index b56cbbe..cc3fe62 100644 --- a/src/main/java/jesse/keeblarcraft/BankMgr/IndividualAccount.java +++ b/src/main/java/jesse/keeblarcraft/BankMgr/IndividualAccount.java @@ -6,32 +6,44 @@ import java.util.List; // TODO: Add ability to store NBT data of items in the future so we can store not just money but items too // like a safety deposit box public class IndividualAccount { - private Integer accountNumber; + private String accountNumber; private String accountNumberAlias; private Integer routingNumber; // Will always be the bank it's in private List accountHolders; + private List accountHolderUuids; private Integer accountBalance; private Boolean allowNegativeBalance; private Boolean accountLocked; - private String accountType; // TODO: Replace with enum in future. Valid is "checking" and "savings" right now + private Integer accountType; // TODO: Replace with enum in future. Valid is "checking" and "savings" right now public IndividualAccount() {} - public IndividualAccount(Integer accountNumber, Integer routingNumber, List holders, - Boolean allowNegativeBalance, Integer initialBalance, String alias) { + public IndividualAccount(String accountNumber, Integer routingNumber, List holders, + List accountHolderUuids, Boolean allowNegativeBalance, Integer initialBalance, + String alias, Integer accountType) { + + System.out.println("Called to create new IndividualAccount with following values: " + accountNumber + " " + routingNumber + " " + holders + " " + + accountHolderUuids + " " + allowNegativeBalance + " " + initialBalance + " " + alias + " " + accountType); this.accountNumber = accountNumber; this.routingNumber = routingNumber; this.accountHolders = holders; + this.accountHolderUuids = accountHolderUuids; this.allowNegativeBalance = allowNegativeBalance; this.accountBalance = initialBalance; this.accountNumberAlias = alias; + this.accountType = accountType; } - public void AddAccountHolder(String newHolder) { + // TODO: Make this a map in the future + public void AddAccountHolder(String newHolder, String newHolderUuid) { if (!accountHolders.contains(newHolder)) { accountHolders.add(newHolder); } + + if (!accountHolderUuids.contains(newHolderUuid)) { + accountHolderUuids.add(newHolderUuid); + } } public void AliasAccount(String newAlias) { @@ -106,7 +118,7 @@ public class IndividualAccount { return accountBalance; } - public Integer GetAccountNumber() { + public String GetAccountNumber() { return accountNumber; } diff --git a/src/main/java/jesse/keeblarcraft/BankMgr/IndividualBank.java b/src/main/java/jesse/keeblarcraft/BankMgr/IndividualBank.java index 2aea2f1..649c20c 100644 --- a/src/main/java/jesse/keeblarcraft/BankMgr/IndividualBank.java +++ b/src/main/java/jesse/keeblarcraft/BankMgr/IndividualBank.java @@ -1,12 +1,19 @@ package jesse.keeblarcraft.BankMgr; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import static java.util.Map.entry; + +import java.io.File; + import jesse.keeblarcraft.ConfigMgr.ConfigManager; +import jesse.keeblarcraft.Utils.ChatUtil; +import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR; +import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION; // Contains the information of an individual bank // @@ -18,12 +25,12 @@ public class IndividualBank { entry("savings", 1) ); - - ConfigManager config = new ConfigManager(); + private ConfigManager config = new ConfigManager(); private Integer routingNumber; // this is the banks unique identifier private Integer numberOfAccounts; // Total number of accounts the bank has. This includes only active accounts inside accountsList. - private String bankFourLetterIdentifier; private Integer maxBankAccounts = 100_000_000; // Making this simple for myself any one type of account has 8 random numbers genereated so 10^8 possible accounts + private String bankFourLetterIdentifier; + private String registeredBankName; // Think FDIC but from the servers account (keeblarcraft insurance corporation) // KBIC will ensure an amount of money based on its trustworthiness to a bank and the number of holders it has. @@ -37,87 +44,207 @@ public class IndividualBank { // Key = ACCOUNT NUMBER // Value = ACCOUNT - private HashMap accountsList; - private HashMap> accountsListFromName; // This is a list that just points to a list of account numbers by person. USEFUL + private class Accounts { + private HashMap accountsList = new HashMap(); + private HashMap> accountsListFromName = new HashMap>(); // This is a list that just points to a list of account numbers by person. USEFUL + } + + Accounts accounts; private List lockedUsers; // A list of users who are locked out of the bank and are incapable of performing more actions within it - public IndividualBank(String routingNumber) { - accountsList = new HashMap(); - accountsListFromName = new HashMap>(); - - // READ IN BANK CONFIG + public IndividualBank(String routingNumber, String nameOfBank) { + accounts = new Accounts(); + lockedUsers = new ArrayList(); + registeredBankName = nameOfBank.toUpperCase(); + bankFourLetterIdentifier = nameOfBank.substring(0, 4).toLowerCase(); + this.routingNumber = Integer.parseInt(routingNumber); + System.out.println("CREATING BANK ACCOUNT WITH ROUTING NUMBER " + routingNumber + " AND NAME " + nameOfBank); + boolean existingFile = false; + try { + // Read in the global accounts list + String accountsListDir = "bank/" + routingNumber.toString() + "/accounts/"; + System.out.println("accountsListDir + bankName is " + accountsListDir + nameOfBank); + accounts = config.GetJsonObjectFromFile(accountsListDir + nameOfBank, Accounts.class); + existingFile = true; + // TODO: REPLACE WITH SQL SERVER. DIRTY ITERATE OVER ALL FILES IN DIRECTORY TO LOAD STRUCTURE + File dir = new File(accountsListDir); + File[] allFiles = dir.listFiles(); + if (allFiles != null) { + for (File file : allFiles ) { + // First grab file identifier as KEY + String accountIdentifier = file.getName(); + String accountFromFile = accountsListDir + "/" + accountIdentifier; + System.out.println("accountIdentifier found in file is " + accountIdentifier); + System.out.println("account identifier with dir path is " + accountFromFile); + accounts.accountsList.put(accountIdentifier, config.GetJsonObjectFromFile(accountFromFile, IndividualAccount.class)); + } + } + } catch (Exception e) { + System.out.println("The try-catch in IndividualBank.java failed to complete. Printing stack trace"); + // e.printStackTrace(); + // Falling into this catch just means the file needs to be made + } + if (!existingFile) + { + System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE)); + try { + // We assume the bank dir is created by server. Create this banks dir + config.CreateDirectory("bank/" + routingNumber); + // Create this banks initial accounts dir + config.CreateDirectory("bank/" + routingNumber + "/accounts"); + // Flash initial account configuration file for this bank + FlashConfig("accounts"); + } catch (Exception e) { + System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED)); + } + } else { + System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE)); + } // A modified config reader is needed here for when each IndividualAccount is read in - the name is taken from that and is attached to the // 'accountsListFromName' structure. This makes it no worse than O(n) to fill these two structures in. // NOTE: This is an *EXPENSIVE* operation! Future us might need to update this. Also note a method is needed for everytime a player opens a new account // or gets put on one to update the map every time - for (Entry account : accountsList.entrySet()) { + for (Entry account : accounts.accountsList.entrySet()) { // We must loop over the string of holders for each account as well to make the flattened accountsListFromName map List accountHolders = account.getValue().GetAccountHolders(); // Match each user to the secondary map & add to list-value if not existing for (Integer holderIndex = 0; holderIndex < accountHolders.size(); holderIndex++) { - if (accountsListFromName.containsKey(accountHolders.get(holderIndex))) { + if (accounts.accountsListFromName.containsKey(accountHolders.get(holderIndex))) { // Case 1: User exists, update map entry - accountsListFromName.get(accountHolders.get(holderIndex)).add(account.getKey()); // Add a new account id to this person in the new flat map + accounts.accountsListFromName.get(accountHolders.get(holderIndex)).add(account.getKey()); // Add a new account id to this person in the new flat map } else { // Case 2: User does not already exist; add a new map entry - accountsListFromName.put(accountHolders.get(holderIndex), List.of(account.getKey())); // Store name as key, and new List with the value of ACCOUNT # + accounts.accountsListFromName.put(accountHolders.get(holderIndex), List.of(account.getKey())); // Store name as key, and new List with the value of ACCOUNT # } } } + numberOfAccounts = accounts.accountsList.size(); + } - numberOfAccounts = accountsList.size(); + public String GetBankName() { + return registeredBankName; + } + + public List GetAccountsOfUser(String uuid) { + System.out.println("UUID passed in: " + uuid); + List accountsFromUser = new ArrayList(); + List listOfAccounts = accounts.accountsListFromName.get(uuid); + + System.out.println("Is list of accounts null? " + listOfAccounts == null); + if (listOfAccounts != null && listOfAccounts.size() > 0) { + for (int i = 0; i < listOfAccounts.size(); i++) { + accountsFromUser.add(accounts.accountsList.get(listOfAccounts.get(i))); + } + } + return accountsFromUser; + } + + public Integer GetBankBalance() { + return bankMoney; + } + + public void AddBankBalance(Integer amount) { + bankMoney += amount; + } + + public void SubtractBankBalance(Integer amount) { + bankMoney -= amount; + } + + public void SetBankBalance(Integer amount) { + bankMoney = amount; + } + + public Boolean IsBankInsured() { + return kbicInsured; + } + + public Integer InsuranceAmount() { + Integer insuredAmnt = 0; + if (kbicInsured) { + insuredAmnt = kbicInsuredAmount; + } else { + insuredAmnt = 0; + } + return insuredAmnt; } // Updates the regular bank account list & the fast-access bank account list // NO values are allowed to be null. Manually update lists separately if that's the behaviour you want! - public void UpdateBankAccounts(String newHolderName, Integer accountIdentifier, IndividualAccount newAccountOnly) { + public void UpdateBankAccounts(String newHolderName, String newHolderUuid, String accountIdentifier, IndividualAccount newAccountOnly) { // Update the fast-access map first - if (accountsListFromName.containsKey(newHolderName)) { + if (accounts.accountsListFromName.containsKey(newHolderName)) { // Check if user is already in map - accountsListFromName.get(newHolderName).add(accountIdentifier); + System.out.println("User found in fast access map. Adding account identifier to fast-access map"); + accounts.accountsListFromName.get(newHolderUuid).add(accountIdentifier); } else { // Add new entry to map - accountsListFromName.put(newHolderName, List.of(accountIdentifier)); + System.out.println("User not found in fast access map. Adding completely new entry"); + accounts.accountsListFromName.put(newHolderUuid, List.of(accountIdentifier)); } // Update regular account list - if (accountsList.containsKey(accountIdentifier)) { + if (accounts.accountsList.containsKey(accountIdentifier)) { // This path assumes we are adding a holder as opposed to adding an account (else, how else would this work?) - accountsList.get(accountIdentifier).AddAccountHolder(newHolderName); + System.out.println("Account found in accounts list, adding this person as a holder instead"); + accounts.accountsList.get(accountIdentifier).AddAccountHolder(newHolderName, newHolderUuid); } else { // Non-existent account means a new one! - accountsList.put(accountIdentifier, newAccountOnly); + System.out.println("Brand new account creation, adding!"); + accounts.accountsList.put(accountIdentifier, newAccountOnly); numberOfAccounts++; } + + System.out.println("Flashing configuration file"); + FlashConfig("bank/" + routingNumber + "/accounts"); } - public Boolean CreateAccount(String holderName, String accountTypeStr) { + public Integer GetRoutingNumber() { + return this.routingNumber; + } + + public Boolean CreateAccount(String holderUuid, String holderName, String accountTypeStr) { Boolean success = false; - if (accountsList.size() <= maxBankAccounts && ACCOUNT_TYPES.containsKey(accountTypeStr.toLowerCase())) { + System.out.println("Attempting to create new bank account given args UUID / NAME / TYPE : " + holderUuid + " " + holderName + " " + accountTypeStr); + System.out.println("accounts size is " + accounts.accountsList.size()); + System.out.println("account string is { " + accountTypeStr + " }. Does this account type exist in this bank? => " + ACCOUNT_TYPES.containsKey(accountTypeStr.toLowerCase())); + if (accounts.accountsList.size() <= maxBankAccounts && ACCOUNT_TYPES.containsKey(accountTypeStr.toLowerCase())) { // Verify this isn't a blacklisted user + System.out.println("Is user bank locked? " + lockedUsers.contains(holderName)); if (!lockedUsers.contains(holderName)) { - Integer maxAttempts = 1000; // Reasonably a unique bank account should pop up within 1000 generations. If not, the user may try again. + Integer maxAttempts = 15; // Reasonably a unique bank account should pop up within 1000 generations. If not, the user may try again. String accountId = AccountNumberGenerator.GenerateNewAccountNumber(bankFourLetterIdentifier, routingNumber, ACCOUNT_TYPES.get(accountTypeStr), holderName); + System.out.println("Account generator came back with bank account id { " + accountId + " }"); + System.out.println("4 letter bank: " + AccountNumberGenerator.GetFinancialSymbolFromId(accountId)); + System.out.println("Routing: " + AccountNumberGenerator.GetRoutingNumberFromId(accountId)); + System.out.println("Account type: " + AccountNumberGenerator.GetAccountTypeFromId(accountId)); + System.out.println("RNG Account number: " + AccountNumberGenerator.GetAccountNumberFromId(accountId)); + // TODO: Fix in future with a method that will guarentee a one-time necessary number generator. Statistically speaking; this will be okay for the // entire life time of the server. BUT, you never know! - while (maxAttempts != 0 && !accountsList.containsKey(AccountNumberGenerator.GetAccountNumberFromId(accountId))) { + while (maxAttempts != 0 && !accounts.accountsList.containsKey(AccountNumberGenerator.GetAccountNumberFromId(accountId))) { accountId = AccountNumberGenerator.GenerateNewAccountNumber(bankFourLetterIdentifier, routingNumber, ACCOUNT_TYPES.get(accountTypeStr), holderName); + System.out.println("Account generator came back with bank account id { " + accountId + " }"); maxAttempts--; } // Final check to add the account - Integer actualAccountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); - if (!accountsList.containsKey(actualAccountNumber)) { - IndividualAccount newAccount = new IndividualAccount(actualAccountNumber, this.routingNumber, List.of(holderName), false, 0, ""); - UpdateBankAccounts(holderName, actualAccountNumber, newAccount); + String actualAccountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); + System.out.println("Bank account identifier is { " + actualAccountNumber + " }. Is this already an existing account? " + accounts.accountsList.containsKey(actualAccountNumber)); + if (!accounts.accountsList.containsKey(actualAccountNumber)) { + IndividualAccount newAccount = new IndividualAccount(actualAccountNumber, this.routingNumber, List.of(holderName), + List.of(holderUuid), false, 0, + "", ACCOUNT_TYPES.get(accountTypeStr)); + System.out.println("Updating accounts list for this bank"); + UpdateBankAccounts(holderName, holderUuid, actualAccountNumber, newAccount); success = true; } } @@ -126,9 +253,9 @@ public class IndividualBank { } public void AliasAccount(String accountId, String newAlias) { - Integer accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); - if (accountsList.containsKey(accountNumber)) { - accountsList.get(accountNumber).AliasAccount(newAlias); + String accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); + if (accounts.accountsList.containsKey(accountNumber)) { + accounts.accountsList.get(accountNumber).AliasAccount(newAlias); } } @@ -136,8 +263,8 @@ public class IndividualBank { Boolean success = false; Integer accountIter = 0; - for (Entry> holderAccounts : accountsListFromName.entrySet()) { - accountsList.get(holderAccounts.getValue().get(accountIter++)).LockAccount(); + for (Entry> holderAccounts : accounts.accountsListFromName.entrySet()) { + accounts.accountsList.get(holderAccounts.getValue().get(accountIter++)).LockAccount(); } return success; } @@ -145,20 +272,53 @@ public class IndividualBank { public Boolean CloseAccount(String accountId) { Boolean success = false; - Integer accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); + String accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId); - if (accountsList.get(accountNumber).GetAccountBalance() == 0) { - accountsList.remove(accountNumber); + if (accounts.accountsList.get(accountNumber).GetAccountBalance() == 0) { + accounts.accountsList.remove(accountNumber); success = true; } return success; } - public Boolean HasAccount(Integer accountIdentifier) { + public Boolean HasAccount(String accountIdentifier) { Boolean containsAccount = false; - if (accountsList.containsKey(accountIdentifier)) { + if (accounts.accountsList.containsKey(accountIdentifier)) { containsAccount = true; } return containsAccount; } + + ///////////////////////////////////////////////////////////////////////////// + /// @fn FlashConfig + /// + /// @brief Flashes the config to the disk + /// + /// @note dirName should be the relative full path to dir + /// @note Function will be removed in near future for SQL but is + /// expensive to run as it flashes everything even if un-updated + ///////////////////////////////////////////////////////////////////////////// + public void FlashConfig(String dirName) { + for (Entry singleAccount : accounts.accountsList.entrySet()) { + + // Iterate over each one & verify if a file exists inside the dir. if it does; + // nuke it and + // replace it with the new contents in memory + String accountNum = singleAccount.getKey().toString(); + + // delete file + File file = new File(dirName + "/" + accountNum + ".json"); + if (file.exists()) { + file.delete(); + } + + // Re-flash file + try { + config.WriteToJsonFile(dirName + "/" + accountNum + ".json", singleAccount.getValue()); + } catch (FILE_WRITE_EXCEPTION e) { + System.out.println("Failed to flash configuration file properly. Printing stack trace"); + e.printStackTrace(); + } + } + } } \ No newline at end of file diff --git a/src/main/java/jesse/keeblarcraft/Commands/BankCommands.java b/src/main/java/jesse/keeblarcraft/Commands/BankCommands.java index 9048758..45cb768 100644 --- a/src/main/java/jesse/keeblarcraft/Commands/BankCommands.java +++ b/src/main/java/jesse/keeblarcraft/Commands/BankCommands.java @@ -1,15 +1,17 @@ package jesse.keeblarcraft.Commands; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.LongArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import java.util.List; import java.util.ArrayList; import java.util.Map; +import java.util.Map.Entry; + import static java.util.Map.entry; import jesse.keeblarcraft.BankMgr.BankManager; +import jesse.keeblarcraft.BankMgr.IndividualAccount; +import jesse.keeblarcraft.BankMgr.IndividualBank; import jesse.keeblarcraft.ConfigMgr.ConfigManager; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; @@ -84,91 +86,19 @@ public class BankCommands { // is just /bank ACTION-WORD [arguments...] [optionals] var bankRoot = CommandManager.literal("bank").build(); - var actionWord = CommandManager.argument("ACTION_WORD", StringArgumentType.greedyString()).build(); - + // Removing "argList" and just running the entire list as an argList from actionWord makes `/bank help` possible var argList = CommandManager.argument("ARG_LIST", StringArgumentType.greedyString()) .executes(context -> ParseBankCommand ( context, - StringArgumentType.getString(context, "ACTION_WORD"), StringArgumentType.getString(context, "ARG_LIST")) ) .build(); // Building the argument tree here dispatcher.getRoot().addChild(bankRoot); - bankRoot.addChild(actionWord); bankRoot.addChild(argList); - - // Aliases - dispatcher.register(CommandManager.literal("wire").redirect(actionWord)); - dispatcher.register(CommandManager.literal("balance").redirect(actionWord)); }); - - - // Command: "/getbalance" - // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - // dispatcher.register(CommandManager.literal("getbalance") - // .executes(context -> GetBalance(context))); - // }); - - // // Command: "/setbalance || rootSetBalance" - // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - // final var rootSetBalance = dispatcher.register(CommandManager.literal("setbalance") - // .then(CommandManager.argument("newBalance", IntegerArgumentType.integer()) - // .then(CommandManager.argument("reason", StringArgumentType.string()) - // .then(CommandManager.argument("otherParty", StringArgumentType.string()) - // .executes(context -> SetBalance( - // IntegerArgumentType.getInteger(context, "newBalance"), - // StringArgumentType.getString(context, "reason"), - // StringArgumentType.getString(context, "otherParty"), - // context)))))); - - // dispatcher.register(CommandManager.literal("setbalance").redirect(rootSetBalance)); - // }); - - // // Command: "/AddMoney" - // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - // dispatcher.register(CommandManager.literal("AddMoney") - // .then(CommandManager.argument("reason", StringArgumentType.string()) - // .then(CommandManager.argument("payment", LongArgumentType.longArg()) - // .then(CommandManager.argument("otherParty", StringArgumentType.string()) - // .executes(context -> AddMoney( - // StringArgumentType.getString(context, "reason"), - // LongArgumentType.getLong(context, "payment"), - // StringArgumentType.getString(context, "otherParty"), - // context)))))); - // }); - - // // Command: "/subtractbalance || /SubtractBalance" - // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - // final var SubtractBalance = dispatcher.register(CommandManager.literal("subtractbalance") - // .then(CommandManager.argument("reason", StringArgumentType.string()) - // .then(CommandManager.argument("payment", LongArgumentType.longArg()) - // .then(CommandManager.argument("otherParty", StringArgumentType.string()) - // .executes(context -> SubtractBalance( - // StringArgumentType.getString(context, "reason"), - // LongArgumentType.getLong(context, "payment"), - // StringArgumentType.getString(context, "otherParty"), - // context)))))); - - // dispatcher.register(CommandManager.literal("subtractbalance").redirect(SubtractBalance)); - // }); - - // // Command: "/wire || /sendmoney" - // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { - // final var sendmoney = dispatcher.register(CommandManager.literal("wire") - // .then(CommandManager.argument("reason", StringArgumentType.string()) - // .then(CommandManager.argument("payment", LongArgumentType.longArg()) - // .then(CommandManager.argument("otherParty", StringArgumentType.string()) - // .executes(context -> Wire( - // StringArgumentType.getString(context, "reason"), - // LongArgumentType.getLong(context, "payment"), - // StringArgumentType.getString(context, "otherParty"), - // context)))))); - - // dispatcher.register(CommandManager.literal("wire").redirect(sendmoney)); - // }); } // Remove filler words which only exist for human readability @@ -178,21 +108,28 @@ public class BankCommands { if (FILLER_WORDS.contains(str)) { argList.remove(index); } + + // Also make every word lower case + str = str.toLowerCase(); index++; } return argList; } - public int ParseBankCommand(CommandContext context, String actionWord, String unformattedArgList) { - // String[] formattedArgList = unformattedArgList.split("\\s+"); // REGEX is matching 1+ whitespace characters - List formattedArgList = List.of(unformattedArgList.split("\\s+")); + public int ParseBankCommand(CommandContext context, String unformattedArgList) { + List formattedArgList = List.of(unformattedArgList.split("\\s+")); // REGEX is matching 1+ whitespace characters + System.out.println("After formatting the arg list, the size of arg list is size { " + formattedArgList.size() + " }"); formattedArgList = RemoveFillerWords(formattedArgList); - actionWord = actionWord.toLowerCase(); + + String actionWord = formattedArgList.get(0); + System.out.println("The action word for this operation is " + actionWord.toUpperCase()); + System.out.println("Parsing bank command on action word { " + actionWord + " } with arg list { " + formattedArgList + " }"); switch(actionWord) { case "create": - CreateCommand(context.getSource().getPlayer(), formattedArgList); + System.out.println("Calling create command..."); + CreateCommand(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size())); break; case "close": CloseCommand(context.getSource().getPlayer(), formattedArgList); @@ -207,7 +144,7 @@ public class BankCommands { BalanceCommand(context.getSource().getPlayer(), formattedArgList); break; case "help": - HelpCommand(context.getSource().getPlayer(), "help"); + HelpCommand(context.getSource().getPlayer(), formattedArgList); break; case "move": MoveCommand(context.getSource().getPlayer(), formattedArgList); @@ -219,10 +156,17 @@ public class BankCommands { ReportCommand(context.getSource().getPlayer(), formattedArgList); break; case "examples": - HelpCommand(context.getSource().getPlayer(), "examples"); + HelpCommand(context.getSource().getPlayer(), List.of("examples")); break; case "syntax": - HelpCommand(context.getSource().getPlayer(), "syntax"); + HelpCommand(context.getSource().getPlayer(), List.of("syntax")); + break; + case "list": + ListAllBanks(context.getSource().getPlayer(), formattedArgList); + break; + case "account": + case "accounts": + ManageAccounts(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size())); break; default: if (context.getSource().isExecutedByPlayer()) { @@ -234,7 +178,42 @@ public class BankCommands { } // Possible code paths: - // REQUIRED (path 1) = {AMOUNT} [FROM:{INTERNAL ACCOUNT #|ALIAS}] TO:{INTERNAL ACCOUNT #|ALIAS} ***Note: TO{} can be assumed to be SELECTED default if not specified + // REQUIRED = {Routing # or Bank name} + // OPTIONAL = [] + public void ManageAccounts(ServerPlayerEntity sourcePlayer, List argList) { + System.out.println("Manage accounts arg list is { " + argList + " }"); + if (argList.size() > 0) { + // TODO: For now we assume they reference the bank name and not routing # + Boolean success = false; + String bankName = argList.get(0).toUpperCase(); + IndividualBank bank = BankManager.GetInstance().GetBankByName(bankName); + System.out.println("Test print on memory"); + Boolean isNull = bank == null; + System.out.println("isNull: " + isNull); + if (bank != null) { + System.out.println("Grabbing user account information"); + List userAccounts = bank.GetAccountsOfUser(sourcePlayer.getUuidAsString()); + sourcePlayer.sendMessage(Text.of("------------[BANK INFO FOR " + bankName.toUpperCase() + "]------------")); + for (int i = 0; i < userAccounts.size(); i++) { + sourcePlayer.sendMessage(Text.of("ACCOUNT NUMBER: " + userAccounts.get(i).GetAccountNumber())); + sourcePlayer.sendMessage(Text.of("HOLDERS: " + userAccounts.get(i).GetAccountHolders())); + sourcePlayer.sendMessage(Text.of("BALANCE: " + userAccounts.get(i).GetAccountBalance())); + sourcePlayer.sendMessage(Text.of("\n")); + } + } else { + sourcePlayer.sendMessage(Text.of("That bank does not exist")); + } + } else { + sourcePlayer.sendMessage(Text.of("Unrecognized move command. Please use \"/bank help ACCOUNTS\" for more information.")); + } + } + + public void ListAllBanks(ServerPlayerEntity sourcePlayer, List argList) { + sourcePlayer.sendMessage(Text.of("Here is a list of available banks on the server: " + BankManager.GetInstance().GetAllBankNames())); + } + + // Possible code paths: + // REQUIRED (path 1) = {AMOUNT} [FROM:{INTERNAL ACCOUNT #|ALIAS}] TO:{INTERNAL ACCOUNT #|ALIAS} ***Note: can be assumed to be SELECTED default if not specified // REQUIRED (path 2) = {INTERNAL ACCOUNT #|ALIAS} {EXTERNAL BANK/FACTION ID} // OPTIONAL = [] public void MoveCommand(ServerPlayerEntity sourcePlayer, List argList) { @@ -327,8 +306,12 @@ public class BankCommands { // Create command - valid arg list types: // /... CREATE {CA/checking/savings/checking-account/savings-account/report} public int CreateCommand(ServerPlayerEntity sourcePlayer, List argList) { + System.out.println("Attempting account creation given arg list { " + argList + " }."); + System.out.println("argList size is " + argList.size()); + if (argList.size() > 0) { String action = argList.get(0).toLowerCase(); + System.out.println("action word is " + action); switch (action) { // Checking & Savings are handled in the same function so we can just "alias" all of these to the same call! @@ -338,14 +321,15 @@ public class BankCommands { case "sa": case "savings": case "savings-account": - argList.remove(0); // Remove action word (all the above aliases) + System.out.println("Creating account. Removing index 0 in argList. New argList is { " + argList + " }"); CreateAccount(sourcePlayer, argList); break; case "rep": case "report": - argList.remove(0); // Remove action word (all the above aliases) GenerateAccountReport(sourcePlayer, argList); break; + default: + // Unrecognized creation type } } else { sourcePlayer.sendMessage(Text.of("Unrecognized create command formed on bank. Please run \"/bank help CREATE\" for more information")); @@ -415,7 +399,6 @@ public class BankCommands { * * * CALL THROUGHS TO BANK MGR HERE - * CALL THROUGHS TO BANK MGR HERE * */ } else { @@ -427,22 +410,33 @@ public class BankCommands { // Possible code paths: // REQUIRED = {} // OPTIONAL = [command|subcommand] - public int HelpCommand(ServerPlayerEntity sourcePlayer, String helpCommand) { - if (helpCommand.length() > 0) { - // Specific command help message - - /* - * - * A ChatUtil message needs to be created to better display messages to players. Once that is created; forward this string to that - * - */ + public int HelpCommand(ServerPlayerEntity sourcePlayer, List helpCommand) { + System.out.println("Running help command"); + // TODO: Make a prettier way of printing this to the player with color + if (helpCommand.size() == 0) { + // General help command + for (Entry helpCmd : HELP_COMMANDS.entrySet()) { + sourcePlayer.sendMessage(Text.of(helpCmd.getKey() + " --> " + helpCmd.getValue())); + sourcePlayer.sendMessage(Text.of("\n")); + } } else { - // General help message - /* - * - * A ChatUtil message needs to be created to better display messages to players. Once that is created; forward this string to that - * - */ + // Iterate over list; verifying what commands are found + List unknownCmds = new ArrayList(); + for (int i = 0; i < helpCommand.size(); i++) { + String newHelpCmd = helpCommand.get(i); + if (HELP_COMMANDS.containsKey(newHelpCmd)) { + // Print help for this specific command + sourcePlayer.sendMessage(Text.of(newHelpCmd + " -> " + HELP_COMMANDS.get(newHelpCmd))); + } else { + // Add to unknown list at end + unknownCmds.add(newHelpCmd); + } + } + + // After all prints have finished tell the player the commands they plugged in that we did not recognize + if (unknownCmds.size() > 0) { + sourcePlayer.sendMessage(Text.of("The following commands do not exist or were mispelt: " + unknownCmds)); + } } return 0; } @@ -450,6 +444,7 @@ public class BankCommands { // Possible args: // /bank create {SAVINGS/CHECKING} [optional: alias] public void CreateAccount(ServerPlayerEntity player, List accountArgs) { + System.out.println("Attempting to create checking account with arg list { " + accountArgs + " }"); if (accountArgs.size() > 0) { String accountType = accountArgs.get(0); String accountAlias = "NO_ALIAS"; @@ -459,12 +454,9 @@ public class BankCommands { accountAlias = accountArgs.get(1).toLowerCase(); } - /* - * - * Finally - calls to BankManager here for more - * - */ - + System.out.println("ACCOUNT CREATION REQUESTED WITH TYPE " + accountType + " AND ALIAS " + accountAlias); + // FIXME: This is just a patch at the moment to let players make bank accounts in server bank + BankManager.GetInstance().InitiateBankAccountCreation(null, player, accountType); } else { player.sendMessage(Text.of("Unrecognized create account command. Please run /bank help create for more information")); } @@ -486,100 +478,4 @@ public class BankCommands { player.sendMessage(Text.of("Unrecognized report field data. Please run /bank help report for more information")); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - // public Integer GetBalance(CommandContext context) { - // Integer ret = -1; - - // if (context.getSource().isExecutedByPlayer()) { - // ServerPlayerEntity player = context.getSource().getPlayer(); - // BankManager playerBank = new BankManager(player.getUuidAsString()); - // player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance()))); - // return 0; - // } - - // return ret; - // } - - // //SetBalance will be a ServerCommand only Perm level Op - // public Integer SetBalance(Integer newBalance, String reason, String otherParty, CommandContext context) { - // Integer ret = -1; - - // if (context.getSource().hasPermissionLevel(1)) { - // ServerPlayerEntity player = context.getSource().getPlayer(); - // BankManager playerBank = new BankManager(player.getUuidAsString()); - // playerBank.SetBalance(newBalance, reason, otherParty); - // player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance()))); - // } - - // return ret; - // } - - // //AddMoney will be a ServerCommand Only Perm level Op - // public Integer AddMoney(String reason, long payment, String otherParty, CommandContext context) { - // Integer ret = -1; - - // if (context.getSource().hasPermissionLevel(1)) { - // ret = 0; - // ServerPlayerEntity player = context.getSource().getPlayer(); - // BankManager playerBank = new BankManager(player.getUuidAsString()); - // playerBank.AddMoney(reason, payment, otherParty); - // player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance()))); - // } - - // return ret; - // } - - // //SubtractBalance will be a ServerCommand Perm leve Op - // public Integer SubtractBalance(String reason, long payment, String otherParty, CommandContext context) { - // Integer ret = -1; - - // if (context.getSource().hasPermissionLevel(1)) { - // ret = 0; - // ServerPlayerEntity player = context.getSource().getPlayer(); - // BankManager playerBank = new BankManager(player.getUuidAsString()); - // playerBank.SubtractBalance(reason, payment, otherParty); - // player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance()))); - // } - - // return ret; - // } - - // public Integer Wire(String reason, long payment, String otherParty, CommandContext context) { - // Integer ret = -1; - - // if (context.getSource().isExecutedByPlayer()) { - // ret = 0; - // ServerPlayerEntity player = context.getSource().getPlayer(); - // BankManager playerBank = new BankManager(player.getUuidAsString()); - // playerBank.Wire(reason, payment, otherParty); - // player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance()))); - // } - - // return ret; - // } } diff --git a/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java b/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java index 32edb8b..a9a6174 100644 --- a/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java +++ b/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java @@ -182,6 +182,7 @@ public class ConfigManager { Boolean ret = false; File dir = new File(dirName); + System.out.println("Attempting to create dir with name " + dirName); try { if ( ! dir.exists()) { diff --git a/src/main/java/jesse/keeblarcraft/Keeblarcraft.java b/src/main/java/jesse/keeblarcraft/Keeblarcraft.java index bdf735c..ac005aa 100644 --- a/src/main/java/jesse/keeblarcraft/Keeblarcraft.java +++ b/src/main/java/jesse/keeblarcraft/Keeblarcraft.java @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import jesse.keeblarcraft.AttributeMgr.AttributeMgr; import jesse.keeblarcraft.AttributeMgr.AttributeTree; +import jesse.keeblarcraft.BankMgr.BankManager; import jesse.keeblarcraft.Commands.CustomCommandManager; import jesse.keeblarcraft.CustomBlocks.BlockList; import jesse.keeblarcraft.CustomItems.ItemManager; @@ -90,6 +91,9 @@ public class Keeblarcraft implements ModInitializer { // Register attributes AttributeMgr.RegisterAttributes(); + // Register the banking system + BankManager.GetInstance().InitializeBanks(); + /// THE BELOW ITEMS MUST BE DONE LAST IN THE STEPS // Register items