Replaces more static strings from migrate, reload and save

This commit is contained in:
2025-08-17 15:41:50 +02:00
parent 57ca6ff2e9
commit 4501156087
12 changed files with 174 additions and 95 deletions

View File

@@ -1,6 +1,7 @@
package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import net.knarcraft.bookswithoutborders.config.Translatable;
import net.knarcraft.bookswithoutborders.container.MigrationRequest;
import net.knarcraft.bookswithoutborders.thread.MigrationQueueThread;
@@ -61,7 +62,8 @@ public class CommandMigrate implements TabExecutor {
private void findFilesToMigrate(@NotNull File folder, @NotNull Queue<MigrationRequest> queue, @NotNull Player player) {
File[] files = folder.listFiles();
if (files == null) {
BooksWithoutBorders.log(Level.WARNING, "Unable to access directory " + folder.getName() + " !");
BooksWithoutBorders.log(Level.WARNING, StringFormatter.replacePlaceholder(
StaticMessage.EXCEPTION_DIRECTORY_UNAVAILABLE.toString(), "{folder}", folder.getName()));
return;
}
for (File file : files) {

View File

@@ -1,6 +1,8 @@
package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.Translatable;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -17,16 +19,16 @@ public class CommandReload implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
if (BooksWithoutBorders.getConfiguration().loadConfig()) {
BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!");
} else {
BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!");
BooksWithoutBorders.sendErrorMessage(sender, "See console for details");
}
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
// Reload books
BooksWithoutBorders.updateBooks(sender, true);
BooksWithoutBorders.clearBookData();
BooksWithoutBorders.updateBooks(sender, true);
if (BooksWithoutBorders.getConfiguration().loadConfig()) {
stringFormatter.displaySuccessMessage(sender, Translatable.SUCCESS_RELOADED);
} else {
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_RELOAD_FAILED);
}
return true;
}

View File

@@ -2,13 +2,15 @@ package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.config.Permission;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import net.knarcraft.bookswithoutborders.config.Translatable;
import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.knarcraft.bookswithoutborders.state.ItemSlot;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
import net.md_5.bungee.api.ChatColor;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -16,6 +18,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@@ -30,7 +33,7 @@ public class CommandSave implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] arguments) {
return saveHeldBook(sender, arguments, false);
return saveHeldBook(sender, arguments, false, label);
}
/**
@@ -39,22 +42,24 @@ public class CommandSave implements TabExecutor {
* @param sender <p>The sender of the command</p>
* @param arguments <p>The arguments given</p>
* @param savePublic <p>Whether to save the book in the public directory or the player directory</p>
* @param command <p>The command executed to trigger this method</p>
* @return <p>True if a book was saved successfully</p>
*/
protected boolean saveHeldBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean savePublic) {
protected boolean saveHeldBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean savePublic,
@NotNull String command) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_PLAYER_ONLY);
return false;
}
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
if (holdingSlot != ItemSlot.NONE) {
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
ItemStack heldBook = InventoryHelper.getHeldBook(player);
if (heldBook != null) {
boolean duplicate = arguments.length == 1 && Boolean.parseBoolean(arguments[0]);
saveBook(player, holdingItem, duplicate, savePublic);
saveBook(player, heldBook, duplicate, savePublic, command);
return true;
} else {
BooksWithoutBorders.sendErrorMessage(sender, "You must be holding a written book or book and quill to save it!");
stringFormatter.displayErrorMessage(sender, Translatable.ERROR_NOT_HOLDING_ANY_BOOK);
return false;
}
}
@@ -66,11 +71,14 @@ public class CommandSave implements TabExecutor {
* @param heldBook <p>The book held</p>
* @param overwrite <p>Whether to overwrite any existing books</p>
* @param saveToPublicFolder <p>Whether to save the book to the public folder instead of the player folder</p>
* @param command <p>The command executed to trigger this method</p>
*/
public void saveBook(@NotNull Player player, @NotNull ItemStack heldBook, boolean overwrite, boolean saveToPublicFolder) {
public void saveBook(@NotNull Player player, @NotNull ItemStack heldBook, boolean overwrite,
boolean saveToPublicFolder, @NotNull String command) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
BookMeta book = (BookMeta) heldBook.getItemMeta();
if (book == null) {
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for your held book!");
stringFormatter.displayErrorMessage(player, Translatable.ERROR_METADATA_MISSING);
return;
}
@@ -78,16 +86,15 @@ public class CommandSave implements TabExecutor {
//Only allow saving of own books if enabled
if (config.getAuthorOnlySave() && !saveToPublicFolder &&
(!player.hasPermission("bookswithoutborders.bypassAuthorOnlySave") &&
(!player.hasPermission(Permission.BYPASS_AUTHOR_ONLY_SAVE.toString()) &&
BookHelper.isNotAuthor(player, book))) {
return;
}
String savePath = BookHelper.getBookDirectoryPathString(saveToPublicFolder ?
BookDirectory.PUBLIC : BookDirectory.PLAYER, player);
if (savePath == null) {
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! Unable to find the save path!");
stringFormatter.displayErrorMessage(player, Translatable.ERROR_SAVE_INVALID_PATH);
return;
}
@@ -103,55 +110,26 @@ public class CommandSave implements TabExecutor {
//Make sure the used folders exist
File file = new File(savePath);
if (!file.exists() && !file.mkdir()) {
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
" a server admin!");
return;
}
File[] foundFiles = file.listFiles();
if (foundFiles == null) {
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
" a server admin!");
stringFormatter.displayErrorMessage(player, Translatable.ERROR_SAVE_FILE_SYSTEM_ERROR);
return;
}
//Find any duplicates of the book
int foundDuplicates = BookFileHelper.findDuplicates(foundFiles, fileName);
//Deal with duplicates
if (foundDuplicates > 0) {
//TODO: Decide if this makes sense or needs to be changed
//Skip duplicate book
if (!fileName.contains("Untitled" + config.getTitleAuthorSeparator()) && !overwrite) {
BooksWithoutBorders.sendErrorMessage(player, "Book is already saved!");
BooksWithoutBorders.sendErrorMessage(player, "Use " + config.getCommandColor() + (saveToPublicFolder ?
"/savepublicbook" : "/savebook") + " true " + config.getErrorColor() + "to overwrite!");
return;
}
//Skip if duplicate limit is reached
if (foundDuplicates > config.getBookDuplicateLimit()) {
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName +
" duplicates reached!");
BooksWithoutBorders.sendErrorMessage(player, "Use " + config.getCommandColor() + (saveToPublicFolder ?
"/savepublicbook" : "/savebook") + " true " +
config.getErrorColor() + "to overwrite!");
return;
}
//Alter duplicated filename
if (fileName.contains("Untitled" + config.getTitleAuthorSeparator()) && !overwrite) {
fileName = "(" + foundDuplicates + ")" + fileName;
}
// Deal with possible duplicates of the file
String newName = checkDuplicates(player, file, fileName, command, config.getTitleAuthorSeparator(),
config.getBookDuplicateLimit(), overwrite);
if (newName == null) {
return;
}
try {
BookToFromTextHelper.bookToYml(savePath, fileName, book);
BookToFromTextHelper.bookToYml(savePath, newName, book);
//Update the relevant book list
BooksWithoutBorders.updateBooks(player, saveToPublicFolder);
BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + ChatColor.RESET + "\"");
stringFormatter.displaySuccessMessage(player,
stringFormatter.replacePlaceholder(Translatable.SUCCESS_SAVED, "{fileName}", newName));
} catch (IOException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Unable to save book");
BooksWithoutBorders.log(Level.SEVERE, StaticMessage.EXCEPTION_SAVE_BOOK_FAILED.toString());
}
}
@@ -163,4 +141,65 @@ public class CommandSave implements TabExecutor {
return new ArrayList<>();
}
/**
* Checks if the book to save is a duplicate of one or more saved books
*
* <p>Note that only unnamed books can have duplicates</p>
*
* @param player <p>The player attempting to save the book</p>
* @param directory <p>The directory to search for duplicates</p>
* @param fileName <p>The name the file is to be saved as</p>
* @param command <p>The command executed to trigger this method</p>
* @param separator <p>The title author separator</p>
* @param duplicateLimit <p>The duplicate limit</p>
* @param overwrite <p>Whether the player enabled overwriting</p>
* @return <p>The possible altered filename, or null if saving the book isn't allowed</p>
*/
@Nullable
private String checkDuplicates(@NotNull Player player, @NotNull File directory, @NotNull String fileName,
@NotNull String command, @NotNull String separator, int duplicateLimit,
boolean overwrite) {
StringFormatter stringFormatter = BooksWithoutBorders.getStringFormatter();
File[] foundFiles = directory.listFiles();
if (foundFiles == null) {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_SAVE_FILE_SYSTEM_ERROR);
return null;
}
// Find any duplicates of the book
int foundDuplicates = BookFileHelper.findDuplicates(foundFiles, fileName);
// No duplicates to process
if (foundDuplicates <= 0) {
return fileName;
}
String fullCommand = "/" + command + " true";
boolean isUnnamed = fileName.contains("Untitled" + separator);
// Skip duplicate unnamed book saving
if (!isUnnamed && !overwrite) {
stringFormatter.displayErrorMessage(player, Translatable.ERROR_SAVE_DUPLICATE_NAMED);
stringFormatter.displayErrorMessage(player, stringFormatter.replacePlaceholder(
Translatable.ERROR_SAVE_OVERWRITE_REQUIRED, "{command}", fullCommand));
return null;
}
// Skip if duplicate limit is reached
if (foundDuplicates > duplicateLimit) {
stringFormatter.displayErrorMessage(player, stringFormatter.replacePlaceholder(
Translatable.ERROR_SAVE_DUPLICATE_UNNAMED, "{fileName}", fileName));
stringFormatter.displayErrorMessage(player, stringFormatter.replacePlaceholder(
Translatable.ERROR_SAVE_OVERWRITE_REQUIRED, "{command}", fullCommand));
return null;
}
// Alter duplicated filename
if (isUnnamed && !overwrite) {
fileName = "(" + foundDuplicates + ")" + fileName;
}
return fileName;
}
}

View File

@@ -13,7 +13,7 @@ public class CommandSavePublic extends CommandSave implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
return saveHeldBook(sender, arguments, true);
return saveHeldBook(sender, arguments, true, label);
}
}

View File

@@ -23,7 +23,6 @@ public class BooksWithoutBordersConfig {
private final ChatColor errorColor = ChatColor.RED;
private final ChatColor successColor = ChatColor.GREEN;
private final ChatColor commandColor = ChatColor.YELLOW;
private final String SLASH = FileSystems.getDefault().getSeparator();
private boolean isInitialized;
private final String bookFolder;
@@ -99,15 +98,6 @@ public class BooksWithoutBordersConfig {
return this.successColor;
}
/**
* Gets the color used to color commands
*
* @return <p>The color used to color commands</p>
*/
public ChatColor getCommandColor() {
return this.commandColor;
}
/**
* Gets the correct slash to use for the used OS
*

View File

@@ -32,6 +32,11 @@ public enum Permission {
*/
FORMAT("format"),
/**
* The permission for bypassing author only saving
*/
BYPASS_AUTHOR_ONLY_SAVE("bypassAuthorOnlySave"),
;
private final @NotNull String node;
@@ -45,16 +50,6 @@ public enum Permission {
this.node = node;
}
/**
* Gets the node of this permission
*
* @return <p>The permission node</p>
*/
@NotNull
public String getNode() {
return "bookswithoutborders." + this.node;
}
/**
* Return node instead of enum when displayed as a string
*
@@ -63,7 +58,7 @@ public enum Permission {
@Override
@NotNull
public String toString() {
return getNode();
return "bookswithoutborders." + this.node;
}
}

View File

@@ -13,6 +13,10 @@ public enum StaticMessage {
EXCEPTION_VAULT_NOT_AVAILABLE("Vault is unavailable, but book price is set to economy. Unsetting book cost!"),
EXCEPTION_VAULT_PRICE_NOT_CHANGED("BooksWithoutBorders failed to hook into Vault! Book price not set!"),
EXCEPTION_ENCRYPTED_FILE_DELETE_FAILED("Book encryption data failed to delete upon decryption!\nFile location: {path}"),
EXCEPTION_DIRECTORY_UNAVAILABLE("Unable to access directory {folder} !"),
EXCEPTION_SAVE_BOOK_FAILED("Unable to save book"),
EXCEPTION_BOOKSHELF_SAVING_FAILED("Unable to save bookshelves!"),
NOTICE_NO_BOOKSHELVES("BooksWithoutBorders found no bookshelves to load"),
;
private final @NotNull String messageString;

View File

@@ -68,6 +68,16 @@ public enum Translatable implements TranslatableMessage {
*/
SUCCESS_MIGRATION_STARTED,
/**
* The success message displayed when the configuration is successfully reloaded
*/
SUCCESS_RELOADED,
/**
* The success message displayed when a book is successfully saved
*/
SUCCESS_SAVED,
/**
* The error to display when the console attempts to run a player-only command
*/
@@ -238,6 +248,36 @@ public enum Translatable implements TranslatableMessage {
*/
ERROR_LOAD_FAILED,
/**
* The error displayed when the configuration cannot be reloaded
*/
ERROR_RELOAD_FAILED,
/**
* The error displayed when unable to generate a valid save path for a book
*/
ERROR_SAVE_INVALID_PATH,
/**
* The error displayed when unable to create necessary folders for saving a book
*/
ERROR_SAVE_FILE_SYSTEM_ERROR,
/**
* The error displayed when enabling overwriting is necessary to save a book
*/
ERROR_SAVE_OVERWRITE_REQUIRED,
/**
* The error displayed when attempting to save a duplicate unnamed book
*/
ERROR_SAVE_DUPLICATE_NAMED,
/**
* The error displayed when attempting to save a duplicate named book exceeding the duplicate limit
*/
ERROR_SAVE_DUPLICATE_UNNAMED,
/**
* The header displayed before printing all commands
*/

View File

@@ -1,6 +1,7 @@
package net.knarcraft.bookswithoutborders.handler;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.StaticMessage;
import net.knarcraft.bookswithoutborders.container.Bookshelf;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -71,8 +72,7 @@ public class BookshelfHandler {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(bookshelfFile);
ConfigurationSection bookshelfSection = configuration.getConfigurationSection("bookshelves");
if (bookshelfSection == null) {
BooksWithoutBorders.log(Level.INFO,
"BooksWithoutBorders found no bookshelves to load");
BooksWithoutBorders.log(Level.INFO, StaticMessage.NOTICE_NO_BOOKSHELVES.toString());
return;
}
@@ -108,7 +108,7 @@ public class BookshelfHandler {
}
configuration.save(bookshelfFile);
} catch (IOException exception) {
BooksWithoutBorders.log(Level.SEVERE, "Unable to save bookshelves!");
BooksWithoutBorders.log(Level.SEVERE, StaticMessage.EXCEPTION_BOOKSHELF_SAVING_FAILED.toString());
}
}

View File

@@ -372,6 +372,7 @@ public final class EncryptionHelper {
* @param bytes <p>The bytes to convert</p>
* @return <p>The resulting hexadecimal string</p>
*/
@NotNull
public static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {

View File

@@ -25,13 +25,9 @@ public final class InventoryHelper {
* @return <p>The held book, or null if not holding one book in the main hand</p>
*/
public static ItemStack getHeldBook(@NotNull Player player) {
@NotNull ItemSlot heldSigned = InventoryHelper.getHeldSlotBook(player, true, true,
true, true);
@NotNull ItemSlot heldUnSigned = InventoryHelper.getHeldSlotBook(player, true, true,
true, false);
if (heldSigned == ItemSlot.MAIN_HAND || heldUnSigned == ItemSlot.MAIN_HAND) {
boolean holdingSigned = heldSigned == ItemSlot.MAIN_HAND;
return InventoryHelper.getHeldBook(player, holdingSigned);
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
if (holdingSlot != ItemSlot.NONE) {
return InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
} else {
return null;
}

View File

@@ -12,6 +12,8 @@ en:
SUCCESS_PAGE_DELETED: "Page deleted!"
SUCCESS_BOOK_LOADED: "Book created!"
SUCCESS_MIGRATION_STARTED: "Starting book migration..."
SUCCESS_RELOADED: "BooksWithoutBorders configuration reloaded!"
SUCCESS_SAVED: "Book Saved as &e\"{fileName}\"&r"
ACTION_COPY: "copy"
ACTION_CLEAR: "clear"
ACTION_DECRYPT: "decrypt"
@@ -54,6 +56,14 @@ en:
ERROR_GROUP_ENCRYPT_ARGUMENTS_MISSING: "You must specify a group name and key to encrypt a book!"
ERROR_GROUP_ENCRYPTED_ALREADY: "Book is already group encrypted!"
ERROR_LOAD_FAILED: "Book failed to load!"
ERROR_RELOAD_FAILED: |
Reload Failed!
See console for details
ERROR_SAVE_INVALID_PATH: "Saving Failed! Unable to find the save path!"
ERROR_SAVE_FILE_SYSTEM_ERROR: "Saving Failed! If this continues to happen, consult a server admin!"
ERROR_SAVE_OVERWRITE_REQUIRED: "Use &e{command}&r to overwrite!"
ERROR_SAVE_DUPLICATE_NAMED: "Book is already saved!"
ERROR_SAVE_DUPLICATE_UNNAMED: "Maximum amount of {fileName} duplicates reached!"
NEUTRAL_COMMANDS_HEADER: |
&e[] denote optional parameters
<> denote required parameters