diff --git a/src/main/java/jesse/keeblarcraft/Commands/CustomCommandManager.java b/src/main/java/jesse/keeblarcraft/Commands/CustomCommandManager.java index fdde4c1..a11bf9b 100644 --- a/src/main/java/jesse/keeblarcraft/Commands/CustomCommandManager.java +++ b/src/main/java/jesse/keeblarcraft/Commands/CustomCommandManager.java @@ -12,7 +12,6 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.text.Text; -import jesse.keeblarcraft.Commands.ShortcutCommands; public class CustomCommandManager { // Intentionally empty constructor since at object definition time it may not be possible to register commands @@ -36,10 +35,12 @@ public class CustomCommandManager { // CUSTOM COMMAND CLASS OBJECTS BELOW ShortcutCommands shortcuts = new ShortcutCommands(); + NoteCommands noteCommands = new NoteCommands(); // REGISTER COMMANDS BELOW System.out.println("REGISTERING CUSTOM COMMAND EXTENSIONS BELOW"); shortcuts.RegisterShortcutCommands(); + noteCommands.RegisterNoteCommands(); } diff --git a/src/main/java/jesse/keeblarcraft/Commands/FactionCommands.java b/src/main/java/jesse/keeblarcraft/Commands/FactionCommands.java new file mode 100644 index 0000000..1736cc6 --- /dev/null +++ b/src/main/java/jesse/keeblarcraft/Commands/FactionCommands.java @@ -0,0 +1,132 @@ +package jesse.keeblarcraft.Commands; + +public class FactionCommands { + + // Register function for commands + public void RegisterFactionCommands() { + + } + + /// PRIVATE HANDLERS BELOW + private int CreateFaction() { + int retValue = -1; + + return retValue; + } + + private int DeleteFaction() { + int retValue = -1; + + return retValue; + } + + private int AddPlayerToFaction() { + int retValue = -1; + + return retValue; + } + + private int KickPlayerFromFaction() { + int retValue = -1; + + return retValue; + } + + private int PromotePlayerInFaction() { + int retValue = -1; + + return retValue; + } + + private int DemotePlayerInFaction() { + int retValue = -1; + + return retValue; + } + + private int SetPlayerRoleInFaction() { + int retValue = -1; + + return retValue; + } + + private int DeclareFactionBase() { + int retValue = -1; + + return retValue; + } + + // admin only + private int SetFactionPower() { + int retValue = -1; + + return retValue; + } + + private int DeclareFactionEvent() { + int retValue = -1; + + return retValue; + } + + private int DeclareEnemyFaction() { + int retValue = -1; + + return retValue; + } + + private int DeclareAlliedFaction() { + int retValue = -1; + + return retValue; + } + + private int DeclareNeutralFaction() { + int retValue = -1; + + return retValue; + } + + private int ListAllFactions() { + int retValue = -1; + + return retValue; + } + + private int RegisterFactionChatChannel() { + int retValue = -1; + + return retValue; + } + + // admin only + private int DeleteFactionChatChannel() { + int retValue = -1; + + return retValue; + } + + private int DepositToFactionBank() { + int retValue = -1; + + return retValue; + } + + private int WithdrawFromFactionBank() { + int retValue = -1; + + return retValue; + } + + private int FactionBankBalance() { + int retValue = -1; + + return retValue; + } + + private int ListFactionBankTransactions() { + int retValue = -1; + + return retValue; + } +} diff --git a/src/main/java/jesse/keeblarcraft/Commands/NoteCommands.java b/src/main/java/jesse/keeblarcraft/Commands/NoteCommands.java new file mode 100644 index 0000000..e478b8a --- /dev/null +++ b/src/main/java/jesse/keeblarcraft/Commands/NoteCommands.java @@ -0,0 +1,135 @@ +package jesse.keeblarcraft.Commands; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.datafixers.Products.P1; + +import jesse.keeblarcraft.ConfigMgr.ConfigManager; +import jesse.keeblarcraft.Utils.ChatUtil; +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; + +public class NoteCommands { + + // IMPORTANT NOTE: + // + // Each player will retain their own private file of notes inside a "notes" directory inside the overall mod config directory + ConfigManager notesConfig = new ConfigManager(); + String NOTES_GLOBAL_DIRECTORY = ""; // The overall "notes" dir inside cfg folder + + public NoteCommands() { + // Check if directory exists + if (notesConfig.DoesDirectoryExist(NOTES_GLOBAL_DIRECTORY) == false) { + // Attempt to create the directory + if (notesConfig.CreateDirectory(NOTES_GLOBAL_DIRECTORY) == true) { + System.out.println("Created notes directory successfully!"); //TODO: Success! + } else { + System.out.println("ERROR: Notes directory FAILED to create!! Permissions missing to create directory!"); //TODO: Critical failure + } + } else { + System.out.println("Notes directory already exists. Skipping creation..."); //TODO: Success! + } + } + + 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") + // .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") + .then(CommandManager.argument("value", StringArgumentType.greedyString()) + .executes(context -> PurgeAllNotes(StringArgumentType.getString(context, "value"), context)))); + }); + + // Command: "/modifynote noteIdHere" + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + dispatcher.register(CommandManager.literal("editnote") + .then(CommandManager.argument("value", IntegerArgumentType.integer()) + .executes(context -> ModifyNote(IntegerArgumentType.getInteger(context, "value"), 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)))); + + // Alias redirects + dispatcher.register(CommandManager.literal("rmnote").redirect(rootDeleteCmd)); + dispatcher.register(CommandManager.literal("deletenote").redirect(rootDeleteCmd)); + }); + } + + /// COMMAND HANDLERS BELOW + + // AddNote + // + // Adds a new note based on the string value provided by the player. The note is labeled in the background based on the portion + // of the story they are currently in as well to help provide filtering methods later on + private int AddNote(String value, CommandContext context) { + int ret = -1; + + if (context.getSource().isExecutedByPlayer()) { + ServerPlayerEntity player = context.getSource().getPlayer(); + notesConfig.AddToKey(player.getUuidAsString(), value); + ChatUtil.SendPlayerMsg(player, "New note logged to entry! View notes any time with /notegui"); + } else { + System.out.println("Only a player can execute this command!"); + } + + return ret; + } + + private int DeleteNote(int value, CommandContext context) { + int ret = -1; + + return ret; + } + + private int ModifyNote(int value, CommandContext context) { + int ret = -1; + + return ret; + } + + private int PurgeAllNotes(String value, CommandContext context) { + int ret = -1; + + return ret; + } + + private int ListNotes(String value, CommandContext context) { + int ret = -1; + + return ret; + } + + private int OpenNoteGui(String value, CommandContext context) { + int ret = -1; + + return ret; + } + + private int FilterForNote(String value, CommandContext context) { + int ret = -1; + + return ret; + } +} diff --git a/src/main/java/jesse/keeblarcraft/Commands/ShortcutCommands.java b/src/main/java/jesse/keeblarcraft/Commands/ShortcutCommands.java index b55a42f..7fd4b74 100644 --- a/src/main/java/jesse/keeblarcraft/Commands/ShortcutCommands.java +++ b/src/main/java/jesse/keeblarcraft/Commands/ShortcutCommands.java @@ -1,8 +1,9 @@ /* * * ShortcutCommands - * - * A class that simplifies some of the current vanilla commands with shortcut commands in the game + * + * A class that simplifies some of the current vanilla commands with shortcut commands in the game. You may remember an old plugin + * called "Essentials" (Or EssentialsEx in a later version) -> This file is re-creating a portion of that plugin but in mod-format */ package jesse.keeblarcraft.Commands; @@ -10,31 +11,62 @@ package jesse.keeblarcraft.Commands; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; +import jesse.keeblarcraft.Utils.ChatUtil; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.minecraft.entity.player.PlayerAbilities; 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 ShortcutCommands { + + float DEFAULT_FLIGHT_SPEED = 0.05f; // Minecraft operates on a 0.0 -> 1.0 scale; with each .01 making a difference in speed. 0.05 is creative flight speed + float SPEED_SCALAR = 20.0f; // For clamping speed down to half of its max output (So 0.5 will be max speed on a system that goes up in 0.05'ths) + public void RegisterShortcutCommands() { // Abstract handling "value" to parse gamemodes 0-2 (survival; creative; spectator) CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { dispatcher.register(CommandManager.literal("gm") .then(CommandManager.argument("value", IntegerArgumentType.integer()) - .executes(context -> CreativeShortcut(IntegerArgumentType.getInteger(context, "value"), context)))); + .executes(context -> GamemodeShortcut(IntegerArgumentType.getInteger(context, "value"), context)))); + }); + + // Fly command ///TODO: Is this just being condensed into the FlightSpeedShortcut fn? + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + dispatcher.register(CommandManager.literal("fly") + .executes(context -> { FlightShortcut(context); + return 0; + })); + }); + + // Fly command with speed + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + dispatcher.register(CommandManager.literal("fly") + .then(CommandManager.argument("value", IntegerArgumentType.integer()) + .executes(context -> FlightSpeedShortcut(IntegerArgumentType.getInteger(context, "value"), context)))); + }); + + ///TODO: Read TODO on function + // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + // dispatcher.register(CommandManager.literal("walk") + // .then(CommandManager.argument("value", IntegerArgumentType.integer()) + // .executes(context -> WalkSpeedShortcut(IntegerArgumentType.getInteger(context, "value"), context)))); + // }); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + dispatcher.register(CommandManager.literal("heal") + .executes(context -> { HealShortcut(context); + return 0; + })); }); } - /// Command handlers below - - private int CreativeShortcut(int value, CommandContext context) + private int GamemodeShortcut(int value, CommandContext context) { int retValue = -1; - context.getSource().sendMessage(Text.literal("You sent value: " + value)); - if (context.getSource().isExecutedByPlayer()) { ServerPlayerEntity player = context.getSource().getPlayer(); @@ -42,7 +74,6 @@ public class ShortcutCommands { if(player.hasPermissionLevel(4)) { switch(value) { case 0: - /// TODO: Add type checking & permission player.changeGameMode(net.minecraft.world.GameMode.SURVIVAL); player.sendMessage(Text.literal("Changing your game mode to SURVIVAL")); retValue = 0; @@ -74,5 +105,106 @@ public class ShortcutCommands { return retValue; } - + + private int FlightShortcut(CommandContext context) { + int retValue = -1; + if (context.getSource().isExecutedByPlayer()) { + ServerPlayerEntity player = context.getSource().getPlayer(); + + if (player.hasPermissionLevel(4)) { + PlayerAbilities abilities = player.getAbilities(); + abilities.flying = !abilities.flying; // Toggle flying + abilities.setFlySpeed(DEFAULT_FLIGHT_SPEED); // It seems flight speed is on a 0-1 scale + player.sendAbilitiesUpdate(); + ChatUtil.SendPlayerMsg(player, "You can now fly! Flight speed is " + abilities.getFlySpeed()); + } else { + player.sendMessage(Text.literal("You do not have permission for this command")); + } + } + + return retValue; + } + + private int FlightSpeedShortcut(int value, CommandContext context) { + int retValue = -1; + + if (context.getSource().isExecutedByPlayer()) { + ServerPlayerEntity player = context.getSource().getPlayer(); + + if (player.hasPermissionLevel(4)) { + PlayerAbilities abilities = player.getAbilities(); + + if (value >= 1 && value <= 10) { + abilities.allowFlying = true; + abilities.setFlySpeed((float) (value / SPEED_SCALAR)); // Dividing by 20f yields max clamp value of 0.5 since MC does 0.0-> 1.0 flight. 0.1 flight is too fast! + player.sendAbilitiesUpdate(); + ChatUtil.SendPlayerMsg(player, "Flight speed set to " + (value)); + } else { + player.sendMessage(Text.literal("Only values from 1-10 are accepted")); + } + } else { + player.sendMessage(Text.literal("You do not have permission for this command")); + } + } + + return retValue; + } + + ///TODO: There is a bug with walk speed that causes the players speed to behave weirdly despite the value. It's either a crawl or mach 10 + ///TODO: It's possible that the player may need to die first to reset speed; and reloading in the debugger does not count as resetting values. This + ///TODO: code may actually work; but I just did not die first in between testing speed changes. + // private int WalkSpeedShortcut(int value, CommandContext context) { + // int retValue = -1; + // if (context.getSource().isExecutedByPlayer()) { + // ServerPlayerEntity player = context.getSource().getPlayer(); + + // if (player.hasPermissionLevel(4)) { + // PlayerAbilities abilities = player.getAbilities(); + + // if (value >= 1 && value <= 10) { + // abilities.setWalkSpeed((float) 1.0f); + // player.sendAbilitiesUpdate(); + // ChatUtil.SendPlayerMsg(player, "Set walk speed to " + (value)); + // } else { + // player.sendMessage(Text.literal("Only values from 1-10 are accepted")); + // } + // } else { + // player.sendMessage(Text.literal("You do not have permission for this command")); + // } + // } + + // return retValue; + // } + + // TODO: Add when we can find where in the API to fill players hunger level + // private int FeedShortcut(CommandContext context) { + // int retValue = -1; + // if (context.getSource().isExecutedByPlayer()) { + // ServerPlayerEntity player = context.getSource().getPlayer(); + + // if (player.hasPermissionLevel(4)) { + + // } else { + // player.sendMessage(Text.literal("You do not have permission for this command")); + // } + // } + + // return retValue; + // } + + private int HealShortcut(CommandContext context) { + int retValue = -1; + if (context.getSource().isExecutedByPlayer()) { + ServerPlayerEntity player = context.getSource().getPlayer(); + + if (player.hasPermissionLevel(4)) { + player.setHealth(player.getMaxHealth()); + ChatUtil.SendPlayerMsg(player, "Healed!"); + } else { + player.sendMessage(Text.literal("You do not have permission for this command")); + } + } + + return retValue; + } } diff --git a/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java b/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java index d4d5856..b000dc3 100644 --- a/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java +++ b/src/main/java/jesse/keeblarcraft/ConfigMgr/ConfigManager.java @@ -6,10 +6,175 @@ * It is typical to define this class as a general object (not static instance) for each class in single-threaded actions. If you need * a special function or action from this class; it is recommended to add it to this class and not make your own. * + * */ package jesse.keeblarcraft.ConfigMgr; +import java.io.FileWriter; +import java.io.File; +import java.io.IOException; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonIOException; +import java.util.List; +import java.util.ArrayList; + +// Import all custom exceptions +import jesse.keeblarcraft.Utils.CustomExceptions.*; + public class ConfigManager { + // Pedantic empty constructor + public ConfigManager() {} + + // CreateFile + // + // Returns true if file is created, will return false if file cannot be created or already exists + public Boolean CreateFile(String fileName) throws FILE_CREATE_EXCEPTION { + Boolean ret = false; + File file = new File(fileName); + + // Check 1: Does the file already exist? + ret = !file.exists(); + + // Check 2: If the file does not exist, attempt to create it + if (ret == false) { + try { + ret = file.createNewFile(); + } catch (IOException e) { + // The file could not be created + throw new FILE_CREATE_EXCEPTION(); + } + } + return ret; + } + + // DeleteFile + // + // Returns true if file is deleted, false if could not be deleted or does not exist + public Boolean DeleteFile(String fileName) throws FILE_DELETE_EXCEPTION { + Boolean ret = false; + File file = new File(fileName); + + // Step 1: Does file exist? + ret = file.exists(); + + // Step 2: If file exists, attempt to delete + if (ret == true) { + try { + ret = file.delete(); + } catch (SecurityException e) { + throw new FILE_DELETE_EXCEPTION(); + } + } + return ret; + } + + // WriteToFile + // + // Will write or append to file (valid modes: "w" or "a") if file is available. Returns false if not + public Boolean WriteToFile(String fileName, String data, String mode) { + Boolean ret = false; + + return ret; + } + + + // WriteToJsonFile + // + // Will write to or append to a json file. It will search if the key exists first & update the field; + // or add a new entry. It should be noted that json objects *can* be buried inside each other. It is + // considered best (and only) practice to call the "GetJsonStringFromFile" function first from this + // class and simply iterate to what you would need and then update the entire entry alongside the + // top-level key. + // + // NOTE: THIS DOES NOT SAFE UPDATE THE KEY OBJECT. PRE-EXISTING DATA WILL BE DELETED FOREVER + public void WriteToJsonFile(String fileName, String key, Object data, String mode) throws FILE_WRITE_EXCEPTION { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + try { + gson.toJson(data, new FileWriter(fileName)); + } catch (JsonIOException | IOException e) { + throw new FILE_WRITE_EXCEPTION(); + } + } + + // GetJsonStringFromFile + // + // Retrieves a json formatted string from the file based on key. Returns empty string if not found + public String GetJsonStringFromFile(String fileName, String key) { + String ret = ""; + + return ret; + } + + public Boolean DoesFileExist(String fileName) { + Boolean ret = false; + + return ret; + } + + public Boolean DoesDirectoryExist(String dirName) { + Boolean ret = false; + + return ret; + } + + public Boolean CreateDirectory(String dirName) throws DIRECTORY_CREATE_EXCEPTION { + Boolean ret = false; + + File dir = new File(dirName); + + try { + if (dir.exists()) { + ret = dir.mkdirs(); + } + } catch (Exception e) { + throw new DIRECTORY_CREATE_EXCEPTION(); + } + return ret; + } + + public Boolean DeleteDirectory(String dirName) throws DIRECTORY_DELETE_EXCEPTION { + Boolean ret = false; + + File dir = new File(dirName); + + try { + ret = dir.delete(); + } catch (Exception e) { + throw new DIRECTORY_DELETE_EXCEPTION(); + } + + return ret; + } + + // AddToKey + // + // Adds new text to a key if found inside the config + public String AddToKey(String key, String newInfo) { + String ret = ""; + + return ret; + } + + // GetFile + // + // Returns a file as an arraylist of all the lines in the file. Generally only used for testing + // + // NOTE: Returns UTF-8 Encoding of file + public List GetFile(String fileName) { + List ret = new ArrayList(); + + try { + return Files.readLines(new File(fileName), Charsets.UTF_8); + } catch (IOException e) { + ret.clear(); + } + + return ret; + } } diff --git a/src/main/java/jesse/keeblarcraft/Keeblarcraft.java b/src/main/java/jesse/keeblarcraft/Keeblarcraft.java index 7e1671d..e903d00 100644 --- a/src/main/java/jesse/keeblarcraft/Keeblarcraft.java +++ b/src/main/java/jesse/keeblarcraft/Keeblarcraft.java @@ -16,6 +16,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jesse.keeblarcraft.Commands.CustomCommandManager; +import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_DUAL_DEFINITION; +import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION; +import jesse.keeblarcraft.Utils.Setup; // import com.mojang.brigadier.Command; @@ -27,6 +30,8 @@ public class Keeblarcraft implements ModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("keeblarcraft"); CustomCommandManager cmdMgr = new CustomCommandManager(); + Setup setup = Setup.GetInstance(); + @Override public void onInitialize() { // This code runs as soon as Minecraft is in a mod-load-ready state. @@ -36,10 +41,16 @@ public class Keeblarcraft implements ModInitializer { LOGGER.info("Hello Fabric world!"); cmdMgr.RegisterCustomCommands(); - // I can't tell if this is required or not; and if it is I imagine it comes before I register the custom commands - // Command command = context -> { - // ServerCommandSource source = context.getSource(); - // return 0; - // }; + if (setup != null) { + try { + setup.RunSetup(); + } catch (SETUP_FAILED_EXCEPTION e) { + System.out.println("ERROR. Setup failed to initialize environment. Mod likely does not have read/write permissions inside area. Mod will now close out."); + e.printStackTrace(); + } + } else { + // Program exit. Dual definition of setup somehow happened! + System.out.println("Dual definition of singleton attempted! Out of order initialization?"); + } } } \ No newline at end of file diff --git a/src/main/java/jesse/keeblarcraft/Utils/ChatUtil.java b/src/main/java/jesse/keeblarcraft/Utils/ChatUtil.java index 1d05215..a9d7a11 100644 --- a/src/main/java/jesse/keeblarcraft/Utils/ChatUtil.java +++ b/src/main/java/jesse/keeblarcraft/Utils/ChatUtil.java @@ -8,4 +8,13 @@ package jesse.keeblarcraft.Utils; -public class ChatUtil { } +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +public class ChatUtil { + + // Helpful print wrapper function + static public void SendPlayerMsg(ServerPlayerEntity player, String text) { + player.sendMessage(Text.literal(text)); + } +} diff --git a/src/main/java/jesse/keeblarcraft/Utils/CustomExceptions.java b/src/main/java/jesse/keeblarcraft/Utils/CustomExceptions.java new file mode 100644 index 0000000..9e2e1aa --- /dev/null +++ b/src/main/java/jesse/keeblarcraft/Utils/CustomExceptions.java @@ -0,0 +1,18 @@ +package jesse.keeblarcraft.Utils; + +public class CustomExceptions { + // File exceptions + public static class FILE_CREATE_EXCEPTION extends Exception {} + public static class FILE_WRITE_EXCEPTION extends Exception {} + public static class FILE_DELETE_EXCEPTION extends Exception {} + + // Directory exceptions + public static class DIRECTORY_DELETE_EXCEPTION extends Exception {} + public static class DIRECTORY_CREATE_EXCEPTION extends Exception {} + public static class DIRECTORY_MOVE_EXCEPTION extends Exception {} + + // Setup exceptions + public static class SETUP_NO_WRITE_EXCEPTION extends Exception {} + public static class SETUP_NO_READ_EXCEPTION extends Exception {} + public static class SETUP_FAILED_EXCEPTION extends Exception {} +} diff --git a/src/main/java/jesse/keeblarcraft/Utils/Setup.java b/src/main/java/jesse/keeblarcraft/Utils/Setup.java new file mode 100644 index 0000000..1ea45c8 --- /dev/null +++ b/src/main/java/jesse/keeblarcraft/Utils/Setup.java @@ -0,0 +1,146 @@ +/* + * + * Setup.java + * + * Setup is a singleton object that runs on the initial mod setup to run critical checks to make sure the mod + * is allowed to do things on the users filesystem (reading conf files, writing conf files, etc). It's quite + * important for the mod to know this! + * +*/ + +package jesse.keeblarcraft.Utils; + +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.spongepowered.include.com.google.common.io.Files; +import java.util.List; +import java.util.ArrayList; + +import jesse.keeblarcraft.ConfigMgr.ConfigManager; +import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_CREATE_EXCEPTION; +import jesse.keeblarcraft.Utils.CustomExceptions.DIRECTORY_DELETE_EXCEPTION; +import jesse.keeblarcraft.Utils.CustomExceptions.SETUP_FAILED_EXCEPTION; + +// Singleton class is designed to help the mod set itself up and create all the important things. It does two things: +// +// Thing 1: If on initial setup of mod for first time; creates all necessary directories and files. +// Doing this helps the mod know if it has permissions to do things, or if it shouldn't even bother! +// Some functionality is disabled if the mod can't access the hard drive in various ways. +// +// Thing 2: On any sequential startup; it checks to make sure all the directories exist & replaces missing ones. +// It will also do a state-check of the system to make sure we will have permissions to read & write. +// 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; + + public Setup() { + System.out.println("Running system setup and checks..."); + } + + // Returns the singleton object + public static Setup GetInstance() { + if (static_inst == null) { + static_inst = new Setup(); + } + + return static_inst; + } + + /// Unit testing functions below + public static Boolean has_read = false; + public static Boolean has_write = false; + + // First time setup variables + private static final List DIRECTORY_LIST = new ArrayList() {{ + 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! + }}; + + // These will be top-level config files above the directories this mod creates + private static final List FILE_LIST = new ArrayList() {{ + add("story.json"); // Big config file, determines when players can do certain things at different story levels + add("factions.json"); // General configuration file for factions stuff + add("events.json"); // General configuration file for story events! + add("general.json"); // The super general configuration file! (May be removed) + }}; + + + // RunChecks() + // + // Checks if we are able to create necessary directories and run reading over the file system for the current + // directory this mod is maintained in. This will return false if any checks fail; but individual checks can + // be accessed in class variables (above) + public Boolean RunChecks() { + ConfigManager conf = new ConfigManager(); + + // Create directory check + try { + has_write = conf.CreateDirectory("test_dir"); + } catch (DIRECTORY_CREATE_EXCEPTION e) { + has_write = false; // No read access + } + + // Write to disk then read that data back + if (has_write) { + try { + has_write = conf.CreateFile("test_dir/note.txt"); + has_write = conf.WriteToFile("test_dir/note.txt", "test_write_read", "w"); + + List lines = conf.GetFile("test_dir/note.txt"); + if (lines.size() == 0) { + has_read = false; + } + } catch (Exception e) { + has_read = false; + } + } + + // Delete directory if created (it's a temporary dir) + if (has_write) { + try { + has_write = conf.DeleteDirectory("test_dir"); + } catch (DIRECTORY_DELETE_EXCEPTION e) { + has_write = false; + } + } + + return has_write && has_read; + } + + // RunSetup + // + // Primary function call that will execute when mod starts up + public Boolean RunSetup() throws SETUP_FAILED_EXCEPTION { + Boolean ret = false; + + // Setup can only complete if it has guarenteed we have read & write permissions + if (ret = RunChecks()) { + try { + // Create necessary directories + ConfigManager conf = new ConfigManager(); + for (Integer i = 0; i < DIRECTORY_LIST.size(); i++) { + if ( ! conf.DoesDirectoryExist(DIRECTORY_LIST.get(i))) { + conf.CreateDirectory(DIRECTORY_LIST.get(i)); + } + } + + // Create necessary files + for (Integer i = 0; i < FILE_LIST.size(); i++) { + if ( ! conf.DoesFileExist(DIRECTORY_LIST.get(i))) { + conf.CreateFile(FILE_LIST.get(i)); + } + } + } catch (Exception e) { + throw new SETUP_FAILED_EXCEPTION(); + } + } else { + throw new SETUP_FAILED_EXCEPTION(); + } + + return ret; + } +}