Compare commits

..

11 Commits

Author SHA1 Message Date
Jkibbels
f7126a0555 [MIXED] Fixed world gen inventory moving + spawn setting
Some checks failed
build / build (21) (push) Has been cancelled
2024-12-05 22:37:27 -05:00
Jkibbels
30ec133358 last minute pre-ship stuff
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-29 04:00:14 -05:00
Jkibbels
b4234d4b6b normalized config files to keeblarcraft config dir inside config
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-24 15:06:34 -05:00
Jkibbels
b048e34eda [MIXED] Some late state banking stuff but also critical first-day features needed for spawn building such as DIMENSION and BIOME additions. FACTIONS also begun implemented
Some checks are pending
build / build (21) (push) Waiting to run
2024-11-23 22:06:00 -05:00
Jkibbels
afbb2533e7 [BANKING] Bug fixing on account number having extra digit + formatting colors for help messages. Includes setting balances with admin command too
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-16 22:56:52 -05:00
Jkibbels
8ece2f2d45 [BANKING] Additions in banking system in order to start getting balance changes... **UNTESTED**
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-13 20:56:20 -05:00
Jkibbels
3abdf9b184 [BANKING] Small bits for future
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-10 01:01:44 -05:00
Jkibbels
a42d196c3c [BANKING] Some tested code that finally works for banking stuff. Much to go still, but more honed in. So much to do :(
Some checks are pending
build / build (21) (push) Waiting to run
2024-11-09 23:21:21 -05:00
Jkibbels
87afc5c8ab [BANKING] Fleshing out more banking information. Logic to populate fast-access bank account list added UNTESTED
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-06 20:01:41 -05:00
Jkibbels
d8a6b7ecd9 [issue/chat-system] GLOBAL CONTENT git br This branch is HIJACKED for all features now until release of server. Inclusion of more financial system; transaction data; and banking information
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-04 19:45:28 -05:00
Jkibbels
5cae7ec313 [issue/chat-system] **UNRELATED** Commit with necessary changes to the banking system for it to be robust enough on release of the server. ONLY banking system changes in here! COMMAND ONLY. NOTHING HOOKED UP YET IN BANK MANAGER!
Some checks failed
build / build (21) (push) Has been cancelled
2024-11-02 04:25:37 -04:00
39 changed files with 3628 additions and 445 deletions

View File

@ -10,6 +10,10 @@ base {
archivesName = project.archives_base_name
}
fabricApi {
configureDataGeneration()
}
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
@ -22,6 +26,8 @@ repositories {
// includeGroup("cc.tweaked")
// }
// }
maven {url = "https://maven.kyrptonaught.dev"}
maven { url = 'https://maven.minecraftforge.net/' } // for Terrablender
}
loom {
@ -33,7 +39,6 @@ loom {
sourceSet sourceSets.client
}
}
}
dependencies {
@ -46,6 +51,9 @@ dependencies {
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
//modCompileOnly "cc.tweaked:cc-tweaked-1.20-fabric-api:1.105.0"
//modRuntimeOnly "cc.tweaked:cc-tweaked-1.20-fabric-api:1.105.0"
modImplementation 'net.kyrptonaught:customportalapi:0.0.1-beta65-1.20'
include 'net.kyrptonaught:customportalapi:0.0.1-beta65-1.20'
modImplementation 'com.github.glitchfiend:TerraBlender-fabric:1.20.1-3.0.1.7'
}
processResources {

View File

@ -1,4 +1,5 @@
#!/bin/bash
rm ~/.minecraft/mods/keeblarcraft* # What else could be named this? Really now?
cp build/devlibs/*sources.jar ~/.minecraft/mods
cp build/libs/keeblarcraft-0.0.1.jar ~/.minecraft/mods
cp build/libs/keeblarcraft-0.0.1.jar run/mods
echo Moved minecraft to mods dir

View File

@ -0,0 +1,3 @@
// 1.20 2024-12-05T19:59:23.022622275 keeblarcraft/Keeblarcraft World Generation
afc3340283d1101601bd4d2ca96341a58eceaf83 data/keeblarcraft/dimension_type/keeblarcraftdim_type.json
4398eda2b0c28b2c754c45f5805534bf1921b243 data/keeblarcraft/worldgen/biome/test_biome.json

View File

@ -0,0 +1,26 @@
{
"ambient_light": 0.5,
"bed_works": true,
"coordinate_scale": 1.0,
"effects": "minecraft:overworld",
"fixed_time": 12750,
"has_ceiling": false,
"has_raids": false,
"has_skylight": true,
"height": 480,
"infiniburn": "#minecraft:infiniburn_overworld",
"logical_height": 256,
"min_y": 0,
"monster_spawn_block_light_limit": 0,
"monster_spawn_light_level": {
"type": "minecraft:uniform",
"value": {
"max_inclusive": 0,
"min_inclusive": 0
}
},
"natural": false,
"piglin_safe": false,
"respawn_anchor_works": false,
"ultrawarm": false
}

View File

@ -0,0 +1,99 @@
{
"carvers": {
"air": [
"minecraft:cave",
"minecraft:cave_extra_underground",
"minecraft:canyon"
]
},
"downfall": 0.6,
"effects": {
"fog_color": 1800383,
"foliage_color": 13763580,
"grass_color": 1818548,
"sky_color": 11803583,
"water_color": 15414436,
"water_fog_color": 1800383
},
"features": [
[],
[
"minecraft:lake_lava_underground",
"minecraft:lake_lava_surface"
],
[
"minecraft:amethyst_geode",
"minecraft:forest_rock"
],
[],
[],
[],
[
"minecraft:ore_dirt",
"minecraft:ore_gravel",
"minecraft:ore_granite_upper",
"minecraft:ore_granite_lower",
"minecraft:ore_diorite_upper",
"minecraft:ore_diorite_lower",
"minecraft:ore_andesite_upper",
"minecraft:ore_andesite_lower",
"minecraft:ore_tuff"
],
[],
[
"minecraft:spring_water",
"minecraft:spring_lava"
],
[
"minecraft:glow_lichen",
"minecraft:trees_plains",
"minecraft:forest_flowers",
"minecraft:patch_large_fern",
"minecraft:brown_mushroom_normal",
"minecraft:red_mushroom_normal",
"minecraft:patch_sugar_cane",
"minecraft:patch_pumpkin"
],
[
"minecraft:freeze_top_layer"
]
],
"has_precipitation": true,
"spawn_costs": {},
"spawners": {
"ambient": [],
"axolotls": [],
"creature": [
{
"type": "minecraft:sheep",
"maxCount": 4,
"minCount": 4,
"weight": 12
},
{
"type": "minecraft:pig",
"maxCount": 4,
"minCount": 4,
"weight": 10
},
{
"type": "minecraft:chicken",
"maxCount": 4,
"minCount": 4,
"weight": 10
},
{
"type": "minecraft:cow",
"maxCount": 4,
"minCount": 4,
"weight": 8
}
],
"misc": [],
"monster": [],
"underground_water_creature": [],
"water_ambient": [],
"water_creature": []
},
"temperature": 0.7
}

View File

@ -0,0 +1,77 @@
package jesse.keeblarcraft.BankMgr;
import java.util.Random;
/*
Account number composition:
EXAMPLE:
KCSB-9284-0JKI94358732
EXPLANATION:
The composition of the above number is mostly not random. The composition is 'C' = LETTER, and the first 4 represent the 4-letter combination
of the financial establishment (think of the stock market NASDAQ). The first 4 '#' (# = Number) digits represent the ROUTING number of the bank.
The next '#' (in its own brackets) represents ACCOUNT TYPE. This means the mod supports up to 9 configurations; some examples would be CHECKING
or SAVINGS. The next three '#' (in their own brackets too) will be the first three letters of the username of the account holder inside an ASCII
table. If the letter is not UTF 8 compatible - then the default will be '0'. The LAST eight '#' (not in brackets) are randomly generated via Java.
* Please note that the brackets in this example are only for demonstration of separating numbers from each other. They do not show up at all in generation.
CCCC-####-[#][###]########
* Additional note - enums are not implicitly integers under the hood like C++; so, a financial establishment could pass in ANY 0-9 digit for "account type" and
each establishment is allowed to treat that however they want. In one banks case it make checking accounts 3; but in anothers it's 4. This is a general programming
note for now. It is probably best to keep this consistent in your server/mod so tellers can understand them universally.
*/
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;
// 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)
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
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";
}
}
// Guarentee an 8 digit number. 10000000 is floor and max rng int is at most 89999999. Combined they total 99999999
Random rng = new Random();
Integer randInteger = 10000000 + rng.nextInt(89999999);
generatedAccountNumber += randInteger;
return generatedAccountNumber;
}
// the below functions must be given a key generated from the above function or they will combust into
// not less than one million pieces! :)
public static String GetFinancialSymbolFromId(String accountId) {
return accountId.substring(0, 4);
}
public static Integer GetRoutingNumberFromId(String accountId) {
return Integer.parseInt(accountId.substring(5, 9));
}
public static String GetAccountNumberFromId(String accountId) {
return accountId.substring(10);
}
public static Integer GetAccountTypeFromId(String accountId) {
return ((int) accountId.charAt(10)) - 48; // ASCII 0 starts at 48. One must subtract 48 to be correct.
}
}

View File

@ -1,256 +1,223 @@
package jesse.keeblarcraft.BankMgr;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map.Entry;
import jesse.CommonServerUtils;
import org.apache.logging.log4j.core.jmx.Server;
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;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class BankManager {
BankManagerFile bankInfo = new BankManagerFile(); // not sure why we make another one but i guess so we
ConfigManager config = new ConfigManager(); // for read and write privs
CommonServerUtils commonServerUtils = new CommonServerUtils();
// 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.
public final class BankManager {
private static BankManager static_inst;
public BankManager(String uuid) {
Boolean existingFile = false;
try {
bankInfo = config.GetJsonObjectFromFile("bank/" + uuid + ".json", BankManagerFile.class);
existingFile = true;
} catch (Exception e) {
// Do nothing. This means the file does not exist
public static BankManager GetInstance() {
if (static_inst == null) {
static_inst = new BankManager();
}
return static_inst;
}
// In the event the above code failed out, this means a new file has to be
// created for the player's uuid
if (!existingFile) {
System.out.println(ChatUtil.ColoredString("Trying to create new file", CONSOLE_COLOR.BLUE));
try {
FlashConfig(bankInfo.uuid);
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED));
private class PlayerBankConfig {
List<String> activeBanks = new ArrayList<String>(); // List of all banks a player has accounts in
String defaultSelectedBank;
}
// Key = player uuid
// Val = player config
HashMap<String, PlayerBankConfig> playerConfigs = new HashMap<String, PlayerBankConfig>(); // Stores global detail information for bank mgr to use
private static Integer KEEBLARCRAFT_SERVER_BANK_ID = 1000; // Server global bank (default bank on server)
ConfigManager config = new ConfigManager();
// KEY = Bank routing number
// Val = Bank object
private HashMap<Integer, IndividualBank> banks = new HashMap<Integer, IndividualBank>();
// KEY = Bank name
// Val = Bank routing number
private HashMap<String, Integer> bankNameFastMap = new HashMap<String, Integer>();
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<Integer, IndividualBank> bank : banks.entrySet()) {
bankNameFastMap.put(bank.getValue().GetBankName(), bank.getValue().GetRoutingNumber());
}
}
public List<String> GetAllBankNames() {
List<String> names = new ArrayList<String>();
// Iterate through all banks in the list to get their names
for (Entry<Integer, IndividualBank> bank : banks.entrySet()) {
names.add(bank.getValue().GetBankName());
}
return names;
}
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 ChangeDefaultPlayerAccount(ServerPlayerEntity player, String newDefaultAccount) {
String bankName = AccountNumberGenerator.GetFinancialSymbolFromId(newDefaultAccount);
System.out.println("ChangeDefaultPlayerAccount: Received bankName " + bankName);
System.out.println(bankNameFastMap);
// Verify bank exists first
if (bankNameFastMap.containsKey(bankName)) {
Integer routNum = bankNameFastMap.get(bankName);
IndividualBank bank = banks.get(routNum);
// Verify this person has access to this account
if(bank.IsAccountHolder(newDefaultAccount, player.getUuidAsString())) {
// Finally update config to this account since checks pass
playerConfigs.get(player.getUuidAsString()).defaultSelectedBank = newDefaultAccount;
}
} else {
System.out.println(ChatUtil.ColoredString("Moving on", CONSOLE_COLOR.BLUE));
}
if ("".equals(bankInfo.uuid)) {
System.out.println(ChatUtil.ColoredString("Assigning new config file for this uuid. No previous existing",
CONSOLE_COLOR.BLUE));
bankInfo.uuid = uuid;
player.sendMessage(Text.of("Could not change default selected bank. Bank does not exist!"));
}
}
//this class is the structure for the hashmap in BankManagerFile
private class BankManagerMetaData {
public BankManagerMetaData(long money, String reason, long payment, String otherParty, Integer time) {
this.balance = money;
this.reason = reason;
this.payment = payment;
this.otherParty = otherParty;
this.time = time;
}
// Change the funds of an account from an administrative perspective
public void AdminChangeFunds(ServerPlayerEntity initiator, String accountId, Integer amount, String changeType, String optionalReason) {
// Check to make sure account id exists
Integer routingNum = AccountNumberGenerator.GetRoutingNumberFromId(accountId);
IndividualBank bankFromRout = GetBankByRoutingNumber(routingNum);
long balance = 0;
String reason; //not sure why my compiler is saying unused
long payment;
String otherParty;
Integer time;
}
System.out.println("Is bank null? " + (bankFromRout == null ? "YES" : "NO"));
System.out.println("Bank specified: " + bankFromRout);
System.out.println("Routing number: " + routingNum);
// Verify bank exists
if (bankFromRout != null) {
// Verify account exists
System.out.println("accountNumber is " + accountId);
String accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId);
System.out.println("changeType is " + changeType);
// This is the general bank account of the read-in config for the player uuid ||| the class that gets converted into a json for the players file
public class BankManagerFile {
// Players uuid is the name of the file
String uuid;
// Contents of file
/*
* Example:
* player_uuid_here:
* {
* "1":
* {
* "balance": "10";
* "reason": "tax evasion";
* "payment": $-44
* "other party": "jt";
* "time": "30";
* }
* "2":
* {
* Etc.
* }
* }
*/
public HashMap<String, BankManagerMetaData> bank = new HashMap<String, BankManagerMetaData>();
}
public long GetBalance() {
long ret = 0;
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
ret = entry.getValue().balance;
}
return ret;
}
//https://maven.fabricmc.net/docs/fabric-api-0.34.8+1.17/net/fabricmc/fabric/api/networking/v1/PlayerLookup.html maybe this for getting the players im still not sure
public void SetBalance(Integer newBalance, String reason, String otherParty) {
Integer transactionNumber = bankInfo.bank.size();
bankInfo.bank.put(transactionNumber.toString(),
new BankManagerMetaData(newBalance, reason, newBalance, otherParty, 0));
FlashConfig(PlayerListNameChecker(otherParty));
}
public void AddMoney(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance += payment;
entry.getValue().reason = "SERVER: " + reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
switch (changeType) {
case "add":
bankFromRout.AddMoneyToAccount(accountNumber, amount);
break;
case "subtract":
bankFromRout.SubtractMoneyFromAccount(accountNumber, amount);
break;
case "set":
bankFromRout.SetMoneyOnAccount(accountNumber, amount);
break;
default:
System.out.println("The operation that was specified by the developer does not exist. Valid operations are add/subtract/set");
break;
}
} else {
bankInfo.bank.put(bankInfo.uuid, new BankManagerMetaData(payment, reason, payment, otherParty, 0));
initiator.sendMessage(Text.of("That bank does not exist!"));
}
}
FlashConfig(PlayerListNameChecker(otherParty));
}
public void InitiateBankFundsTransfer(ServerPlayerEntity fromPlayer, String toAccount, Integer amount) {
// Get player default selection
String fromAccount = playerConfigs.get(fromPlayer.getUuidAsString()).defaultSelectedBank;
public void SubtractBalance(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance -= payment;//not working?
entry.getValue().reason = "SERVER: " + reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
String fromAccountSymbol = AccountNumberGenerator.GetFinancialSymbolFromId(fromAccount);
String toAccountSymbol = AccountNumberGenerator.GetFinancialSymbolFromId(toAccount);
System.out.println("InitiateBankFundsTransfer: FROM_ACCOUNT, FROM_ACCOUNT_SYMBOL, TO_ACCOUNT_SYMBOL: " + fromAccount + ", " + fromAccountSymbol + ", " + toAccountSymbol);
Integer destRoutingNumber = bankNameFastMap.get(toAccountSymbol);
Integer fromRoutingNumber = bankNameFastMap.get(fromAccountSymbol);
IndividualBank destBank = banks.get(destRoutingNumber);
IndividualBank fromBank = banks.get(fromRoutingNumber);
// Verify banks exist
if (destBank != null && fromBank != null) {
if (fromBank.IsValidWithdrawal(amount, fromAccount)) {
fromBank.SubtractMoneyFromAccount(fromAccount, amount);
destBank.AddMoneyToAccount(toAccount, amount);
fromPlayer.sendMessage(Text.of("[" + fromAccountSymbol + "]: Your wire has processed."));
} else {
fromPlayer.sendMessage(Text.of("[" + fromAccountSymbol + "]: You are not allowed to make this withdrawal."));
}
} else {
bankInfo.bank.put(bankInfo.uuid, new BankManagerMetaData(0, reason, payment, otherParty, 0));
}
FlashConfig(PlayerListNameChecker(otherParty));
}
public void Wire(String reason, long payment, String otherParty) {
if (bankInfo.bank.size() > 0) {
for (Entry<String, BankManagerMetaData> entry : bankInfo.bank.entrySet()) {
entry.getValue().balance -= payment;
entry.getValue().reason = reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
if (payment <= 0) {
// add a error for the PLAYER not the server
return;
}
}
// make a server instance
MinecraftServer server = CommonServerUtils.GetServerInstance();
String[] playerList = server.getPlayerNames();
//NOT SURE IF THIS FOR LOOP IS ONE OFF COULD POSSIBLEY BE SO IF NO ONE IS GETTING MONEY DOUBLE CHECK FOR LOOP
for (int i = 0; i < playerList.length; i++) {
System.out.println(ChatUtil.ColoredString("PLAYERS: " + playerList, CONSOLE_COLOR.YELLOW));
if (playerList[i] == otherParty) {
System.out.println(ChatUtil.ColoredString("Found Player: " + otherParty, CONSOLE_COLOR.GREEN));
// we will use getuuidbyname then set the other VALID players bank BE SURE TO
// MAKE THEM A BANK FIRST IF THEY DONT HAVE ONE fortnite forever
if (config.GetFile("bank/" + GetUuidByName(server, otherParty) + ".json").size() < 1) {
BankManagerFile newBankInfo = new BankManagerFile();
BankManagerMetaData newBank = new BankManagerMetaData(0, "Account Created", 0, "Server", 0);
newBankInfo.bank.put(GetUuidByName(server, otherParty).toString(), newBank);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", newBankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
return;
fromPlayer.sendMessage(Text.of("Something went wrong! Either your bank or their bank does not exist. You shouldn't get this error!"));
}
}
//since the other player has a account now and we have access to it we can start fucking around
BankManagerFile newBankInfo = new BankManagerFile();
newBankInfo = config.GetJsonObjectFromFile("bank/" + GetUuidByName(server, otherParty) + ".json", BankManagerFile.class);
// for now we will only use adding valance and not subtracting since that dosent make any sense
for (Entry<String, BankManagerMetaData> entry : newBankInfo.bank.entrySet()) {
entry.getValue().balance += payment;
entry.getValue().reason = reason;
entry.getValue().payment = payment;
entry.getValue().otherParty = otherParty;
entry.getValue().time = 0;
}
//cannot use regular flash config since the hard coded values need to add agurment for the uuid
//needs to be inside the player check
FlashConfig(newBankInfo.uuid);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", bankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
public void InitiateBankAccountClosure(String bankIdentifier, ServerPlayerEntity player, String bankAccountId) {
}
public String GetDefaultSelectedAccount(String playerUuid, String bankIdentifier) {
String account = "";
if (playerConfigs.containsKey(playerUuid)) {
account = playerConfigs.get(playerUuid).defaultSelectedBank;
}
return account;
}
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(ChatUtil.ColoredString("Player Not Found: " + otherParty, CONSOLE_COLOR.RED));
return;
}
}
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 {
System.out.println(ChatUtil.ColoredString("You need to finance better", CONSOLE_COLOR.RED));
return;
player.sendMessage(Text.of("That bank does not exist"));
}
}
String GetUuidByName(MinecraftServer server, String playerName) {
PlayerManager playerManager = server.getPlayerManager();
ServerPlayerEntity player = playerManager.getPlayer(playerName);
if (player.getUuid() != null) {
return player.getUuidAsString();
if (success) {
player.sendMessage(Text.of("The banking operation was successful and your banking information has been updated"));
} else {
return "";
}
}
String PlayerListNameChecker(String otherParty) {
MinecraftServer server = CommonServerUtils.GetServerInstance();
String[] playerList = server.getPlayerNames();
for (int i = 0; i < playerList.length; i++) {
System.out.println(ChatUtil.ColoredString("PLAYERS: " + playerList, CONSOLE_COLOR.YELLOW));
if (playerList[i] == otherParty) {
System.out.println(ChatUtil.ColoredString("Found Player: " + otherParty, CONSOLE_COLOR.GREEN));
// we will use getuuidbyname then set the other VALID players bank BE SURE TO
// MAKE THEM A BANK FIRST IF THEY DONT HAVE ONE fortnite forever
if (config.GetFile("bank/" + GetUuidByName(server, otherParty) + ".json").size() < 1) {
BankManagerFile newBankInfo = new BankManagerFile();
BankManagerMetaData newBank = new BankManagerMetaData(0, "Account Created", 0, "Server", 0);
newBankInfo.bank.put(GetUuidByName(server, otherParty).toString(), newBank);
try {
config.WriteToJsonFile("bank/" + newBankInfo.uuid + ".json", newBankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
return "";
}
}
else {
System.out.println(ChatUtil.ColoredString("Bank Account Found", CONSOLE_COLOR.GREEN));
}
return GetUuidByName(server, otherParty);
}
}
System.out.println(ChatUtil.ColoredString("For Loop condition bypassed Null Playerlist or Null Server instance", CONSOLE_COLOR.RED));
return "";
}
public void FlashConfig(String uuid) {
try {
config.WriteToJsonFile("bank/" + uuid + ".json", bankInfo);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Could not flash notes configuration file", CONSOLE_COLOR.RED));
player.sendMessage(Text.of("The banking operating FAILED. You may need to visit the bank for more information!"));
}
}
}

View File

@ -0,0 +1,143 @@
package jesse.keeblarcraft.BankMgr;
import java.util.List;
// Contains the information of an individuals player's bank account.
// 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 String globalAccountNumber;
private String bankLetterIdentifier;
private String accountNumber;
private String accountNumberAlias;
private Integer routingNumber; // Will always be the bank it's in
private List<String> accountHolders;
private List<String> accountHolderUuids;
private Integer accountBalance;
private Boolean allowNegativeBalance;
private Boolean accountLocked;
private Integer accountType; // TODO: Replace with enum in future. Valid is "checking" and "savings" right now
public IndividualAccount() {}
public IndividualAccount(String accountNumber, Integer routingNumber, List<String> holders,
List<String> accountHolderUuids, Boolean allowNegativeBalance, Integer initialBalance,
String alias, Integer accountType, String bankLetterIdentifier) {
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;
this.bankLetterIdentifier = bankLetterIdentifier;
this.globalAccountNumber = bankLetterIdentifier + "-" + routingNumber + "-" + accountNumber;
}
// 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);
}
}
// May have unintended consequences if account can't go negative
public void SetMoney(Integer amount) {
System.out.println("Previous balance on account: " + this.accountBalance + ". New balance: " + amount);
this.accountBalance = amount;
}
public void AliasAccount(String newAlias) {
this.accountNumberAlias = newAlias;
}
public Boolean IsLocked() {
return accountLocked;
}
public void LockAccount() {
accountLocked = true;
}
public Boolean Deposit(Integer amount) {
Boolean success = false;
if (!accountLocked)
{
accountBalance += amount;
success = true;
}
return success;
}
public Boolean Withdraw(Integer amount) {
Boolean success = false;
if (!accountLocked)
{
// Determine remaining balance
Integer remaining = accountBalance - amount;
success = (remaining < 0 && !allowNegativeBalance);
// Complete the transaction if successful
if (success) {
accountBalance = remaining;
}
}
return success;
}
public Boolean CanWithdraw(Integer amount) {
Boolean canWithdraw = false;
if (!accountLocked && (accountBalance - amount >= 0 || allowNegativeBalance)) {
canWithdraw = true;
}
return canWithdraw;
}
public Boolean IsHolder(String name) {
Boolean ret = false;
if (accountHolders.contains(name)) {
ret = true;
}
return ret;
}
public Boolean AllowsNegative() {
return this.allowNegativeBalance;
}
public List<String> GetAccountHolders() {
return accountHolders;
}
public Integer GetAccountBalance() {
return accountBalance;
}
public String GetAccountNumber() {
return accountNumber;
}
public Integer GetRoutingNumber() {
return routingNumber;
}
public String GetGlobalAccountNumber() {
return globalAccountNumber;
}
}

View File

@ -0,0 +1,401 @@
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
//
// The bank will keep track of all accounts within its facilities. In addition to accounts, the bank
// maintains its own identifier which is unique and other misc things.
public class IndividualBank {
private Map<String, Integer> ACCOUNT_TYPES = Map.ofEntries(
entry("checking", 0),
entry("savings", 1)
);
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 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;
private static String CONFIG_LOCATION = "config/keeblarcraft/bank/";
// 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.
private Integer kbicInsuredAmount;
private Boolean kbicInsured;
// bankMoney is the total amount of money the bank possesses itself. The bank itself is personally responsible
// for backing the amount of money it claims it has and this is the balance that is withdrawn from for credits.
// A bank can have a sum of money that is less than the total amount of money of all its account holders
private Integer bankMoney;
// Key = ACCOUNT NUMBER
// Value = ACCOUNT
private class Accounts {
// Key = account identifier
// Val = account object
private HashMap<String, IndividualAccount> accountsList = new HashMap<String, IndividualAccount>();
// Key = user uuid
// Val = List of account identifiers
private HashMap<String, List<String>> accountsListFromName = new HashMap<String, List<String>>(); // This is a list that just points to a list of account numbers by person. USEFUL
}
Accounts accounts;
private List<String> 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, String nameOfBank) {
accounts = new Accounts();
lockedUsers = new ArrayList<String>();
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 = CONFIG_LOCATION + 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(CONFIG_LOCATION + 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<String, IndividualAccount> account : accounts.accountsList.entrySet()) {
// We must loop over the string of holders for each account as well to make the flattened accountsListFromName map
List<String> 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 (accounts.accountsListFromName.containsKey(accountHolders.get(holderIndex))) {
// Case 1: User exists, update map entry
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
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();
}
public String GetBankName() {
return registeredBankName;
}
public List<IndividualAccount> GetAccountsOfUser(String uuid) {
System.out.println("UUID passed in: " + uuid);
List<IndividualAccount> accountsFromUser = new ArrayList<IndividualAccount>();
List<String> listOfAccounts = accounts.accountsListFromName.get(uuid);
System.out.println("Is list of accounts null? " + (listOfAccounts == null ? "YES" : "NO"));
System.out.println("List of account size: " + listOfAccounts.size());
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, String newHolderUuid, String accountIdentifier, IndividualAccount newAccountOnly) {
// Update the fast-access map first
System.out.println("UpdateBankAccounts called with information " + newHolderName + " " + newHolderUuid + " " + accountIdentifier);
if (accounts.accountsListFromName.containsKey(newHolderUuid)) {
// Check if user is already in map
accounts.accountsListFromName.get(newHolderUuid).add(accountIdentifier);
} else {
// Add new entry to map
List<String> userAccountList = new ArrayList<String>(); // Lists are immutable; must make ArrayList
userAccountList.add(accountIdentifier);
accounts.accountsListFromName.put(newHolderUuid, userAccountList);
}
// Update regular account list
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?)
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!
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 Integer GetRoutingNumber() {
return this.routingNumber;
}
public void AddMoneyToAccount(String accountId, Integer amount) {
IndividualAccount account = accounts.accountsList.get(accountId);
System.out.println("Received account # " + accountId + " and money amnt " + amount);
if (account != null) {
account.Deposit(amount);
}
FlashConfig("bank/" + routingNumber + "/accounts");
}
public void SubtractMoneyFromAccount(String accountId, Integer amount) {
IndividualAccount account = accounts.accountsList.get(accountId);
for (Entry<String, IndividualAccount> debug : accounts.accountsList.entrySet()) {
System.out.println("ACCOUNT ID: " + debug.getKey());
System.out.println("ACCOUNT NUM: " + debug.getValue().GetAccountNumber());
}
if (account != null) {
account.Withdraw(amount);
}
FlashConfig("bank/" + routingNumber + "/accounts");
}
public void SetMoneyOnAccount(String accountId, Integer amount) {
IndividualAccount account = accounts.accountsList.get(accountId);
System.out.println("Is account null? " + (account == null ? "YES" : "NO"));
System.out.println("Received account # " + accountId + " and money amnt " + amount);
for (Entry<String, IndividualAccount> debug : accounts.accountsList.entrySet()) {
System.out.println("ACCOUNT ID: " + debug.getKey());
System.out.println("ACCOUNT NUM: " + debug.getValue().GetAccountNumber());
}
if (account != null) {
account.SetMoney(amount);
}
FlashConfig("bank/" + routingNumber + "/accounts");
}
public Boolean CreateAccount(String holderUuid, String holderName, String accountTypeStr) {
Boolean success = false;
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 = 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 && !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
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), bankFourLetterIdentifier);
System.out.println("Updating accounts list for this bank");
UpdateBankAccounts(holderName, holderUuid, actualAccountNumber, newAccount);
success = true;
}
}
}
return success;
}
public void AliasAccount(String accountId, String newAlias) {
String accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId);
if (accounts.accountsList.containsKey(accountNumber)) {
accounts.accountsList.get(accountNumber).AliasAccount(newAlias);
}
}
public Boolean LockAccountHolder(String holderName) {
Boolean success = false;
Integer accountIter = 0;
for (Entry<String, List<String>> holderAccounts : accounts.accountsListFromName.entrySet()) {
accounts.accountsList.get(holderAccounts.getValue().get(accountIter++)).LockAccount();
}
return success;
}
public Boolean CloseAccount(String accountId) {
Boolean success = false;
String accountNumber = AccountNumberGenerator.GetAccountNumberFromId(accountId);
if (accounts.accountsList.get(accountNumber).GetAccountBalance() == 0) {
accounts.accountsList.remove(accountNumber);
success = true;
}
return success;
}
public Boolean HasAccount(String accountIdentifier) {
Boolean containsAccount = false;
if (accounts.accountsList.containsKey(accountIdentifier)) {
containsAccount = true;
}
return containsAccount;
}
public Boolean IsValidWithdrawal(Integer withdrawalAmount, String accountIdentifier) {
Boolean isValid = false;
if (accounts.accountsList.containsKey(accountIdentifier)) {
IndividualAccount account = accounts.accountsList.get(accountIdentifier);
if (account.CanWithdraw(withdrawalAmount)) {
isValid = true;
}
}
return isValid;
}
public Boolean IsAccountHolder(String accountIdentifier, String uuid) {
Boolean isHolder = false;
// Verify account exists first
if (accounts.accountsList.containsKey(accountIdentifier)) {
isHolder = accounts.accountsList.get(accountIdentifier).IsHolder(uuid);
}
return isHolder;
}
/////////////////////////////////////////////////////////////////////////////
/// @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<String, IndividualAccount> 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();
}
}
}
}

View File

@ -0,0 +1,15 @@
package jesse.keeblarcraft.BankMgr;
// All details that involve a transaction attempt
public class TransactionMetadata {
enum TRANSACTION_TYPE {
DEBIT,
CREDIT
}
public String fromParty; // Account ID
public String toParty; // Account ID
public Integer amount;
public String transactionId;
public TRANSACTION_TYPE transactionType;
}

View File

@ -44,18 +44,18 @@ public class AttributeCommands {
(
EntityArgumentType.getPlayer(context, "targetPlayer"),
StringArgumentType.getString(context, "attributeName"),
context)
context
)
.build();
).build();
var attributeNameDelete = CommandManager.argument("attributeName", StringArgumentType.greedyString())
.executes(context -> DeleteAttribute
(
EntityArgumentType.getPlayer(context, "targetPlayer"),
StringArgumentType.getString(context, "attributeName"),
context)
context
)
.build();
).build();
// Build out the argument tree here
dispatcher.getRoot().addChild(attributeNode);

View File

@ -1,12 +1,20 @@
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 jesse.keeblarcraft.Utils.HelpBuilder;
import jesse.keeblarcraft.Utils.HelpBuilder.COLOR_CODE;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
@ -14,142 +22,799 @@ import net.minecraft.text.Text;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
public class BankCommands {
private static List<String> FILLER_WORDS = new ArrayList<String>(List.of("of", "from", "as", "with", "name", "dubbed", "coined", "note"));
private static String HELPCMD_HELP = "help";
private static String HELPCMD_CREATE = "create";
private static String HELPCMD_SELECT = "select";
private static String HELPCMD_CLOSE = "close";
private static String HELPCMD_REPORT = "report";
private static String HELPCMD_BALANCE = "balance";
private static String HELPCMD_EXAMPLES = "examples";
private static String HELPCMD_MOVE = "move";
private static String HELPCMD_SYNTAX = "syntax";
private static String HELPCMD_ALIAS = "alias";
private static String HELPCMD_WIRE = "wire";
private static String HELPCMD_ACCOUNTS = "accounts";
private static String HELPCMD_ADMIN_BALANCE_CHANGE = "admin-set-balance";
private static String HELPCMD_ADMIN_BALANCE_GET = "admin-get-balance";
private static String HELPCMD_ADMIN_CREATE_BANK = "admin-create-bank";
private static String HELPCMD_ADMIN_CLOSE_BANK = "admin-close-bank";
private static String HELPCMD_ADMIN_FORCE_WIRE = "admin-force-wire";
private static String HELPCMD_ADMIN_LOCK_BANK = "admin-lock-bank";
private static String HELPCMD_SET_SERVER_ALLOWANCE = "admin-set-server-allowance";
private static String HELPCMD_GET_SERVER_ALLOWANCE = "admin-get-server-allowance";
private static String HELPCMD_ADMIN_COMMANDS_LIST = "admin-commands-list";
private static String HELPCMD_ADMIN_ACCOUNTS = "admin-accounts";
private static String HELPCMD_ADMIN_ACCOUNTS_LIST = "admin-accounts-list";
private static String HELPCMD_ADMIN_ACCOUNTS_MOVE = "admin-accounts-move";
private static String HELPCMD_ADMIN_ACCOUNTS_FORCE_CLOSE = "admin-accounts-force-close";
private static String HELPCMD_ADMIN_ACCOUNTS_ADD = "admin-accounts-add";
private static String HELPCMD_ADMIN_ACCOUNTS_TRANSACTIONS = "admin-accounts-transactions";
private static String HELPCMD_ADMIN_ACCOUNTS_LOCK = "admin-accounts-lock";
HelpBuilder msgFormatter = new HelpBuilder();
private static Map<String, String> HELP_COMMANDS = Map.ofEntries(
entry
(
HELPCMD_HELP, "Usage: /bank help. This is the general help message for the bank system. For detailed help messages, please run /bank help <command OR subcommand>. List of possible " +
"commands are: CREATE, SELECT, CLOSE, REPORT, BALANCE, EXAMPLES, WIRE, MOVE, SYNTAX, ALIAS"
),
entry
(
HELPCMD_CREATE, "Usage: /bank create {checking|savings}. This will create a bank account with the default selected bank (/bank select {BANK_ID} to choose other)."
),
entry
(
HELPCMD_SELECT, "Usage: /bank select {BANK_ID}. Do `/bank list` for a list of banks on the server."
),
entry
(
HELPCMD_CLOSE, "Usage: /bank close {ACCOUNT_ID}. Do `/bank accounts list` to see all the accounts you have! Note: CANNOT close accounts that aren't 0 balance."
),
entry
(
HELPCMD_REPORT, "Usage: /bank report [ACCOUNT_ID|none]. Optional args mean you can get a specific report list for one account; but if none specified we default to all accounts."
),
entry
(
HELPCMD_BALANCE, "Usage: /bank balance [ACCOUNT_ID]. Optional args get balances for other accounts that aren't the default selected account. You may run `/bank accounts list` to " +
"see all possible options to get a balance for."
),
entry
(
HELPCMD_EXAMPLES, "Usage: /bank examples. This will print a much larger command with more thorough examples (and even shortcuts!) of all the commands from the bank system."
),
entry
(
HELPCMD_MOVE, "Usage: /bank move {ACCOUNT_ID} to {BANK_ID}. The intended use of this command is to move accounts across banks, but you could also plug in another ACCOUNT_ID in " +
" the `BANK_ID` part to perform an internal wire instead. NOTE: Moving might not succeed based on bank rules!"
),
entry
(
HELPCMD_SYNTAX, "Usage: /bank syntax. This banking system was written to be human readable in command-form and often operates like a database. You can put certain filler-words inside your commands that are " +
"parsed out at runtime if it helps with reading. Example command: \"/bank select 942-3449-42\" will work just fine to select a bank account as a primary account. HOWEVER, you can also " +
"type \"/bank select for-bills as default\" so long as you aliased the account identifier as 'for-bills'. Much nicer to read and work with. [Related: /bank help aliases]"
),
entry
(
HELPCMD_ALIAS, "Usage: /bank alias {ACCOUNT_ID} {UNIQUE_NAME}. Alias provides a handy way to refer to bank accounts by their unique names instead of long-form account ids. NOTE: Aliases " +
"are ONLY unique within the context of an individual bank and not across all banks, but generally you can replace `ACCOUNT_ID` as seen in other commands with the alias if it exists."
),
entry
(
HELPCMD_WIRE, "Usage: /bank wire {AMOUNT} to {ACCOUNT_ID|NAME}. It is recommended to wire a specific account ID over a name since it works if the other player is offline. If name is specified, " +
" the wire will go through to that players default account. Use with caution!"
),
entry
(
HELPCMD_ACCOUNTS, "Usage: /bank accounts {BANK_ID}. Prints out all of your banking information at a bank assuming you have any."
),
entry
(
HELPCMD_ADMIN_BALANCE_CHANGE, "Usage: /bank admin {set-bal|add-money|subtract-money} {ACCOUNT_ID|BANK_ID} {AMOUNT}. This command covers set/add/subtract money. Usage varies on context, but general help command is going to be " +
"/bank admin {set-bal|add-money|sub-money} {USER_ACCOUNT_ID|BANK_ROUTING_NUM} {amount}. Please note that the \"|\" in this context means OR - you do not put both! " +
"So you can set a banks balance or accounts balance with this command."
),
entry
(
HELPCMD_ADMIN_BALANCE_GET, "Usage: /bank admin get {USER_ACCOUNT_ID|BANK_ID}. Please note that the \"|\" in this means OR. Do NOT put both!"
),
entry
(
HELPCMD_ADMIN_CREATE_BANK, "Usage: /bank admin create {new bank name}. Bank name must be longer than 4 letters!"
),
entry
(
HELPCMD_ADMIN_CLOSE_BANK, "Usage: /bank admin close {BANK_ID}. Please be aware if the bank has an active balance or user accounts you will be asked to " +
"confirm if this is really what you want to do. Money is not magic, so if you specify to close the bank the bank balance is transferred back " +
"to the servers primary bank account & all user accounts money are transferred to the server bank. Accounts will automatically be opened to preserve " +
"the money for users to claim because banks are not ordinarily supposed to just close. Only run this command if you absolutely have to!"
),
entry
(
HELPCMD_ADMIN_FORCE_WIRE, "Usage: /bank admin wire {AMOUNT} from {ACCOUNT_ID} to {ACCOUNT_ID}. Please be aware this is a heavy handed command and may " +
"unintentionally override a bank accounts default setting. A bank account with a rule that can't go negative WILL go negative if this command is " +
"run because it's an administrative command! Use with caution"
),
entry
(
HELPCMD_ADMIN_LOCK_BANK, "Usage: /bank admin lock-bank {BANK_ID}. Completely freezes all activity in and out of a bank."
),
entry
(
HELPCMD_SET_SERVER_ALLOWANCE, "Usage: /bank admin set-server-allowance {amount} {interval}. Example usage: /bank admin set-server-allowance 1,000,000 " +
"30d. Supported interval characters: d/m/y (day/month/year). These times are in MINECRAFT in-game time, not real life time! Months are assumed to be equally " +
"30 days and years are 365 days. If you need precision, just specify your amount in days!"
),
entry
(
HELPCMD_GET_SERVER_ALLOWANCE, "Usage: /bank admin get-server-allowance. Returns the amount of money in the allowance for server and the interval it is in. "
),
entry
(
HELPCMD_ADMIN_COMMANDS_LIST, "Usage: /bank admin {sub-command} [optionals]. POSSIBLE ADMIN COMMANDS LIST: [set-bal, add-money, get-bal, sub-money, accounts, create-bank, close-bank, lock-bank, unlock, " +
"force-wire, get-server-allowance, set-server-allowance]. Commands FROM [accounts] cmd => [list, move, force-close, add, transactions, lock]"
),
entry
(
HELPCMD_ADMIN_ACCOUNTS, "Usage: /bank admin accounts {sub-command} [optionals]. This command has sub-commands that are expected. Possible => [list, move, force-close, add, transactions, lock]. General syntax is " +
"/bank admin accounts [sub-command]"
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_ADD, "Usage: /bank admin accounts add {ONLINE_PLAYER} [BANK_ID]. Adds a regular checking account to a bank for a player who is online. " +
"If BANK_ID not specified; defaults to the servers default bank"
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_FORCE_CLOSE, "Usage: /bank admin accounts force-close {ACCOUNT_ID}. Command not supported in this version."
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_LIST, "Usage: /bank admin accounts list {USERNAME}. Command not supported in this version."
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_LOCK, "Usage: /bank admin accounts lock {ACCOUNT_ID}. Command not supported in this version."
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_MOVE, "Usage: /bank admin accounts move {ACCOUNT_ID} to {BANK_ID}. Command not supported in this version."
),
entry
(
HELPCMD_ADMIN_ACCOUNTS_TRANSACTIONS, "Usage: /bank admin accounts transactions {ACCOUNT_ID}. Command not supported in this version."
)
);
ConfigManager config = new ConfigManager();
public void RegisterCommands() {
// Command: "/getbalance"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("getbalance")
.executes(context -> GetBalance(context)));
});
// This is basically a small re-created SQL language but for bank commands. I recommend asking me for the drawn
// image to better understand what this is accomplishing if it is confusing. Essentially it is easy though, as it
// is just /bank ACTION-WORD [arguments...] [optionals]
var bankRoot = CommandManager.literal("bank").build();
// 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))))));
// 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, "ARG_LIST"))
)
.build();
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));
// Building the argument tree here
dispatcher.getRoot().addChild(bankRoot);
bankRoot.addChild(argList);
});
}
public Integer GetBalance(CommandContext<ServerCommandSource> context) {
Integer ret = -1;
// Remove filler words which only exist for human readability
public List<String> RemoveFillerWords(List<String> argList) {
int index = 0;
for (String str : argList) {
if (FILLER_WORDS.contains(str)) {
argList.remove(index);
}
// Also make every word lower case
str = str.toLowerCase();
index++;
}
return argList;
}
public int ParseBankCommand(CommandContext<ServerCommandSource> context, String unformattedArgList) {
List<String> 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);
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":
System.out.println("Calling create command...");
CreateCommand(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size()));
break;
case "close":
CloseCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "select":
SelectCommand(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size()));
break;
case "wire":
WireCommand(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size()));
break;
case "balance":
BalanceCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "help":
HelpCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "move":
MoveCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "alias":
AliasCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "report":
ReportCommand(context.getSource().getPlayer(), formattedArgList);
break;
case "examples":
HelpCommand(context.getSource().getPlayer(), List.of(HELPCMD_EXAMPLES));
break;
case "syntax":
HelpCommand(context.getSource().getPlayer(), List.of(HELPCMD_SYNTAX));
break;
case "list":
ListAllBanks(context.getSource().getPlayer(), formattedArgList);
break;
case "account":
case "accounts":
ManageAccounts(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size()));
break;
case "admin":
AdminCommand(context.getSource().getPlayer(), formattedArgList.subList(1, formattedArgList.size()));
break;
default:
if (context.getSource().isExecutedByPlayer()) {
ServerPlayerEntity player = context.getSource().getPlayer();
BankManager playerBank = new BankManager(player.getUuidAsString());
player.sendMessage(Text.literal(String.valueOf(playerBank.GetBalance())));
context.getSource().getPlayer().sendMessage(Text.of("You have entered an improperly formatted message. Please reference the bank manual if you are confused how to form a command!"));
}
break;
}
return 0;
}
return ret;
// General helper parse function and throws correct error for player if fails
public Integer ParseToInteger(ServerPlayerEntity player, String possibleInt, String helpCmd) {
Integer amount = 0;
try {
amount = Integer.parseInt(possibleInt);
} catch (NumberFormatException exception) {
HelpCommand(player, List.of(helpCmd));
}
//SetBalance will be a ServerCommand only Perm level Op
public Integer SetBalance(Integer newBalance, String reason, String otherParty, CommandContext<ServerCommandSource> 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())));
System.out.println("ParseToInteger was called. The input was " + possibleInt + " and the parsed int is " + amount);
return amount;
}
return ret;
public Boolean IsOperator(ServerPlayerEntity player) {
// THIS IS A HACK. REMOVE BEFORE FINAL VERSION
//TODO: JUST RETURN HASPERMISSIONLEVEL
return true;
// if (player.hasPermissionLevel(4)) {
// return true;
// } else {
// return false;
// }
}
//AddMoney will be a ServerCommand Only Perm level Op
public Integer AddMoney(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> 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())));
// Possible code paths ({}=required, []=optionals, ()=explanation):
// REQUIRED (path) = {SetBal|AddMoney|SubMoney} {LONG_ID|BANK_ID} {amount} (Depending on context, changes balance somehow from an account ID or bank)
// REQUIRED (path) = {GetBal} {LONG_ID:BANK_ID} (Gets the balance of a bank account or bank)
// REQUIRED (path) = {accounts} {list} {PLAYER_NAME} (Returns summarized account information on a player)
// REQUIRED (path) = {accounts} {move} {LONG_ID} to {BANK_ID} (Will move account to diff bank. May generate new account ID)
// REQUIRED (path) = {accounts} {force-close} {LOND_ID} (Forces an account to be closed. Money is returned to server bank account)
// REQUIRED (path) = {accounts} {add} to {PLAYER_NAME} [BANK_ID] (Adds an account to a player to server by default. Specify other for other)
// REQUIRED (path) = {accounts} {transactions} from {LONG_ID} (Gets transaction report from an account by ID)
// REQUIRED (path) = {accounts} {lock} {LONG_ID:USER} (Locks a specific bank account OR all bank accounts owned by $USER)
// REQUIRED (path) = {create-bank} {BANK_ID} (Create a new bank. Default is owned by server)
// REQUIRED (path) = {close-bank} {BANK_ID} (Closing a bank forces a close on all accounts. Banks money is returned to server bank)
// REQUIRED (path) = {lock-bank} {BANK_ID} (Freezes all activities through a bank)
// REQUIRED (path) = {unlock} {BANK_ID:ACCOUNT_ID:USER} (will unlock a bank, account, or all of a users accounts)
// REQUIRED (path) = {wire} {amount} from {ACCOUNT_ID} to {ACCOUNT_ID} (Forces a wire from one account to another. Overrides overdraft setting since it's administrative)
// REQUIRED (path) = {get-server-allowance} (Returns the rate of which the server adds money to itself magically & how much)
// REQUIRED (path) = {set-server-allowance} {amount} {interval} (Sets the magic allowance rate of server & period in which it does it to add money magically to server bank)
public void AdminCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
// The player must be opped & the size must be at LEAST 2 (1 keyword + extra for sublist)
String pName = sourcePlayer.getDisplayName().toString();
System.out.println("Is player admin? " + (IsOperator(sourcePlayer) ? "YES" : "NO"));
if (IsOperator(sourcePlayer) && argList.size() >= 1) {
String arg = argList.get(0);
List<String> remainingArgs = argList.subList(1, argList.size());
switch (arg) {
case "account":
case "accounts":
break;
case "submoney":
case "subtract-money":
case "subtractmoney":
case "sub-money":
// Require account identifier + balance
if (remainingArgs.size() >= 2) {
String accountId = remainingArgs.get(0);
Integer amount = ParseToInteger(sourcePlayer, remainingArgs.get(1), HELPCMD_ADMIN_BALANCE_CHANGE);
String optionalReason = "";
if (remainingArgs.size() >= 3) {
optionalReason = remainingArgs.get(2);
}
return ret;
AdminBalanceChange(sourcePlayer, accountId, amount, "subtract", optionalReason);
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_BALANCE_CHANGE));
}
break;
case "addmoney":
case "add-money":
// Require account identifier + balance
if (remainingArgs.size() >= 2) {
String accountId = remainingArgs.get(0);
Integer amount = ParseToInteger(sourcePlayer, remainingArgs.get(1), HELPCMD_ADMIN_BALANCE_CHANGE);
String optionalReason = "";
if (remainingArgs.size() >= 3) {
optionalReason = remainingArgs.get(2);
}
//SubtractBalance will be a ServerCommand Perm leve Op
public Integer SubtractBalance(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> 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())));
AdminBalanceChange(sourcePlayer, accountId, amount, "add", optionalReason);
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_BALANCE_CHANGE));
}
break;
case "setbal":
case "set-bal":
case "setbalance":
case "set-balance":
// Require account identifier + balance
if (remainingArgs.size() >= 2) {
String accountId = remainingArgs.get(0);
Integer amount = ParseToInteger(sourcePlayer, remainingArgs.get(1), HELPCMD_ADMIN_BALANCE_CHANGE);
String optionalReason = "";
if (remainingArgs.size() >= 3) {
optionalReason = remainingArgs.get(2);
}
return ret;
System.out.println("Running set-balance with amount " + amount);
if (amount != 0) {
AdminBalanceChange(sourcePlayer, accountId, amount, "set", optionalReason);
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_BALANCE_CHANGE));
}
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_BALANCE_CHANGE));
}
break;
case "getbal":
case "get-bal":
case "getbalance":
case "get-balance":
// Require account identifier
if (remainingArgs.size() >= 1) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_BALANCE_GET));
}
break;
case "createbank":
case "create-bank":
if (remainingArgs.size() >= 1) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_CREATE_BANK));
}
break;
case "closebank":
case "close-bank":
if (remainingArgs.size() >= 1) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_CLOSE_BANK));
}
break;
case "forcewire":
case "force-wire":
case "wiremoney":
case "wire-money":
case "wire":
if (remainingArgs.size() >= 3) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_FORCE_WIRE));
}
break;
case "lock-bank":
case "lockbank":
if (remainingArgs.size() >= 1) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_LOCK_BANK));
}
break;
case "get-server-allowance":
case "getserverallowance":
sourcePlayer.sendMessage(Text.of(GetServerAllowance()));
break;
case "setserverallowance":
case "set-server-allowance":
if (remainingArgs.size() >= 1) {
} else {
HelpCommand(sourcePlayer, List.of(HELPCMD_SET_SERVER_ALLOWANCE));
}
break;
default:
HelpCommand(sourcePlayer, List.of(HELPCMD_ADMIN_COMMANDS_LIST));
break;
}
public Integer Wire(String reason, long payment, String otherParty, CommandContext<ServerCommandSource> 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())));
} else {
sourcePlayer.sendMessage(Text.of("Only admins can use this command!"));
}
}
return ret;
public String GetServerAllowance() {
return "";
}
public void AdminBalanceChange(ServerPlayerEntity player, String accountId, Integer amount, String type, String optionalReason) {
System.out.println("AdminChangeFunds on account " + accountId);
BankManager.GetInstance().AdminChangeFunds(player, accountId, amount, type, optionalReason);
}
public void AdminGetBalance(String accountId) {
}
public void AdminWireMoney(String fromAccount, String toAccount, Integer amount, String optionalReason) {
}
public void AdminCreateBank(String bankName, Integer initialBankBalance, Integer kbicInsuredAmount) {
}
public void AdminCloseBank(String bankIdentifier, String optionalReason, Boolean forceClosure) {
}
public void AdminAccounts() {
}
public void LockBank(String bankIdentifier, String optionalReason) {
}
public void LockPlayer(String playerName, String optionalReason) {
}
public void LockBankAccount(String accountId, String optionalReason) {
}
// Possible code paths:
// REQUIRED = {Routing # or Bank name}
// OPTIONAL = []
public void ManageAccounts(ServerPlayerEntity sourcePlayer, List<String> 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 #
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<IndividualAccount> userAccounts = bank.GetAccountsOfUser(sourcePlayer.getUuidAsString());
sourcePlayer.sendMessage(Text.of("[BANK INFO FOR " + bankName.toUpperCase() + "]"));
System.out.println("userAccounts size: " + userAccounts.size());
for (int i = 0; i < userAccounts.size(); i++) {
String accountNumber = userAccounts.get(i).GetAccountNumber();
String globalAccountNumber = userAccounts.get(i).GetGlobalAccountNumber();
List<String> accountHolders = userAccounts.get(i).GetAccountHolders();
Integer accountBalance = userAccounts.get(i).GetAccountBalance();
String l1 = "ACCOUNT NUMBER: " + msgFormatter.ColorMsg(accountNumber, COLOR_CODE.BLUE);
String l2 = "GLOBAL ACCOUNT NUMBER: " + msgFormatter.ColorMsg(globalAccountNumber, COLOR_CODE.BLUE);
String l3 = "HOLDERS: " + msgFormatter.ColorMsg(accountHolders, COLOR_CODE.GRAY);
String l4 = "BALANCE: " + msgFormatter.ColorMsg(accountBalance, COLOR_CODE.GREEN);
sourcePlayer.sendMessage((Text) msgFormatter.MakeCopyableTxt(l1, "Click to copy", accountNumber));
sourcePlayer.sendMessage((Text) msgFormatter.MakeCopyableTxt(l2, "Click to copy", globalAccountNumber));
sourcePlayer.sendMessage((Text) msgFormatter.MakeCopyableTxt(l3, "Click to copy", accountHolders));
sourcePlayer.sendMessage((Text) msgFormatter.MakeCopyableTxt(l4, "Click to copy", accountBalance));
sourcePlayer.sendMessage(Text.of("\n"));
}
} else {
sourcePlayer.sendMessage(Text.of(msgFormatter.ColorMsg("That bank does not exist", COLOR_CODE.RED)));
}
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized move command. Please use \"/bank help ACCOUNTS\" for more information."));
}
}
public void ListAllBanks(ServerPlayerEntity sourcePlayer, List<String> 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<String> argList) {
if (argList.size() >= 2) {
String possibleExternalIdentifier = argList.get(1);
boolean isExternalTransfer = false;
/*
* CALL TO BANKMGR TO VERIFY IF ABOVE IS VALID BANK ID. IF NOT; THIS IS INTERNAL TRANSFER
*/
if (isExternalTransfer) {
// External move to another bank or faction
String targetMoveAccount = argList.get(0);
String destBankId = argList.get(1);
/*
* CALL TO BANKMGR TO MOVE ACCOUNTS HERE
*/
} else {
// Internal transfer to this players accounts within system
String optionalFromAccount = ""; /* CALL TO BANKMGR TO GET DEFAULT SELECTED ACCOUNT FOR `FROM` FIELD */
if (argList.size() == 3) {
optionalFromAccount = argList.get(1);
}
String transferAmount = argList.get(0);
String destinationTransfer = argList.get(2);
/*
* BANK MGR CALL HERE
*/
}
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized move command. Please use \"/bank help MOVE\" for more information."));
}
}
// Posible code paths:
// REQUIRED = {ACCOUNT-ID|ALIAS} {ALIAS}
// OPTIONAL = []
public void AliasCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
if (argList.size() > 0) {
String newAlias = argList.get(0);
/*
* CALL TO BANKMGR TO SEE IF ALIAS ARG ALREADY EXISTS
*/
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized alias command. Please run \"/bank help ALIAS\" for more information."));
}
}
// Possible code paths:
// REQUIRED = {...} valid argList required
// OPTIONAL = []
// NOTE: This is just a shorthand to get to `/bank create report`
public void ReportCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
if (argList.size() > 0) {
argList.add(0, "report"); // Since we lose 'report' when we pass it as an alias/passthrough; we just shim it into the first spot
CreateCommand(sourcePlayer, argList);
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized report command. Please run \"/bank help REPORT\" for more information."));
}
}
// Possible code paths:
// REQUIRED = {}
// OPTIONAL = [identifier|alias]
public int BalanceCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
String accountToBalance = "default";
/*
*
* Code here to get default account from player
*
*/
if (argList.size() == 1) {
/*
* Set accountToBalance here
*/
}
/*
* Return to player balance here in new chatutil function not yet made
*/
return 0;
}
// Create command - valid arg list types:
// /... CREATE {CA/checking/savings/checking-account/savings-account/report}
public int CreateCommand(ServerPlayerEntity sourcePlayer, List<String> 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!
case "ca":
case "checking":
case "checking-account":
case "sa":
case "savings":
case "savings-account":
System.out.println("Creating account. Removing index 0 in argList. New argList is { " + argList + " }");
CreateAccount(sourcePlayer, argList);
break;
case "rep":
case "report":
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"));
}
return 0;
}
// Possible code paths:
// REQUIRED = {ACCOUNT ID|ALIAS}
// OPTIONAL = []
public int CloseCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
if (argList.size() > 0) {
String accountToClose = argList.get(0);
/*
* CALL TO BANKMGR TO DO STUFF
*/
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized close command. Please see \"/bank help CLOSE\" for more information."));
}
return 0;
}
// Possible code paths:
// required = {account id | alias}
// optional = [default|secondary|backup]
public int SelectCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
if (argList.size() > 0) {
String requiredArg = argList.get(0);
// If the optional args exist; fetch them
String optionalArg = "";
if (argList.size() == 2) {
optionalArg = argList.get(1);
}
BankManager.GetInstance().ChangeDefaultPlayerAccount(sourcePlayer, requiredArg);
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized select command. Please run /bank help select for more information."));
}
return 0;
}
// Possible code paths:
// REQUIRED = {AMOUNT} {ACCOUNT_ID} [optional]
// OPTIONAL = [reason]
public int WireCommand(ServerPlayerEntity sourcePlayer, List<String> argList) {
System.out.println("WireCommand called with arg size " + argList.size());
if (argList.size() >= 2) {
Integer amountToWire = ParseToInteger(sourcePlayer, argList.get(0), HELPCMD_WIRE);
String destAccount = argList.get(1);
String optionalReason = "";
if (argList.size() >= 3) {
optionalReason = argList.get(3);
}
System.out.println("optional reason: " + optionalReason);
BankManager.GetInstance().InitiateBankFundsTransfer(sourcePlayer, destAccount, amountToWire);
} else {
sourcePlayer.sendMessage(Text.of("Unrecognized wire command. Please run /bank help wire for more information."));
}
return 0;
}
// Possible code paths:
// REQUIRED = {}
// OPTIONAL = [command|subcommand]
public int HelpCommand(ServerPlayerEntity sourcePlayer, List<String> helpCommand) {
System.out.println("Running help command");
if (helpCommand.size() == 0) {
// General help command
for (Entry<String, String> helpCmd : HELP_COMMANDS.entrySet()) {
String cmd = msgFormatter.ColorMsg(helpCmd.getKey(), COLOR_CODE.GOLD);
String details = msgFormatter.FormatMsg(helpCmd.getValue(), COLOR_CODE.BLUE, COLOR_CODE.GRAY);
sourcePlayer.sendMessage(Text.of(cmd + " --> " + details));
sourcePlayer.sendMessage(Text.of("\n"));
}
} else {
// Iterate over list; verifying what commands are found
List<String> unknownCmds = new ArrayList<String>();
for (int i = 0; i < helpCommand.size(); i++) {
String newHelpCmd = helpCommand.get(i);
if (HELP_COMMANDS.containsKey(newHelpCmd)) {
// Print help for this specific command
String cmd = msgFormatter.ColorMsg(newHelpCmd, COLOR_CODE.GOLD);
String details = msgFormatter.FormatMsg(HELP_COMMANDS.get(newHelpCmd), COLOR_CODE.BLUE, COLOR_CODE.GRAY);
sourcePlayer.sendMessage(Text.of(cmd + " -> " + details));
} 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) {
String msg = msgFormatter.ColorMsg("The following commands do not exist or were mispelt: ", COLOR_CODE.RED);
sourcePlayer.sendMessage(Text.of(msg + unknownCmds));
}
}
return 0;
}
// Possible args:
// /bank create {SAVINGS/CHECKING} [optional: alias]
public void CreateAccount(ServerPlayerEntity player, List<String> 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";
// Set the optional account alias for ease-of-reference
if (accountArgs.size() >= 2) {
accountAlias = accountArgs.get(1).toLowerCase();
}
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"));
}
}
// Possible code paths for /bank create report
// required = {ACCOUNT ID / ALIAS} or {ALL}
public void GenerateAccountReport(ServerPlayerEntity player, List<String> reportArgs) {
if (reportArgs.size() > 0) {
String reportIdentifier = reportArgs.get(0).toLowerCase();
// First path is just to run a report on all accounts this player is attached to:
if (reportIdentifier == "all") {
/* BANKMANAGER CALL HERE. LOOP LIKELY NEEDED */
} else {
/* BANKMANAGER CALL HERE */
}
} else {
player.sendMessage(Text.of("Unrecognized report field data. Please run /bank help report for more information"));
}
}
}

View File

@ -22,6 +22,8 @@ public class CustomCommandManager {
NoteCommands noteCommands = new NoteCommands();
BankCommands bankCommands = new BankCommands();
AttributeCommands attributeCommands = new AttributeCommands();
FactionCommands factionCommands = new FactionCommands();
MiscCommands miscCommands = new MiscCommands();
// REGISTER COMMANDS BELOW
System.out.println(ChatUtil.ColoredString("REGISTERING CUSTOM COMMAND EXTENSIONS BELOW", CONSOLE_COLOR.BLUE));
@ -29,6 +31,7 @@ public class CustomCommandManager {
noteCommands.RegisterNoteCommands();
bankCommands.RegisterCommands();
attributeCommands.RegisterCommands();
factionCommands.RegisterFactionCommands();
miscCommands.RegisterCommands();
}
}

View File

@ -1,21 +1,89 @@
package jesse.keeblarcraft.Commands;
import java.util.List;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import jesse.keeblarcraft.FactionMgr.FactionManager;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class FactionCommands {
// Register function for commands
public void RegisterFactionCommands() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var factionNode = CommandManager.literal("faction").build();
var createFaction = CommandManager.literal("create").build();
var disbandFaction = CommandManager.literal("disband").build();
var promote = CommandManager.literal("promote").build();
var demote = CommandManager.literal("demote").build();
// The below nodes are duplicates but are necessary to make the execute path jump correctly
var createFactionName = CommandManager.argument("faction_name", StringArgumentType.greedyString())
.executes(context -> CreateFaction
(
context,
StringArgumentType.getString(context, "faction_name")
)
).build();
var disbandFactionName = CommandManager.argument("faction_name", StringArgumentType.greedyString())
.executes(context -> DeleteFaction
(
context,
StringArgumentType.getString(context, "faction_name")
)
).build();
var leaveFaction = CommandManager.literal("leave").executes(context -> LeaveFaction(context)
).build();
var listAll = CommandManager.literal("list")
.executes(context -> ListAllFactions(context.getSource().getPlayer())).build();
// Root node
dispatcher.getRoot().addChild(factionNode);
// List command
factionNode.addChild(listAll);
factionNode.addChild(createFaction);
factionNode.addChild(disbandFaction);
factionNode.addChild(leaveFaction);
createFaction.addChild(createFactionName);
disbandFaction.addChild(disbandFactionName);
});
}
/// PRIVATE HANDLERS BELOW
private int CreateFaction() {
int retValue = -1;
private int CreateFaction(CommandContext<ServerCommandSource> context, String newFactionName) {
int retValue = 0;
System.out.println("CreateFaction getting player obj");
ServerPlayerEntity player = context.getSource().getPlayer();
System.out.println("CreateFaction called");
if (newFactionName.length() >= 4) {
FactionManager.GetInstance().CreateFaction(newFactionName, player);
} else {
player.sendMessage(Text.of("Your faction must be at least 4 letters long!"));
}
return retValue;
}
private int DeleteFaction() {
int retValue = -1;
private int DeleteFaction(CommandContext<ServerCommandSource> context, String newFactionName) {
int retValue = 0;
ServerPlayerEntity player = context.getSource().getPlayer();
FactionManager.GetInstance().DeleteFaction(newFactionName, player);
return retValue;
}
@ -26,6 +94,14 @@ public class FactionCommands {
return retValue;
}
private int LeaveFaction(CommandContext<ServerCommandSource> context) {
ServerPlayerEntity player = context.getSource().getPlayer();
FactionManager.GetInstance().LeaveFaction(player);
return 0;
}
private int KickPlayerFromFaction() {
int retValue = -1;
@ -87,9 +163,18 @@ public class FactionCommands {
return retValue;
}
private int ListAllFactions() {
private int ListAllFactions(ServerPlayerEntity player) {
int retValue = -1;
System.out.println("Listing factions");
List<String> facs = FactionManager.GetInstance().ListOfFactions();
if (facs != null) {
player.sendMessage(Text.of("All factions on server: " + facs));
} else {
player.sendMessage(Text.of("There are no factions on this server yet!"));
}
return retValue;
}

View File

@ -0,0 +1,178 @@
package jesse.keeblarcraft.Commands;
import org.apache.logging.log4j.core.jmx.Server;
import com.mojang.brigadier.arguments.StringArgumentType;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.ConfigMgr.GeneralConfig;
import jesse.keeblarcraft.EventMgr.DimensionLoadingEvent;
import jesse.keeblarcraft.Utils.DirectionalVec;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.inventory.EnderChestInventory;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.nbt.NbtList;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
public class MiscCommands {
ConfigManager config = new ConfigManager();
public void RegisterCommands() {
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// var magicInv = CommandManager.literal("magic-inv").build();
// var claim = CommandManager.literal("claim").executes(context -> ClaimInventory(context.getSource().getPlayer())).build();
// dispatcher.getRoot().addChild(magicInv);
// magicInv.addChild(claim);
// });
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// var eChest = CommandManager.literal("enderchest").build();
// var player = CommandManager.argument("PLAYER", EntityArgumentType.player()).executes(
// context -> GetEnderchestOfPlayer(context.getSource().getPlayer(), EntityArgumentType.getPlayer(context, "PLAYER"))
// ).build();
// dispatcher.getRoot().addChild(eChest);
// eChest.addChild(player);
// // Alias
// dispatcher.register(CommandManager.literal("echest").redirect(eChest));
// });
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var forceSpawn = CommandManager.literal("set-global-spawn").executes(context -> ForceGlobalSpawn(context.getSource().getPlayer())).build();
dispatcher.getRoot().addChild(forceSpawn);
});
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var warp = CommandManager.literal("warp").build();
var warpLoc = CommandManager.argument("LOCATION", StringArgumentType.string())
.executes(context -> Warp(context.getSource().getPlayer(), StringArgumentType.getString(context, "LOCATION"))).build();
dispatcher.getRoot().addChild(warp);
warp.addChild(warpLoc);
});
}
public int Warp(ServerPlayerEntity player, String location) {
if (player.hasPermissionLevel(4)) {
System.out.println("Player is opped");
// hard coding spawn as only valid warp location. a more robust warp system can come later
DirectionalVec coords = GeneralConfig.GetInstance().GetSpawnCoords();
// DIRTY HACK: I am unsure how to compare the straight registry key. So for now, we will just iterate over all the worlds in the server. This really should
// not be a big deal since I have never seen a server with hundreds of worlds... but you never know I guess.
for (ServerWorld world : player.getServer().getWorlds()) {
System.out.println("Matched!");
if (world.getRegistryKey().toString().equals(coords.world.toString())) {
try {
player.teleport(world, coords.x, coords.y, coords.z, coords.yaw, coords.pitch);
} catch (Exception e) {
e.printStackTrace();
}
break;
} else {
System.out.println("KEY {" + world.getRegistryKey() + "} DID NOT MATCH OURS {" + coords.world + "}");
}
}
System.out.println("POST TELEPORT");
} else {
player.sendMessage(Text.of("This command is only available to server admins at the moment!"));
}
return 0;
}
public int ForceGlobalSpawn(ServerPlayerEntity player) {
if (player.hasPermissionLevel(4)) {
Vec3d coords = player.getPos();
DirectionalVec spawnVec = new DirectionalVec();
spawnVec.x = coords.x;
spawnVec.y = coords.y;
spawnVec.z = coords.z;
spawnVec.pitch = player.getPitch();
spawnVec.yaw = player.getYaw();
spawnVec.world = player.getWorld().getRegistryKey();
System.out.println("REG KEY OF SET: " + player.getWorld().getRegistryKey().toString());
System.out.println("REG KEY OF SET: " + player.getWorld().getRegistryKey());
player.sendMessage(Text.of("Global spawn has been set to [X, Y, Z]--[YAW, PITCH]: [" +
spawnVec.x + " " + spawnVec.y + " " + spawnVec.z + "]--" + "[" + spawnVec.yaw + " " + spawnVec.pitch + "]" + " IN WORLD " +
player.getWorld().asString()));
GeneralConfig.GetInstance().SetSpawnCoords(spawnVec);
} else {
player.sendMessage(Text.of("You do not have permission to use this command."));
}
return 0;
}
public int GetEnderchestOfPlayer(ServerPlayerEntity cmdInitiator, ServerPlayerEntity targetPlayer) {
// if (cmdInitiator.hasPermissionLevel(4)) {
// EnderChestInventory enderInv = targetPlayer.getEnderChestInventory();
// enderInv.
// } else {
// }
return 0;
}
// public int ClaimInventory(ServerPlayerEntity player) {
// Vec3d playerPosition = player.getPos();
// BlockPos blockPosition = new BlockPos((int) playerPosition.x + 4, (int) playerPosition.y, (int) playerPosition.z).east();
// BlockPos blockPosition2 = new BlockPos((int) playerPosition.x + 3, (int) playerPosition.y, (int) playerPosition.z).east();
// // Verify we can place the blocks before loading cached inventory
// if (player.getWorld().setBlockState(blockPosition, chestState)
// && player.getWorld().setBlockState(blockPosition2, chestState)) {
// // The below code WILL remove the inventory from the dimension cache. Only get
// // it when we succeed in placing chests!
// NbtList nbtInventory = DimensionLoadingEvent.GetInstance().GetInventory(player.getUuidAsString());
// if (nbtInventory != null) {
// BlockEntity block = player.getWorld().getBlockEntity(blockPosition);
// BlockEntity block2 = player.getWorld().getBlockEntity(blockPosition2);
// if (block != null && block2 != null) {
// block.markDirty();
// block2.markDirty();
// }
// player.sendMessage(Text.of("Look around, your magic inventory chest was placed near you!"));
// }
// } else {
// player.sendMessage(Text.of("Well, this is embarassing! Could not place magic inventory chest near you!"));
// }
// return 0;
// }
}

View File

@ -27,22 +27,23 @@ public class NoteCommands {
/////////////////////////////////////////////////////////////////////////////
public NoteCommands() {
// Check if directory exists
if (notesConfig.DoesDirectoryExist(NOTES_GLOBAL_DIRECTORY) == false) {
// Attempt to create the directory
try {
if (notesConfig.CreateDirectory(NOTES_GLOBAL_DIRECTORY) == true) {
System.out.println(ChatUtil.ColoredString("Created notes directory successfully!", CONSOLE_COLOR.BLUE)); //TODO: Success!
} else {
System.out.println(ChatUtil.ColoredString("ERROR: Notes directory FAILED to create!! Either the directory already exists or we are missing permissions!", CONSOLE_COLOR.RED)); //TODO: Critical failure --not specfic enough to mark it as a red or blue
}
} catch (DIRECTORY_CREATE_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("Directory creation failed", CONSOLE_COLOR.RED));
}
} else {
System.out.println(ChatUtil.ColoredString("Notes directory already exists. Skipping creation...", CONSOLE_COLOR.BLUE)); //TODO: Success!
}
// if (notesConfig.DoesDirectoryExist(NOTES_GLOBAL_DIRECTORY) == false) {
// // Attempt to create the directory
// try {
// if (notesConfig.CreateDirectory(NOTES_GLOBAL_DIRECTORY) == true) {
// System.out.println(ChatUtil.ColoredString("Created notes directory successfully!", CONSOLE_COLOR.BLUE)); //TODO: Success!
// } else {
// System.out.println(ChatUtil.ColoredString("ERROR: Notes directory FAILED to create!! Either the directory already exists or we are missing permissions!", CONSOLE_COLOR.RED)); //TODO: Critical failure --not specfic enough to mark it as a red or blue
// }
// } catch (DIRECTORY_CREATE_EXCEPTION e) {
// System.out.println(ChatUtil.ColoredString("Directory creation failed", CONSOLE_COLOR.RED));
// }
// } else {
// System.out.println(ChatUtil.ColoredString("Notes directory already exists. Skipping creation...", CONSOLE_COLOR.BLUE)); //TODO: Success!
// }
}
//TODO: Rework note commands upon story mode release
/////////////////////////////////////////////////////////////////////////////
/// @fn RegisterNoteCommands
///
@ -50,69 +51,69 @@ public class NoteCommands {
/////////////////////////////////////////////////////////////////////////////
public void RegisterNoteCommands() {
// Command: "/addnote note goes here"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("addnote")
.then(CommandManager.argument("value", StringArgumentType.greedyString())
.executes(context -> AddNote(StringArgumentType.getString(context, "value"), context))));
});
// Command: "/delnote noteIdHere"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// dispatcher.register(CommandManager.literal("delnote")
// dispatcher.register(CommandManager.literal("addnote")
// .then(CommandManager.argument("value", StringArgumentType.greedyString())
// .executes(context -> AddNote(StringArgumentType.getString(context, "value"), context))));
// });
// Command: "/purgenotes"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("purgenotes")
.executes(context -> PurgeAllNotes(context)));
});
// // Command: "/delnote noteIdHere"
// // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// // dispatcher.register(CommandManager.literal("delnote")
// // .then(CommandManager.argument("value", StringArgumentType.greedyString())
// // .executes(context -> AddNote(StringArgumentType.getString(context, "value"), context))));
// // });
// Command: "/modifynote noteIdHere new_note_string_here"
// Alises: "/editnote"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var mNote = dispatcher.register(CommandManager.literal("editnote")
.then(CommandManager.argument("note_id", IntegerArgumentType.integer())
.then(CommandManager.argument("new_note", StringArgumentType.string())
.executes(context -> ModifyNote(
IntegerArgumentType.getInteger(context, "note_id"),
StringArgumentType.getString(context, "new_note"),
context)))));
// // Command: "/purgenotes"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// dispatcher.register(CommandManager.literal("purgenotes")
// .executes(context -> PurgeAllNotes(context)));
// });
dispatcher.register(CommandManager.literal("editnote").redirect(mNote));
});
// // Command: "/modifynote noteIdHere new_note_string_here"
// // Alises: "/editnote"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// final var mNote = dispatcher.register(CommandManager.literal("editnote")
// .then(CommandManager.argument("note_id", IntegerArgumentType.integer())
// .then(CommandManager.argument("new_note", StringArgumentType.string())
// .executes(context -> ModifyNote(
// IntegerArgumentType.getInteger(context, "note_id"),
// StringArgumentType.getString(context, "new_note"),
// context)))));
// Command Root: "/delnote noteIdHere"
// Aliases: "/rmnote", "/deletenote"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var rootDeleteCmd = dispatcher.register(CommandManager.literal("delnote")
.then(CommandManager.argument("value", IntegerArgumentType.integer())
.executes(context -> DeleteNote(IntegerArgumentType.getInteger(context, "value"), context))));
// dispatcher.register(CommandManager.literal("editnote").redirect(mNote));
// });
// Alias redirects
dispatcher.register(CommandManager.literal("rmnote").redirect(rootDeleteCmd));
dispatcher.register(CommandManager.literal("deletenote").redirect(rootDeleteCmd));
});
// // Command Root: "/delnote noteIdHere"
// // Aliases: "/rmnote", "/deletenote"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// final var rootDeleteCmd = dispatcher.register(CommandManager.literal("delnote")
// .then(CommandManager.argument("value", IntegerArgumentType.integer())
// .executes(context -> DeleteNote(IntegerArgumentType.getInteger(context, "value"), context))));
// Command Root: "/notegui"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("notegui")
.executes(context -> { OpenNoteGui(context);
return 0;
}));
});
// // Alias redirects
// dispatcher.register(CommandManager.literal("rmnote").redirect(rootDeleteCmd));
// dispatcher.register(CommandManager.literal("deletenote").redirect(rootDeleteCmd));
// });
// Command Root: "/notelist"
// Aliases: "/listnotes"
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
final var rootListNotes = dispatcher.register(CommandManager.literal("notelist")
.executes(context -> { ListNotes(context);
return 0;
}));
// // Command Root: "/notegui"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// dispatcher.register(CommandManager.literal("notegui")
// .executes(context -> { OpenNoteGui(context);
// return 0;
// }));
// });
dispatcher.register(CommandManager.literal("listnotes").redirect(rootListNotes));
});
// // Command Root: "/notelist"
// // Aliases: "/listnotes"
// CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
// final var rootListNotes = dispatcher.register(CommandManager.literal("notelist")
// .executes(context -> { ListNotes(context);
// return 0;
// }));
// dispatcher.register(CommandManager.literal("listnotes").redirect(rootListNotes));
// });
}
/////////////////////////////////////////////////////////////////////////////

View File

@ -28,10 +28,14 @@ import java.util.List;
import org.apache.commons.io.FileUtils;
import java.util.ArrayList;
import java.util.HashMap;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.Utils.CustomExceptions.*;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtList;
public class ConfigManager {
@ -87,6 +91,63 @@ public class ConfigManager {
return ret;
}
// Writes NBT (binary?) data to a file
public void WriteNbtListToFile(String fileName, String key, NbtList data) {
fileName = "config/keeblarcraft/" + fileName;
File file = new File(fileName);
if (!file.exists()) {
file.getParentFile().mkdirs();
}
try {
NbtCompound compound = new NbtCompound();
compound.put(key, data);
NbtIo.writeCompressed(compound, file);
} catch (Exception e) {
e.printStackTrace();
}
}
// Side effect: Files are deleted after being read. Use WriteNbt to write back
public HashMap<String, NbtList> ReadAllNbtListFromDirectory(String dir, int listType) {
HashMap<String, NbtList> list = new HashMap<String, NbtList>();
dir = "config/keeblarcraft/" + dir;
File directory = new File(dir);
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
String key = file.getName().substring(0, file.getName().length() - 4);
NbtList nbtList = ReadNbtListFromFile(file, key, listType);
if (nbtList != null) {
// Subtract out '.nbt' from name
list.put(key, nbtList);
file.delete();
}
}
}
return list;
}
public NbtList ReadNbtListFromFile(File file, String key, int listType) {
NbtList list = null;
try {
NbtCompound c = NbtIo.readCompressed(file);
list = c.getList(key, listType);
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
// WriteToFile
//
// Will write or append to file (valid modes: "w" or "a") if file is available. Returns false if not
@ -130,13 +191,14 @@ public class ConfigManager {
// NOTE: THIS DOES NOT SAFE UPDATE THE KEY OBJECT. PRE-EXISTING DATA WILL BE DELETED FOREVER
public void WriteToJsonFile(String fileName, Object data) throws FILE_WRITE_EXCEPTION {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
fileName = "config/keeblarcraft/" + fileName;
try {
FileWriter writer = new FileWriter(fileName);
gson.toJson(data, writer);
writer.flush();
writer.close();
} catch (JsonIOException | IOException e) {
System.out.println(ChatUtil.ColoredString("Could not successfully write to json file", CONSOLE_COLOR.RED));
System.out.println(ChatUtil.ColoredString("Could not successfully write to json file [" + fileName + "]", CONSOLE_COLOR.RED));
throw new FILE_WRITE_EXCEPTION();
}
}
@ -147,6 +209,8 @@ public class ConfigManager {
public <T> T GetJsonObjectFromFile(String fileName, Class<T> classToConvertTo) throws JsonSyntaxException {
Gson gson = new Gson();
String ret = "";
fileName = "config/keeblarcraft/" + fileName;
System.out.println("Call to GetJsonObjectFromFile with path + file being " + fileName);
// hot fix: Not sure how to return "false" for invalid conversion when I'm forced to convert or just catch... Look into a better
// return value in the future - but for now throw JsonSyntaxException no matter what exception is caught
@ -182,6 +246,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()) {

View File

@ -0,0 +1,103 @@
package jesse.keeblarcraft.ConfigMgr;
import java.util.ArrayList;
import java.util.List;
import jesse.keeblarcraft.Utils.DirectionalVec;
import jesse.keeblarcraft.Utils.CustomExceptions.FILE_WRITE_EXCEPTION;
public class GeneralConfig {
private static GeneralConfig static_inst;
ConfigManager cfgMgr = new ConfigManager();
private ImplementedConfig config;
public static GeneralConfig GetInstance() {
if (static_inst == null) {
static_inst = new GeneralConfig();
}
return static_inst;
}
// Guarentee to not be null
public ImplementedConfig GetConfig() {
if (config == null) {
config = new ImplementedConfig();
FlashConfig();
}
return config;
}
public GeneralConfig() {
Boolean existingFile = false;
try {
config = cfgMgr.GetJsonObjectFromFile("general.json", ImplementedConfig.class);
System.out.println("Read in config. Random value: " + config.global_spawn.x);
existingFile = true;
} catch(Exception e) {
System.out.println("FAILED TO READ IN GENERALCONFIG");
e.printStackTrace();
}
if (!existingFile) {
config = new ImplementedConfig();
FlashConfig();
}
if (config == null) {
config = new ImplementedConfig();
FlashConfig();
}
}
// Can return null; if null then do not spawn the player because global spawn wasn't set!
public DirectionalVec GetSpawnCoords() {
System.out.println("Among us");
System.out.println("GetSpawnCoords called. is global_spawn null? " + (config.global_spawn == null ? "YES": "NO"));
return config.global_spawn;
}
public void SetSpawnCoords(DirectionalVec vec) {
config.global_spawn = vec;
FlashConfig();
}
// The idea behind this function is a one time check option. If you have the UUID, they must now be joining
// so we wrap adding them to the list in one check
public Boolean IsNewPlayer(String uuid) {
System.out.println("IsNewPlayer called. List has: " + config.playerList);
Boolean isNew = !config.playerList.contains(uuid);
System.out.println("Value of isNew is " + isNew);
if (isNew) {
config.playerList.add(uuid);
FlashConfig();
}
return isNew;
}
public String GetMOTD() {
return config.MOTD;
}
private class ImplementedConfig {
public String MOTD = "Welcome to the server! Have fun!";
// This is lazy, but this will fill with every unique UUID that has joined the server. This is how I am checking
// to see if a player has just joined the server for a first time or not
public List<String> playerList = new ArrayList<>();
DirectionalVec global_spawn;
}
private void FlashConfig() {
System.out.println("Attempting to write generalconfig to file. Is null? " + (config == null ? "YES" : "NO"));
try {
cfgMgr.WriteToJsonFile("general.json", config);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println("Caught FileWriteException from general config writing. uh oh!");
}
}
}

View File

@ -0,0 +1,86 @@
// package jesse.keeblarcraft.CustomBlocks.BlockEntities;
// import jesse.keeblarcraft.Keeblarcraft;
// import jesse.keeblarcraft.CustomBlocks.BlockManager;
// import jesse.keeblarcraft.world.ImplementedInventory;
// import net.minecraft.block.BlockState;
// import net.minecraft.block.entity.BlockEntity;
// import net.minecraft.entity.player.PlayerEntity;
// import net.minecraft.entity.player.PlayerInventory;
// import net.minecraft.inventory.Inventories;
// import net.minecraft.item.ItemStack;
// import net.minecraft.nbt.NbtCompound;
// import net.minecraft.registry.RegistryWrapper;
// import net.minecraft.screen.NamedScreenHandlerFactory;
// import net.minecraft.screen.PropertyDelegate; // for syncing client + server
// import net.minecraft.screen.ScreenHandler;
// import net.minecraft.text.Text;
// import net.minecraft.util.collection.DefaultedList;
// import net.minecraft.util.math.BlockPos;
// public class MagicChestBlockEntity extends BlockEntity implements NamedScreenHandlerFactory, ImplementedInventory {
// private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize(256, ItemStack.EMPTY);
// protected final PropertyDelegate propertyDelegate;
// private int progress = 0;
// private int maxProgress = 72;
// public MagicChestBlockEntity(BlockPos pos, BlockState state) {
// super(Keeblarcraft.MAGIC_CHEST_BLOCK_ENTITY, pos, state);
// this.propertyDelegate = new PropertyDelegate() {
// @Override
// public int get(int index) {
// return switch(index) {
// case 0 -> MagicChestBlockEntity.this.progress;
// case 1 -> MagicChestBlockEntity.this.maxProgress;
// default -> 0;
// };
// }
// @Override
// public void set(int index, int value) {
// switch (index) {
// case 0 -> MagicChestBlockEntity.this.progress = value;
// case 1 -> MagicChestBlockEntity.this.maxProgress = value;
// };
// }
// // MUST be size of how many integers we are synchronizing
// @Override
// public int size() {
// return 2;
// }
// };
// }
// @Override
// public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
// // Screen handler will sync the inventory
// return new MagicChestScreenHandler(syncId, playerInventory, this);
// }
// @Override
// public DefaultedList<ItemStack> getItems() {
// return inventory;
// }
// @Override
// public Text getDisplayName() {
// return Text.translatable(getCachedState().getBlock().getTranslationKey());
// }
// @Override
// public void readNbt(NbtCompound nbt) {
// super.readNbt(nbt);
// Inventories.readNbt(nbt, this.inventory);
// }
// @Override
// public void writeNbt(NbtCompound nbt) {
// super.writeNbt(nbt);
// Inventories.writeNbt(nbt, this.inventory);
// }
// }

View File

@ -0,0 +1,43 @@
// package jesse.keeblarcraft.CustomBlocks.Blocks;
// import jesse.keeblarcraft.CustomBlocks.BlockEntities.MagicChestBlockEntity;
// import net.minecraft.block.BlockRenderType;
// import net.minecraft.block.BlockState;
// import net.minecraft.block.BlockWithEntity;
// import net.minecraft.block.entity.BlockEntity;
// import net.minecraft.entity.player.PlayerEntity;
// import net.minecraft.screen.NamedScreenHandlerFactory;
// import net.minecraft.util.ActionResult;
// import net.minecraft.util.Hand;
// import net.minecraft.util.hit.BlockHitResult;
// import net.minecraft.util.math.BlockPos;
// import net.minecraft.world.World;
// public class MagicChestBlock extends BlockWithEntity {
// public MagicChestBlock(Settings settings) {
// super(settings);
// }
// @Override
// public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
// return new MagicChestBlockEntity(pos, state);
// }
// @Override
// public BlockRenderType getRenderType(BlockState state) {
// return BlockRenderType.MODEL;
// }
// @Override
// public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
// if (!world.isClient) {
// NamedScreenHandlerFactory screenHandlerFactory = ((MagicChestBlockEntity) world.getBlockEntity(pos));
// if (screenHandlerFactory != null) {
// player.openHandledScreen(screenHandlerFactory);
// }
// }
// return ActionResult.SUCCESS;
// }
// }

View File

@ -0,0 +1,105 @@
package jesse.keeblarcraft.EventMgr;
import java.util.HashMap;
import java.util.Map.Entry;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Utils.ChatUtil;
import jesse.keeblarcraft.Utils.ChatUtil.CONSOLE_COLOR;
import jesse.keeblarcraft.world.dimension.ModDimensions;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
public class DimensionLoadingEvent {
private static DimensionLoadingEvent static_inst;
public static DimensionLoadingEvent GetInstance() {
if (static_inst == null) {
static_inst = new DimensionLoadingEvent();
}
return static_inst;
}
private static class InventoryWrapper {
public HashMap<String, NbtList> inventories = new HashMap<String, NbtList>();
}
private static InventoryWrapper iw = new InventoryWrapper();
private static String CONFIG_LOCATION = "misc/dim_loading_cached_inventories/";
ConfigManager config = new ConfigManager();
public DimensionLoadingEvent() {
// read config
iw.inventories = config.ReadAllNbtListFromDirectory(CONFIG_LOCATION, NbtElement.COMPOUND_TYPE);
}
// TODO: In the future when the attribute system is more complete this will need to filter a whitelist of items
// from the that system + story mode because some items will be able to transcend dimensions!
public void HandleWorldMove(ServerPlayerEntity player, ServerWorld origin, ServerWorld destination) {
System.out.println("World move event called!");
// Player is ENTERING the custom dimension; strip their inventory!
if (destination.getDimensionEntry().matchesKey(ModDimensions.KEEBLAR_DIM_TYPE) && (!iw.inventories.containsKey(player.getUuidAsString()))) {
// Make sure player is in map. For now we only care about storing OVERWORLD inventory. We DO NOT care about
// the dimension inventory!
// if (!iw.inventories.containsKey(player.getUuidAsString())) {
// Copy the nbt into the list
NbtList inventoryNbt = new NbtList();
player.getInventory().writeNbt(inventoryNbt);
iw.inventories.put(player.getUuidAsString(), inventoryNbt);
player.getInventory().clear();
// }
// Player is LEAVING the custom dimension. Give them their stuff back
} else if (origin.getDimensionEntry().matchesKey(ModDimensions.KEEBLAR_DIM_TYPE) && iw.inventories.containsKey(player.getUuidAsString())) {
// if (iw.inventories.containsKey(player.getUuidAsString())) {
player.getInventory().readNbt(iw.inventories.get(player.getUuidAsString()));
iw.inventories.remove(player.getUuidAsString());
// }
}
FlashConfig();
}
// Call on server close before we lose the volatile memory
public void SaveInventories() {
System.out.println("Call to save inventories. Flashing IW.Inventories with size " + iw.inventories.size());
FlashConfig();
}
public void CheckPlayer(ServerPlayerEntity player) {
// Check the players logged in world. If they are logging into the overworld - we need to see if they are in our
// map and give them the inventory back
if ((!player.getWorld().getDimensionEntry().matchesKey(ModDimensions.KEEBLAR_DIM_TYPE)) && iw.inventories.containsKey(player.getUuidAsString())) {
// We need to store the contents of the nbt into a regular inventory and magical chest and give it back to the player
player.sendMessage(Text.of("[IMPORTANT]: It appears you have a saved inventory of items on the server! You can claim it with /magic-inventory claim"));
}
}
// Fetches an inventory from this list. Use MagicInventory to store it along the transfer way!
public NbtList GetInventory(String uuid) {
NbtList nbt = iw.inventories.get(uuid);
iw.inventories.remove(uuid);
return nbt;
}
public void FlashConfig() {
try {
// config.WriteToJsonFile(CONFIG_LOCATION, iw);
// First, ensure list is size > 0
for (Entry<String, NbtList> list : iw.inventories.entrySet()) {
if (list.getValue().size() > 0) {
config.WriteNbtListToFile(CONFIG_LOCATION + list.getKey() + ".nbt", list.getKey(), list.getValue());
}
}
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not flash dimension loading configuration file", CONSOLE_COLOR.RED));
}
}
}

View File

@ -0,0 +1,57 @@
package jesse.keeblarcraft.EventMgr;
import jesse.keeblarcraft.AttributeMgr.AttributeMgr;
import jesse.keeblarcraft.AttributeMgr.AttributeTree;
import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.ConfigMgr.GeneralConfig;
import jesse.keeblarcraft.Utils.DirectionalVec;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
public class PlayerJoinListener {
private static PlayerJoinListener static_inst;
public static PlayerJoinListener GetInstance() {
if (static_inst == null) {
static_inst = new PlayerJoinListener();
}
return static_inst;
}
public void HandleServerJoinEvent(ServerPlayNetworkHandler handler, PacketSender sender, MinecraftServer server) {
var player = handler.player;
// Handle skill tree map instance
if (AttributeMgr.activeTrees.containsKey(player.getUuidAsString()) == false) {
AttributeMgr.activeTrees.put(player.getUuidAsString(), new AttributeTree(player.getUuidAsString()));
}
// Handle first time joining events (world spawn teleport, MOTD, etc)
System.out.println("Running first time login stuff");
IsFirstTimeLogin(player, server);
}
// this will be reworked in the future
private void IsFirstTimeLogin(ServerPlayerEntity player, MinecraftServer server) {
// Send the MOTD + Spawn in global spawn
player.sendMessage(Text.of(GeneralConfig.GetInstance().GetMOTD()));
if (GeneralConfig.GetInstance().IsNewPlayer(player.getUuidAsString())) {
DirectionalVec coords = GeneralConfig.GetInstance().GetSpawnCoords();
if (coords != null) {
for (ServerWorld world : player.getServer().getWorlds()) {
if (world.getRegistryKey().toString().equals(coords.world.toString())) {
try {
player.teleport(world, coords.x, coords.y, coords.z, coords.yaw, coords.pitch);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
}

View File

@ -0,0 +1,290 @@
package jesse.keeblarcraft.FactionMgr;
import static java.util.Map.entry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
// This class is written to a config file typically and represents
// all the details surrounding a faction
public class FactionConfig {
// Enum may be extended in future
public static enum VALID_FACTION_ROLES {
OWNER,
COOWNER,
MANAGEMENT,
EMPLOYEE
}
private static Map<VALID_FACTION_ROLES, Integer> ROLE_LEVELS = Map.ofEntries
(
entry (VALID_FACTION_ROLES.EMPLOYEE, 0),
entry (VALID_FACTION_ROLES.MANAGEMENT, 1),
entry (VALID_FACTION_ROLES.COOWNER, 2),
entry (VALID_FACTION_ROLES.OWNER, 3)
);
// The below map is 100x easier to just have statically typed here and maintained manually than expensively getting value by key (I am also lazy)
private static Map<Integer, VALID_FACTION_ROLES> ROLES_BY_LEVEL = Map.ofEntries
(
entry (0, VALID_FACTION_ROLES.EMPLOYEE),
entry (1, VALID_FACTION_ROLES.MANAGEMENT),
entry (2, VALID_FACTION_ROLES.COOWNER),
entry (3, VALID_FACTION_ROLES.OWNER)
);
public class WriteableFaction {
// Key = Player UUID
// Val = Faction role of player
HashMap<String, VALID_FACTION_ROLES> factionPlayerList = new HashMap<String, VALID_FACTION_ROLES>();
// Key = Player DISPLAY name
// Val = Faction role of player
HashMap<String, VALID_FACTION_ROLES> DISPLAY_ONLY_LIST = new HashMap<String, VALID_FACTION_ROLES>();
// List contains UUID of players who are openly invited to this faction
List<String> openInvites = new ArrayList<String>();
Integer factionBankBalance;
Integer factionPower;
String factionName;
}
public Boolean CreateFaction(String factionName, String creatorUuid, String creatorDisplayName) {
Boolean success = false;
if (!IsValid(factionName)) {
WriteableFaction newFaction = new WriteableFaction();
newFaction.factionPlayerList.put(creatorUuid, VALID_FACTION_ROLES.OWNER);
newFaction.DISPLAY_ONLY_LIST.put(creatorDisplayName, VALID_FACTION_ROLES.OWNER);
newFaction.factionBankBalance = 0;
newFaction.factionPower = 1;
allFactions.put(factionName, newFaction);
success = true;
}
return success;
}
public Boolean DeleteFaction(String factionName, String callerUuid) {
Boolean success = false;
// faction must exist
if (IsValid(factionName)) {
// faction must contain player
if (allFactions.get(factionName).factionPlayerList.containsKey(callerUuid)) {
// player must be owner of faction
if (allFactions.get(factionName).factionPlayerList.get(callerUuid) == VALID_FACTION_ROLES.OWNER) {
//TODO: BankManager will connect with this in future and the money of the faction must go somewhere!
allFactions.remove(factionName);
success = true;
}
}
}
return success;
}
private Boolean CanInvite(String factionName, String callerUuid) {
Boolean success = false;
if (IsValid(factionName)) {
if (allFactions.get(factionName).factionPlayerList.containsKey(callerUuid)) {
VALID_FACTION_ROLES callerRole = allFactions.get(factionName).factionPlayerList.get(callerUuid);
if (callerRole == VALID_FACTION_ROLES.MANAGEMENT || callerRole == VALID_FACTION_ROLES.COOWNER || callerRole == VALID_FACTION_ROLES.OWNER) {
success = true;
}
}
}
return success;
}
private Boolean IsOnInviteList(String factionName, String playerUuid) {
Boolean success = false;
if (IsValid(factionName)) {
if (allFactions.get(factionName).openInvites.contains(playerUuid)) {
success = true;
}
}
return success;
}
private void AddToFaction(String factionName, String playerUuid, String playerDisplayName) {
if (allFactions.containsKey(factionName)) {
allFactions.get(factionName).factionPlayerList.put(playerUuid, VALID_FACTION_ROLES.EMPLOYEE);
allFactions.get(factionName).DISPLAY_ONLY_LIST.put(playerDisplayName, VALID_FACTION_ROLES.EMPLOYEE);
}
}
public Boolean InvitePlayerToFaction(String factionName, String callerUuid, String invitedUuid) {
Boolean success = false;
if (CanInvite(factionName, callerUuid)) {
allFactions.get(factionName).openInvites.add(invitedUuid);
success = true;
}
return success;
}
public Boolean JoinFaction(String factionName, String playerUuid, String playerDisplayName) {
Boolean success = false;
if (IsOnInviteList(factionName, playerUuid) ) {
AddToFaction(factionName, playerUuid, playerDisplayName);
success = true;
}
return success;
}
public Boolean LeaveFaction(String factionName, String playerUuid, String playerName) {
Boolean success = false;
if (IsValid(factionName)) {
allFactions.get(factionName).factionPlayerList.remove(playerUuid);
allFactions.get(factionName).DISPLAY_ONLY_LIST.remove(playerName);
// TODO: In future add ability if owner leave then promote next person
// Delete faction if all the players are gone
if (allFactions.get(factionName).factionPlayerList.size() == 0) {
allFactions.remove(factionName);
}
success = true;
}
return success;
}
private Boolean HasPlayer(String factionName, String playerUuid) {
Boolean success = false;
if (IsValid(factionName)) {
success = allFactions.get(factionName).factionPlayerList.containsKey(playerUuid);
}
return success;
}
public Boolean IsValid(String factionName) {
if (allFactions.containsKey(factionName)) {
return true;
} else {
return false;
}
}
// Empty string means no faction found. This is expensive!
public String FindFactionOfPlayer(String playerUuid) {
String faction = "";
System.out.println("Attempting to find player factions with uuid " + playerUuid);
for (Entry<String, WriteableFaction> entry : allFactions.entrySet()) {
if (entry.getValue().factionPlayerList.containsKey(playerUuid)) {
System.out.println("FAC [" + entry.getKey() + "]: PLAY-LIST: " + entry.getValue().factionPlayerList);
faction = entry.getKey();
break;
}
}
return faction;
}
public Boolean PromotePlayer(String factionName, String callerUuid, String promoteeUuid, String promoteeDisplayName) {
Boolean success = false;
if (CanInvite(factionName, callerUuid) && HasPlayer(factionName, promoteeUuid)) {
VALID_FACTION_ROLES callerRole = allFactions.get(factionName).factionPlayerList.get(callerUuid);
VALID_FACTION_ROLES promoteeRole = allFactions.get(factionName).factionPlayerList.get(promoteeUuid);
Integer callerRoleLevel = ROLE_LEVELS.get(callerRole);
Integer promoteeRoleLevel = ROLE_LEVELS.get(promoteeRole);
// Factions is setup so that anyone can promote anybody UNDERNEATH them. However, you CANNOT promote a player to your level!
if (callerRoleLevel > promoteeRoleLevel + 1) {
// Get the new employee role
promoteeRole = ROLES_BY_LEVEL.get(promoteeRoleLevel + 1);
// Update role in faction
allFactions.get(factionName).factionPlayerList.put(promoteeUuid, promoteeRole);
allFactions.get(factionName).DISPLAY_ONLY_LIST.put(promoteeDisplayName, promoteeRole);
success = true;
}
}
return success;
}
// Factions is setup in a way where anybody can demote anybody underneath them and demotions of the lowest level lead to an automatic kick. The
// same behavior is kept here
public Boolean CanKickPlayer(String factionName, String callerUuid, String kickeeUuid, String kickeeDisplayName) {
Boolean success = false;
if (IsValid(factionName) && HasPlayer(factionName, kickeeUuid)) {
VALID_FACTION_ROLES callerRole = allFactions.get(factionName).factionPlayerList.get(callerUuid);
VALID_FACTION_ROLES kickeeRole = allFactions.get(factionName).factionPlayerList.get(kickeeUuid);
Integer callerRoleLevel = ROLE_LEVELS.get(callerRole);
Integer kickeeRoleLevel = ROLE_LEVELS.get(kickeeRole);
if (callerRoleLevel > kickeeRoleLevel) {
success = true;
}
}
return success;
}
private Boolean KickPlayer(String factionName, String callerUuid, String kickeeUuid, String kickeeDisplayName) {
Boolean success = false;
if (CanKickPlayer(factionName, callerUuid, kickeeUuid, kickeeDisplayName)) {
allFactions.get(factionName).factionPlayerList.remove(kickeeUuid);
allFactions.get(factionName).DISPLAY_ONLY_LIST.remove(kickeeDisplayName);
success = true;
}
return success;
}
public Boolean DemotePlayer(String factionName, String callerUuid, String demoteeUuid, String demoteeDisplayName) {
Boolean success = false;
if (CanInvite(factionName, callerUuid) && HasPlayer(factionName, demoteeUuid)) {
VALID_FACTION_ROLES callerRole = allFactions.get(factionName).factionPlayerList.get(callerUuid);
VALID_FACTION_ROLES demoteeRole = allFactions.get(factionName).factionPlayerList.get(demoteeUuid);
Integer callerRoleLevel = ROLE_LEVELS.get(callerRole);
Integer demoteeRoleLevel = ROLE_LEVELS.get(demoteeRole);
// Factions is setup so that anyone can demote anybody underneath them & the lowest level will cause a demotion to be a KICK from faction
if (callerRoleLevel > demoteeRoleLevel) {
// If the role level would be lower than bottom level, KICK player
if (demoteeRoleLevel - 1 < ROLE_LEVELS.get(VALID_FACTION_ROLES.EMPLOYEE)) {
success = KickPlayer(factionName, callerUuid, demoteeUuid, demoteeDisplayName);
} else {
// Regular demotion!
demoteeRole = ROLES_BY_LEVEL.get(demoteeRoleLevel - 1);
// Update faction
allFactions.get(factionName).factionPlayerList.put(demoteeUuid, demoteeRole);
allFactions.get(factionName).DISPLAY_ONLY_LIST.put(demoteeDisplayName, demoteeRole);
success = true;
}
}
}
return success;
}
public List<String> ListOfAllFactions() {
List<String> facs = new ArrayList<String>();
System.out.println("ListOfFactions - map size: " + allFactions.size());
for (Entry<String, WriteableFaction> entry : allFactions.entrySet()) {
System.out.println("Adding fac " + entry.getKey() + " to fac");
facs.add(entry.getKey());
}
return facs;
}
// Key = Faction identifier
// Val = Faction object
HashMap<String, WriteableFaction> allFactions = new HashMap<String, WriteableFaction>();
}

View File

@ -0,0 +1,167 @@
/*
*
* FactionManager
*
* Class is responsible for keeping track of factions chosen by the players in the game and saves to the configuration
* file for persistent data storage. Class handles checks as well for eligibility purposes (making sure players can join, etc)
*
*/
package jesse.keeblarcraft.FactionMgr;
import java.util.List;
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;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
public class FactionManager {
private static String FACTION_CFG_FILE = "config/keeblarcraft/factions/factions.json";
ConfigManager config = new ConfigManager();
private static FactionManager static_inst;
public static FactionManager GetInstance() {
if (static_inst == null) {
static_inst = new FactionManager();
}
return static_inst;
}
private class FactionConfigClassWrapper {
FactionConfig factions = new FactionConfig();
}
FactionConfigClassWrapper factionConfig;// = new FactionConfigClassWrapper();
// Constructor
public FactionManager() {
// Read in config at start of object
System.out.println("FACTIONMANAGER CONSTRUCTOR CALLED");
Boolean existingFile = false;
factionConfig = new FactionConfigClassWrapper();
Boolean tmpchck = factionConfig == null;
System.out.println("Is factionConfig null still? " + (tmpchck ? "YES" : "NO"));
try {
factionConfig = config.GetJsonObjectFromFile(FACTION_CFG_FILE, FactionConfigClassWrapper.class);
tmpchck = factionConfig == null;
System.out.println("Is factionconfig null after trying to load stuff? " + (tmpchck ? "YES" : "NO"));
existingFile = true;
} catch (Exception e) {
// Do nothing
}
// Create the file if it didn't exist before
if (!existingFile)
{
try {
config.CreateDirectory(FACTION_CFG_FILE);
FlashConfig();
} catch (Exception e) {
System.out.println(ChatUtil.ColoredString("Could not write to file", CONSOLE_COLOR.RED));
}
}
if (factionConfig == null) {
// the only way for this to be possible is if the read-in was bad. flash config file then try again
factionConfig = new FactionConfigClassWrapper();
//TODO: Add safe-guard in here to check if default faction dir exists and move it to OLD/CORRUPTED (so data is not nuked from orbit)
factionConfig.factions = new FactionConfig();
FlashConfig();
}
}
public Boolean LeaveFaction(ServerPlayerEntity player) {
Boolean success = false;
String playerFac = factionConfig.factions.FindFactionOfPlayer(player.getUuidAsString());
if (playerFac != "") {
success = factionConfig.factions.LeaveFaction(playerFac, player.getUuidAsString(), player.getDisplayName().toString());
player.sendMessage(Text.of("[Factions]: You left your faction!"));
} else {
player.sendMessage(Text.of("[Factions]: You are not in a faction!"));
}
return success;
}
public Boolean CreateFaction(String factionName, ServerPlayerEntity creator) {
Boolean success = false;
String facOfPlayer = factionConfig.factions.FindFactionOfPlayer(creator.getUuidAsString());
if (facOfPlayer == "") {
success = factionConfig.factions.CreateFaction(factionName, creator.getUuidAsString(), creator.getDisplayName().toString());
if (!success) {
creator.sendMessage(Text.of("[Factions]: Could not create faction - faction already exists."));
} else {
creator.sendMessage(Text.of("[Factions]: Successfully created faction!"));
FlashConfig();
}
} else {
creator.sendMessage(Text.of("[Factions]: You are already in a faction! You cannot create one."));
}
return success;
}
public Boolean DeleteFaction(String factionName, ServerPlayerEntity caller) {
Boolean success = factionConfig.factions.DeleteFaction(factionName, caller.getUuidAsString());
if (!success) {
caller.sendMessage(Text.of("[Factions]: Could not delete faction. You must be owner & faction must exist."));
} else {
caller.sendMessage(Text.of("[Factions]: Successfully deleted faction."));
FlashConfig();
}
return success;
}
public List<String> ListOfFactions() {
System.out.println("Callthrough of listoffactions");
return factionConfig.factions.ListOfAllFactions();
}
public String GetFactionOfPlayer(String playerUuid) {
return factionConfig.factions.FindFactionOfPlayer(playerUuid);
}
public Boolean PromotePlayer(ServerPlayerEntity caller, String promoteeUuid, String promoteeDisplayName) {
Boolean success = factionConfig.factions.PromotePlayer(GetFactionOfPlayer(caller.getUuidAsString()), caller.getUuidAsString(), promoteeUuid, promoteeDisplayName);
if (!success) {
caller.sendMessage(Text.of("[Factions]: Could not promote player - you need to be a higher rank than them and they cannot be promoted to your level!"));
} else {
caller.sendMessage(Text.of("[Factions]: Successfully promoted player!"));
}
return success;
}
public Boolean DemotePlayer(ServerPlayerEntity caller, String promoteeUuid, String promoteeDisplayName) {
Boolean success = factionConfig.factions.DemotePlayer(GetFactionOfPlayer(caller.getUuidAsString()), caller.getUuidAsString(), promoteeUuid, promoteeDisplayName);
if (!success) {
caller.sendMessage(Text.of("[Factions]: Could not demote player - you need to be a higher rank than them to demote them!"));
} else {
caller.sendMessage(Text.of("[Factions]: Successfully demoted player!"));
}
return success;
}
public void FlashConfig() {
try {
config.WriteToJsonFile(FACTION_CFG_FILE, factionConfig);
} catch (FILE_WRITE_EXCEPTION e) {
System.out.println("config writing of faction file failed. oh well!");
}
}
}

View File

@ -1,15 +0,0 @@
/*
*
* TeamManager
*
* Class is responsible for keeping track of teams/factions chosen by the players in the game and saves to the configuration
* file for persistent data storage. Class handles checks as well for eligibility purposes (making sure players can join, etc)
*
*/
package jesse.keeblarcraft.FactionMgr;
public class TeamManager {
// Class controls managing teams and pulling from configuration file and loading to configuration file
}

View File

@ -12,11 +12,25 @@
package jesse.keeblarcraft;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents.ServerStopping;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyrptonaught.customportalapi.api.CustomPortalBuilder;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.Items;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
@ -24,9 +38,14 @@ 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.CustomBlocks.BlockEntities.MagicChestBlockEntity;
// import jesse.keeblarcraft.CustomBlocks.Blocks.MagicChestBlock;
import jesse.keeblarcraft.CustomItems.ItemManager;
import jesse.keeblarcraft.EventMgr.DimensionLoadingEvent;
import jesse.keeblarcraft.EventMgr.PlayerJoinListener;
import jesse.keeblarcraft.EventMgr.ServerTickListener;
import jesse.keeblarcraft.GuiMgr.TreeHandler;
import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION;
@ -44,6 +63,12 @@ public class Keeblarcraft implements ModInitializer {
Identifier.of(Keeblarcraft.MOD_ID, "tree_gui"),
new ScreenHandlerType<>(TreeHandler::new, FeatureSet.empty()));
// public static final Block MAGIC_CHEST_BLOCK = Registry.register(Registries.BLOCK, Identifier.of(Keeblarcraft.MOD_ID, "magic_chest_block"),
// new MagicChestBlock(AbstractBlock.Settings.copy(Blocks.CHEST)));
// public static final BlockEntityType<MagicChestBlockEntity> MAGIC_CHEST_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE,
// Identifier.of(Keeblarcraft.MOD_ID, "magic_chest_block_entity"), BlockEntityType.Builder.create(MagicChestBlockEntity::new, MAGIC_CHEST_BLOCK).build());
CustomCommandManager cmdMgr = new CustomCommandManager();
Setup setup = Setup.GetInstance();
@ -63,12 +88,16 @@ public class Keeblarcraft implements ModInitializer {
// This is a very special case where this must be in this classes' initializer
// method
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
var player = handler.player;
Keeblarcraft.LOGGER.info("Player " + player.getName() + " has logged in. Creating tree...");
// var player = handler.player;
// Keeblarcraft.LOGGER.info("Player " + player.getName() + " has logged in. Creating tree...");
// player.sendMessage(Text.of("Welcome to the Keeblcraft RPG Server!"));
if (AttributeMgr.activeTrees.containsKey(player.getUuidAsString()) == false) {
AttributeMgr.activeTrees.put(player.getUuidAsString(), new AttributeTree(player.getUuidAsString()));
}
// if (AttributeMgr.activeTrees.containsKey(player.getUuidAsString()) == false) {
// AttributeMgr.activeTrees.put(player.getUuidAsString(), new AttributeTree(player.getUuidAsString()));
// }
System.out.println("ServerPlayConnectionEvents.JOIN called");
PlayerJoinListener.GetInstance().HandleServerJoinEvent(handler, sender, server);
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
@ -80,6 +109,17 @@ public class Keeblarcraft implements ModInitializer {
}
});
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> {
System.out.println("Calling back...");
DimensionLoadingEvent.GetInstance().HandleWorldMove(player, origin, destination);
});
ServerLifecycleEvents.SERVER_STOPPING.register((server) ->{
// Stuff here
System.out.println("SERVER_STOPPING callback called.");
DimensionLoadingEvent.GetInstance().SaveInventories();
});
// Initialize our ticks!!
ServerTickListener.InitializeServerTicks();
@ -90,6 +130,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
@ -98,6 +141,17 @@ public class Keeblarcraft implements ModInitializer {
// Register blocks
BlockList.RegisterBlocks();
// World generation
// Custom portal generator
System.out.println("BUILDING CUSTOM PORTAL");
CustomPortalBuilder.beginPortal()
.frameBlock(Blocks.GOLD_BLOCK)
.lightWithItem(Items.ENDER_EYE)
.destDimID(new Identifier(Keeblarcraft.MOD_ID, "keeblarcraftdim"))
.tintColor(234, 183, 8)
.registerPortal();
} catch (SETUP_FAILED_EXCEPTION e) {
System.out.println(ChatUtil.ColoredString("ERROR. Setup failed to initialize environment. Mod likely does not have read/write permissions inside area. Mod will now close out.", CONSOLE_COLOR.RED));

View File

@ -0,0 +1,13 @@
package jesse.keeblarcraft.Utils;
import net.minecraft.registry.RegistryKey;
import net.minecraft.world.World;
public class DirectionalVec {
public RegistryKey<World> world;
public double x;
public double y;
public double z;
public float yaw;
public float pitch;
}

View File

@ -0,0 +1,114 @@
package jesse.keeblarcraft.Utils;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
public class HelpBuilder {
private String COLOR_START = "§";
private String COLOR_END = "§f";
public enum COLOR_CODE {
BLUE,
GRAY,
GOLD,
RED,
GREEN
}
private String getColor(COLOR_CODE code) {
String colorStr = COLOR_START;
switch(code) {
case BLUE:
return colorStr + "9";
case GRAY:
return colorStr + "7";
case GOLD:
return colorStr + "6";
case RED:
return colorStr + "4";
case GREEN:
return colorStr + "2";
}
// If this code is reachable, then someone has not properly handled the above switch-case
return colorStr;
}
public MutableText MakeCopyableTxt(String terminalTxt, String hoverTxt, Integer copyStr) {
return MakeCopyableTxt(terminalTxt, hoverTxt, Integer.toString(copyStr));
}
public MutableText MakeCopyableTxt(String terminalTxt, String hoverTxt, List<String> expandedList) {
String expanded = "[";
int index = 0;
for (String str : expandedList) {
expanded += str;
// Add delimiter if next index is not at size
if (++index < expandedList.size()) {
expanded += ",";
}
}
expanded += "]";
return MakeCopyableTxt(terminalTxt, hoverTxt, expanded);
}
public MutableText MakeCopyableTxt(String terminalTxt, String hoverTxt, String copyStr) {
Text copyableText = Text.of(terminalTxt);
MutableText testTxt = (MutableText) copyableText;
System.out.println("Making hoverable stuff");
testTxt.setStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, copyStr))
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.of(hoverTxt))));
System.out.println("Done making hoverable stuff");
System.out.println("Value of copyAbleText: " + copyableText.getString());
System.out.println("Value of testTxt: " + testTxt.getString());
return testTxt;
}
public String ColorMsg(Integer msg, COLOR_CODE color) {
return getColor(color) + msg + COLOR_END;
}
public List<String> ColorMsg(List<String> msg, COLOR_CODE color) {
List<String> retList = new ArrayList<String>();
for (String str : msg) {
retList.add(getColor(color) + str + COLOR_END);
}
return retList;
}
public String ColorMsg(String msg, COLOR_CODE color) {
return getColor(color) + msg + COLOR_END;
}
// Parses a help command and color codes it. assume everything up to first '.' is the
// help cmd usage and color it with primaryColor. secondaryColor applied to rest
public String FormatMsg(String helpCmd, COLOR_CODE primaryColor, COLOR_CODE secondaryColor) {
String coloredStr = getColor(primaryColor);
List<String> splitStr = List.of(helpCmd.split("\\."));
Boolean isFirst = true;
for (String str : splitStr) {
if (isFirst) {
coloredStr += str;
isFirst = false;
coloredStr += getColor(secondaryColor);
} else {
coloredStr += str;
}
}
return coloredStr + COLOR_END;
}
}

View File

@ -30,6 +30,7 @@ import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION;
// If we do not have these, or only partial - then functionality may be disabled for runtime performance
public final class Setup {
private static Setup static_inst;
private static String GLOBAL_CONFIG = "config/keeblarcraft/";
public Setup() {
System.out.println(ChatUtil.ColoredString("Running system setup and checks...", CONSOLE_COLOR.BLUE));
@ -50,22 +51,24 @@ public final class Setup {
// First time setup variables
private static final List<String> DIRECTORY_LIST = new ArrayList<String>() {{
add("notes"); // Expect 1 file per player!
add("factions"); // Expect 1 file per faction!
add("story"); // Expect 1 file per story chapter!
add("commands"); // Expect 1 file per command that's configurable!
add("events"); // Expect 1 file per event that is configurable!
add("bank");
add("attributes");
add(GLOBAL_CONFIG); // inside config dir
add(GLOBAL_CONFIG + "notes"); // Expect 1 file per player!
add(GLOBAL_CONFIG + "factions"); // Expect 1 file per faction!
add(GLOBAL_CONFIG + "story"); // Expect 1 file per story chapter!
add(GLOBAL_CONFIG + "commands"); // Expect 1 file per command that's configurable!
add(GLOBAL_CONFIG + "events"); // Expect 1 file per event that is configurable!
add(GLOBAL_CONFIG + "bank");
add(GLOBAL_CONFIG + "attributes");
add(GLOBAL_CONFIG + "misc");
}};
// These will be top-level config files above the directories this mod creates
private static final List<String> FILE_LIST = new ArrayList<String>() {{
add("story/general_story_config.json"); // Big config file, determines when players can do certain things at different story levels
add("factions/general_factions_config.json"); // General configuration file for factions stuff
add("events/general_event_config.json"); // General configuration file for story events!
add("general.json"); // The super general configuration file! (May be removed)
add("attributes/general_attribute_config.json");
add(GLOBAL_CONFIG + "story/general_story_config.json"); // Big config file, determines when players can do certain things at different story levels
add(GLOBAL_CONFIG + "factions/general_factions_config.json"); // General configuration file for factions stuff
add(GLOBAL_CONFIG + "events/general_event_config.json"); // General configuration file for story events!
add(GLOBAL_CONFIG + "general.json"); // The super general configuration file! (May be removed)
add(GLOBAL_CONFIG + "attributes/general_attribute_config.json");
}};
// RunChecks()

View File

@ -0,0 +1,28 @@
package jesse.keeblarcraft.datagen;
import java.util.concurrent.CompletableFuture;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class WorldGenerator extends FabricDynamicRegistryProvider {
public WorldGenerator(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFeature) {
super(output, registriesFeature);
}
@Override
public String getName() {
return "Keeblarcraft World Generation";
}
@Override
protected void configure(WrapperLookup registries, Entries entries) {
entries.addAll(registries.getWrapperOrThrow(RegistryKeys.PLACED_FEATURE));
entries.addAll(registries.getWrapperOrThrow(RegistryKeys.BIOME));
entries.addAll(registries.getWrapperOrThrow(RegistryKeys.DIMENSION_TYPE));
}
}

View File

@ -0,0 +1,26 @@
package jesse.keeblarcraft.world;
import jesse.keeblarcraft.datagen.WorldGenerator;
import jesse.keeblarcraft.world.biome.ModBiomes;
import jesse.keeblarcraft.world.dimension.ModDimensions;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
import net.minecraft.registry.RegistryBuilder;
import net.minecraft.registry.RegistryKeys;
public class DataGeneration implements DataGeneratorEntrypoint {
@Override
public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
FabricDataGenerator.Pack pack = fabricDataGenerator.createPack();
pack.addProvider(WorldGenerator::new);
}
@Override
public void buildRegistry(RegistryBuilder registryBuilder) {
registryBuilder.addRegistry(RegistryKeys.BIOME, ModBiomes::bootstrap);
registryBuilder.addRegistry(RegistryKeys.DIMENSION_TYPE, ModDimensions::bootstrapType);
}
}

View File

@ -0,0 +1,140 @@
package jesse.keeblarcraft.world;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventories;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.collection.DefaultedList;
/**
* A simple {@code Inventory} implementation with only default methods + an item
* list getter.
*
* @author Juuz
*
* @from https://wiki.fabricmc.net/tutorial:inventory
*/
public interface ImplementedInventory extends Inventory {
/**
* Retrieves the item list of this inventory.
* Must return the same instance every time it's called.
*/
DefaultedList<ItemStack> getItems();
/**
* Creates an inventory from the item list.
*/
static ImplementedInventory of(DefaultedList<ItemStack> items) {
return () -> items;
}
/**
* Creates a new inventory with the specified size.
*/
static ImplementedInventory ofSize(int size) {
return of(DefaultedList.ofSize(size, ItemStack.EMPTY));
}
/**
* Returns the inventory size.
*/
@Override
default int size() {
return getItems().size();
}
/**
* Checks if the inventory is empty.
*
* @return true if this inventory has only empty stacks, false otherwise.
*/
@Override
default boolean isEmpty() {
for (int i = 0; i < size(); i++) {
ItemStack stack = getStack(i);
if (!stack.isEmpty()) {
return false;
}
}
return true;
}
/**
* Retrieves the item in the slot.
*/
@Override
default ItemStack getStack(int slot) {
return getItems().get(slot);
}
/**
* Removes items from an inventory slot.
*
* @param slot The slot to remove from.
* @param count How many items to remove. If there are less items in the slot
* than what are requested,
* takes all items in that slot.
*/
@Override
default ItemStack removeStack(int slot, int count) {
ItemStack result = Inventories.splitStack(getItems(), slot, count);
if (!result.isEmpty()) {
markDirty();
}
return result;
}
/**
* Removes all items from an inventory slot.
*
* @param slot The slot to remove from.
*/
@Override
default ItemStack removeStack(int slot) {
return Inventories.removeStack(getItems(), slot);
}
/**
* Replaces the current stack in an inventory slot with the provided stack.
*
* @param slot The inventory slot of which to replace the itemstack.
* @param stack The replacing itemstack. If the stack is too big for
* this inventory ({@link Inventory#getMaxCountPerStack()}),
* it gets resized to this inventory's maximum amount.
*/
@Override
default void setStack(int slot, ItemStack stack) {
getItems().set(slot, stack);
if (stack.getCount() > stack.getMaxCount()) {
stack.setCount(stack.getMaxCount());
}
}
/**
* Clears the inventory.
*/
@Override
default void clear() {
getItems().clear();
}
/**
* Marks the state as dirty.
* Must be called after changes in the inventory, so that the game can properly
* save
* the inventory contents and notify neighboring blocks of inventory changes.
*/
@Override
default void markDirty() {
// Override if you want behavior.
}
/**
* @return true if the player can use the inventory, false otherwise.
*/
@Override
default boolean canPlayerUse(PlayerEntity player) {
return true;
}
}

View File

@ -0,0 +1,68 @@
package jesse.keeblarcraft.world.biome;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.registry.Registerable;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeEffects;
import net.minecraft.world.biome.GenerationSettings;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.feature.DefaultBiomeFeatures;
import net.minecraft.world.gen.feature.VegetationPlacedFeatures;
public class ModBiomes {
public static final RegistryKey<Biome> TEST_BIOME = RegistryKey.of(RegistryKeys.BIOME,
new Identifier(Keeblarcraft.MOD_ID, "test_biome"));
public static void bootstrap(Registerable<Biome> context) {
context.register(TEST_BIOME, testBiome(context));
}
public static void globalOverworldGeneration(GenerationSettings.LookupBackedBuilder builder) {
DefaultBiomeFeatures.addLandCarvers(builder);
DefaultBiomeFeatures.addAmethystGeodes(builder);
// DefaultBiomeFeatures.addDungeons(builder);
DefaultBiomeFeatures.addMineables(builder);
DefaultBiomeFeatures.addSprings(builder);
DefaultBiomeFeatures.addFrozenTopLayer(builder);
}
public static Biome testBiome(Registerable<Biome> context) {
SpawnSettings.Builder spawnBuilder = new SpawnSettings.Builder();
DefaultBiomeFeatures.addFarmAnimals(spawnBuilder);
// DefaultBiomeFeatures.addBatsAndMonsters(spawnBuilder);
GenerationSettings.LookupBackedBuilder biomeBuilder =
new GenerationSettings.LookupBackedBuilder(context.getRegistryLookup(RegistryKeys.PLACED_FEATURE),
context.getRegistryLookup(RegistryKeys.CONFIGURED_CARVER));
globalOverworldGeneration(biomeBuilder);
DefaultBiomeFeatures.addMossyRocks(biomeBuilder);
biomeBuilder.feature(GenerationStep.Feature.VEGETAL_DECORATION, VegetationPlacedFeatures.TREES_PLAINS);
DefaultBiomeFeatures.addForestFlowers(biomeBuilder);
DefaultBiomeFeatures.addLargeFerns(biomeBuilder);
DefaultBiomeFeatures.addDefaultMushrooms(biomeBuilder);
DefaultBiomeFeatures.addDefaultVegetation(biomeBuilder);
return new Biome.Builder()
.precipitation(true)
.downfall(0.6f)
.temperature(0.7f)
.generationSettings(biomeBuilder.build())
.spawnSettings(spawnBuilder.build())
.effects((new BiomeEffects.Builder())
.waterColor(0xeb34a4)
.waterFogColor(0x1b78bf)
.skyColor(0xb41bbf)
.grassColor(0x1bbfb4)
.foliageColor(0xd203fc)
.fogColor(0x1b78bf).build())
.build();
}
}

View File

@ -0,0 +1,27 @@
package jesse.keeblarcraft.world.biome;
import com.mojang.datafixers.util.Pair;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import terrablender.api.Region;
import terrablender.api.RegionType;
import java.util.function.Consumer;
public class ModOverworldRegion extends Region {
public ModOverworldRegion(Identifier name, int weight) {
super(name, RegionType.OVERWORLD, weight);
}
@Override
public void addBiomes(Registry<Biome> registry, Consumer<Pair<MultiNoiseUtil.NoiseHypercube,
RegistryKey<Biome>>> mapper) {
// this.addModifiedVanillaOverworldBiomes(mapper, modifiedVanillaOverworldBuilder -> {
// modifiedVanillaOverworldBuilder.replaceBiome(BiomeKeys.FOREST, ModBiomes.TEST_BIOME);
// });
}
}

View File

@ -0,0 +1,17 @@
package jesse.keeblarcraft.world.biome;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.world.biome.surface.ModMaterialRules;
import net.minecraft.util.Identifier;
import terrablender.api.Regions;
import terrablender.api.SurfaceRuleManager;
import terrablender.api.TerraBlenderApi;
public class ModTerrablenderAPI implements TerraBlenderApi {
@Override
public void onTerraBlenderInitialized() {
Regions.register(new ModOverworldRegion(new Identifier(Keeblarcraft.MOD_ID, "overworld"), 4));
SurfaceRuleManager.addSurfaceRules(SurfaceRuleManager.RuleCategory.OVERWORLD, Keeblarcraft.MOD_ID, ModMaterialRules.makeRules());
}
}

View File

@ -0,0 +1,33 @@
package jesse.keeblarcraft.world.biome.surface;
import jesse.keeblarcraft.world.biome.ModBiomes;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.world.gen.surfacebuilder.MaterialRules;
// for landscaping and stuff
public class ModMaterialRules {
private static final MaterialRules.MaterialRule DIRT = makeStateRule(Blocks.DIRT);
private static final MaterialRules.MaterialRule GRASS_BLOCK = makeStateRule(Blocks.GRASS_BLOCK);
private static final MaterialRules.MaterialRule STONE_BLOCK = makeStateRule(Blocks.STONE);
private static final MaterialRules.MaterialRule GRANITE_BLOCK = makeStateRule(Blocks.GRANITE);
// private static final MaterialRules.MaterialRule RUBY = makeStateRule(ModBlocks.RUBY_BLOCK);
// private static final MaterialRules.MaterialRule RAW_RUBY = makeStateRule(ModBlocks.RAW_RUBY_BLOCK);
public static MaterialRules.MaterialRule makeRules() {
MaterialRules.MaterialCondition isAtOrAboveWaterLevel = MaterialRules.water(-1, 0);
MaterialRules.MaterialRule grassSurface = MaterialRules.sequence(MaterialRules.condition(isAtOrAboveWaterLevel, GRASS_BLOCK), DIRT);
return MaterialRules.sequence(MaterialRules.sequence(MaterialRules.condition(MaterialRules.biome(ModBiomes.TEST_BIOME),
MaterialRules.condition(MaterialRules.STONE_DEPTH_FLOOR, STONE_BLOCK)),
MaterialRules.condition(MaterialRules.STONE_DEPTH_CEILING, GRANITE_BLOCK)),
// Default to a grass and dirt surface
MaterialRules.condition(MaterialRules.STONE_DEPTH_FLOOR, grassSurface)
);
}
private static MaterialRules.MaterialRule makeStateRule(Block block) {
return MaterialRules.block(block.getDefaultState());
}
}

View File

@ -0,0 +1,45 @@
package jesse.keeblarcraft.world.dimension;
import java.util.OptionalLong;
import jesse.keeblarcraft.Keeblarcraft;
import net.minecraft.registry.Registerable;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.intprovider.UniformIntProvider;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionTypes;
public class ModDimensions {
public static final RegistryKey<DimensionOptions> KEEBLAR_KEY = RegistryKey.of(RegistryKeys.DIMENSION,
new Identifier(Keeblarcraft.MOD_ID, "keeblarcraftdim"));
public static final RegistryKey<World> KEEBLAR_LEVEL_KEY = RegistryKey.of(RegistryKeys.WORLD,
new Identifier(Keeblarcraft.MOD_ID, "keeblarcraftdim"));
public static final RegistryKey<DimensionType> KEEBLAR_DIM_TYPE = RegistryKey.of(RegistryKeys.DIMENSION_TYPE,
new Identifier(Keeblarcraft.MOD_ID, "keeblarcraftdim_type"));
public static void bootstrapType(Registerable<DimensionType> context) {
context.register(KEEBLAR_DIM_TYPE, new DimensionType(
OptionalLong.of(12750), // fixedTime
true, // hasSkylight
false, // hasCeiling
false, // ultraWarm
false, // natural
1.0, // coordinateScale
true, // bedWorks
false, // respawnAnchorWorks
0, // minY
480, // height DO NOT LOWER ANYMORE. IN PRODUCTION
256, // logicalHeight
BlockTags.INFINIBURN_OVERWORLD, // infiniburn
DimensionTypes.OVERWORLD_ID, // effectsLocation
0.5f, // ambientLight
new DimensionType.MonsterSettings(false, false, UniformIntProvider.create(0, 0), 0)));
}
}

View File

@ -0,0 +1,36 @@
{
"type": "keeblarcraft:keeblarcraftdim_type",
"generator": {
"type": "minecraft:noise",
"settings": "minecraft:overworld",
"biome_source": {
"type": "minecraft:multi_noise",
"biomes": [
{
"biome": "minecraft:plains",
"parameters": {
"temperature": 0.3,
"humidity": 0.1,
"continentalness": 0.2,
"erosion": 0.1,
"weirdness": 0.1,
"depth": 0,
"offset": 0
}
},
{
"biome": "keeblarcraft:test_biome",
"parameters": {
"temperature": 0,
"humidity": 0,
"continentalness": 0.1,
"erosion": 0,
"weirdness": 0,
"depth": 0,
"offset": 0
}
}
]
}
}
}

View File

@ -20,6 +20,12 @@
],
"client": [
"jesse.keeblarcraft.KeeblarcraftClient"
],
"fabric-datagen": [
"jesse.keeblarcraft.world.DataGeneration"
],
"terrablender": [
"jesse.keeblarcraft.world.biome.ModTerrablenderAPI"
]
},
"depends": {
@ -27,7 +33,7 @@
"minecraft": "~1.20",
"java": ">=17",
"fabric-api": "*",
"fabric-key-binding-api-v1": "*"
"terrablender": "*"
},
"suggests": {
"another-mod": "*"