Adds ability to easier find books by first letter
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
This commit is contained in:
@@ -39,12 +39,16 @@ import org.bukkit.inventory.ItemFactory;
|
|||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@@ -61,6 +65,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
private static ItemFactory itemFactory;
|
private static ItemFactory itemFactory;
|
||||||
private static Map<UUID, List<String>> playerBooksList;
|
private static Map<UUID, List<String>> playerBooksList;
|
||||||
private static List<String> publicBooksList;
|
private static List<String> publicBooksList;
|
||||||
|
private static Map<Character, Integer> publicLetterIndex;
|
||||||
|
private static Map<UUID, Map<Character, Integer>> playerLetterIndex;
|
||||||
private static BooksWithoutBorders booksWithoutBorders;
|
private static BooksWithoutBorders booksWithoutBorders;
|
||||||
private static ConsoleCommandSender consoleSender;
|
private static ConsoleCommandSender consoleSender;
|
||||||
|
|
||||||
@@ -88,6 +94,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
if (!playerBooksList.containsKey(playerUUID)) {
|
if (!playerBooksList.containsKey(playerUUID)) {
|
||||||
List<String> newFiles = BookFileHelper.listFiles(sender, false);
|
List<String> newFiles = BookFileHelper.listFiles(sender, false);
|
||||||
playerBooksList.put(playerUUID, newFiles);
|
playerBooksList.put(playerUUID, newFiles);
|
||||||
|
playerLetterIndex.put(playerUUID, BookFileHelper.populateLetterIndices(newFiles));
|
||||||
}
|
}
|
||||||
return playerBooksList.get(playerUUID);
|
return playerBooksList.get(playerUUID);
|
||||||
} else {
|
} else {
|
||||||
@@ -95,6 +102,22 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the letter index map for public books, or a specific player's books
|
||||||
|
*
|
||||||
|
* @param playerIndex <p>The player to get the index for, or null for the public index</p>
|
||||||
|
* @return <p>An index mapping between a character and the first index containing that character</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Map<Character, Integer> getLetterIndex(@Nullable UUID playerIndex) {
|
||||||
|
if (playerIndex == null) {
|
||||||
|
return publicLetterIndex;
|
||||||
|
} else {
|
||||||
|
Map<Character, Integer> letterIndex = playerLetterIndex.get(playerIndex);
|
||||||
|
return Objects.requireNonNullElseGet(letterIndex, HashMap::new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates available books
|
* Updates available books
|
||||||
*
|
*
|
||||||
@@ -103,10 +126,13 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
*/
|
*/
|
||||||
public static void updateBooks(CommandSender sender, boolean updatePublic) {
|
public static void updateBooks(CommandSender sender, boolean updatePublic) {
|
||||||
List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
|
List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
|
||||||
|
newFiles.sort(Comparator.naturalOrder());
|
||||||
if (updatePublic) {
|
if (updatePublic) {
|
||||||
publicBooksList = newFiles;
|
publicBooksList = newFiles;
|
||||||
|
publicLetterIndex = BookFileHelper.populateLetterIndices(newFiles);
|
||||||
} else if (sender instanceof Player player) {
|
} else if (sender instanceof Player player) {
|
||||||
playerBooksList.put(player.getUniqueId(), newFiles);
|
playerBooksList.put(player.getUniqueId(), newFiles);
|
||||||
|
playerLetterIndex.put(player.getUniqueId(), BookFileHelper.populateLetterIndices(newFiles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +149,10 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
booksWithoutBorders = this;
|
booksWithoutBorders = this;
|
||||||
consoleSender = this.getServer().getConsoleSender();
|
consoleSender = this.getServer().getConsoleSender();
|
||||||
playerBooksList = new HashMap<>();
|
playerBooksList = new HashMap<>();
|
||||||
|
playerLetterIndex = new HashMap<>();
|
||||||
BooksWithoutBordersConfig.initialize(this);
|
BooksWithoutBordersConfig.initialize(this);
|
||||||
publicBooksList = BookFileHelper.listFiles(consoleSender, true);
|
publicBooksList = BookFileHelper.listFiles(consoleSender, true);
|
||||||
|
publicLetterIndex = BookFileHelper.populateLetterIndices(publicBooksList);
|
||||||
|
|
||||||
PluginManager pluginManager = this.getServer().getPluginManager();
|
PluginManager pluginManager = this.getServer().getPluginManager();
|
||||||
|
|
||||||
|
@@ -10,11 +10,15 @@ import net.md_5.bungee.api.chat.HoverEvent;
|
|||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@@ -119,11 +123,21 @@ public final class BookFileHelper {
|
|||||||
*/
|
*/
|
||||||
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page) {
|
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page) {
|
||||||
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
||||||
|
|
||||||
|
Map<Character, Integer> firstInstances;
|
||||||
|
if (listPublic) {
|
||||||
|
firstInstances = BooksWithoutBorders.getLetterIndex(null);
|
||||||
|
} else if (sender instanceof Player player) {
|
||||||
|
firstInstances = BooksWithoutBorders.getLetterIndex(player.getUniqueId());
|
||||||
|
} else {
|
||||||
|
firstInstances = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
|
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
|
||||||
if (page > totalPages) {
|
if (page > totalPages) {
|
||||||
sender.sendMessage(ChatColor.GRAY + "No such page");
|
sender.sendMessage(ChatColor.GRAY + "No such page");
|
||||||
} else {
|
} else {
|
||||||
showBookMenu(sender, command, page, totalPages, availableBooks);
|
showBookMenu(sender, command, page, totalPages, availableBooks, firstInstances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,11 +149,28 @@ public final class BookFileHelper {
|
|||||||
* @param page <p>The currently selected page</p>
|
* @param page <p>The currently selected page</p>
|
||||||
* @param totalPages <p>The total amount of pages</p>
|
* @param totalPages <p>The total amount of pages</p>
|
||||||
* @param availableBooks <p>All books available to the sender</p>
|
* @param availableBooks <p>All books available to the sender</p>
|
||||||
|
* @param firstInstances <p>The map between a character, and the index of the first instance of that character in the book list</p>
|
||||||
*/
|
*/
|
||||||
private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page,
|
private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page,
|
||||||
int totalPages, @NotNull List<String> availableBooks) {
|
int totalPages, @NotNull List<String> availableBooks,
|
||||||
|
@NotNull Map<Character, Integer> firstInstances) {
|
||||||
ComponentBuilder headerComponent = new ComponentBuilder();
|
ComponentBuilder headerComponent = new ComponentBuilder();
|
||||||
headerComponent.append("Books page " + page + " of " + totalPages).color(ChatColor.GREEN);
|
headerComponent.append("Books page " + page + " of " + totalPages).color(ChatColor.GREEN);
|
||||||
|
|
||||||
|
// Display links to first page where a letter can be found
|
||||||
|
for (int characterIndex = 0; characterIndex <= 25; characterIndex++) {
|
||||||
|
char character = (char) ('a' + characterIndex);
|
||||||
|
if (firstInstances.containsKey(character)) {
|
||||||
|
int pageIndex = (firstInstances.get(character) / booksPerPage) + 1;
|
||||||
|
headerComponent.append(" ", ComponentBuilder.FormatRetention.NONE).append(character + "").color(
|
||||||
|
ChatColor.AQUA).event(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
|
||||||
|
"/" + command + " page" + pageIndex)).event(new HoverEvent(
|
||||||
|
HoverEvent.Action.SHOW_TEXT, new Text("To page " + pageIndex)));
|
||||||
|
} else {
|
||||||
|
headerComponent.append(" " + character, ComponentBuilder.FormatRetention.NONE).color(ChatColor.GRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sender.spigot().sendMessage(headerComponent.create());
|
sender.spigot().sendMessage(headerComponent.create());
|
||||||
|
|
||||||
// Print the previous page button
|
// Print the previous page button
|
||||||
@@ -148,26 +179,30 @@ public final class BookFileHelper {
|
|||||||
String fullCommand = "/" + command + " page" + (page - 1);
|
String fullCommand = "/" + command + " page" + (page - 1);
|
||||||
HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page - 1)));
|
HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page - 1)));
|
||||||
ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
||||||
previousComponent.append("Previous [<] " + ChatColor.DARK_GRAY + fullCommand).event(prevPagePreview).event(prevPageClick);
|
previousComponent.append("Previous [<]").event(prevPagePreview).event(prevPageClick).append(" " +
|
||||||
|
ChatColor.DARK_GRAY + fullCommand, ComponentBuilder.FormatRetention.NONE);
|
||||||
} else {
|
} else {
|
||||||
previousComponent.append("Previous [<]").color(ChatColor.GRAY);
|
previousComponent.append("Previous [<]").color(ChatColor.GRAY);
|
||||||
}
|
}
|
||||||
sender.spigot().sendMessage(previousComponent.create());
|
sender.spigot().sendMessage(previousComponent.create());
|
||||||
|
|
||||||
// Print the main list of all book indexes and titles
|
// Print the main list of all book indexes and titles
|
||||||
HoverEvent interactSuggestion = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click me"));
|
|
||||||
int startIndex = (page - 1) * booksPerPage;
|
int startIndex = (page - 1) * booksPerPage;
|
||||||
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
|
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
|
||||||
ComponentBuilder bookComponent = new ComponentBuilder();
|
ComponentBuilder bookComponent = new ComponentBuilder();
|
||||||
TextComponent indexComponent = new TextComponent("[" + (bookIndex + 1) + "]");
|
TextComponent indexComponent = new TextComponent("[" + (bookIndex + 1) + "]");
|
||||||
indexComponent.setColor(ChatColor.GOLD);
|
indexComponent.setColor(ChatColor.GOLD);
|
||||||
bookComponent.append(indexComponent).event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
bookComponent.append(indexComponent).event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
||||||
"/" + command + " " + (bookIndex + 1))).event(interactSuggestion);
|
"/" + command + " " + (bookIndex + 1))).event(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
|
new Text("Select book by index")));
|
||||||
|
|
||||||
TextComponent bookNameComponent = new TextComponent(availableBooks.get(bookIndex));
|
bookComponent.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
|
||||||
|
TextComponent bookNameComponent = new TextComponent(getNiceName(availableBooks.get(bookIndex)));
|
||||||
bookNameComponent.setColor(ChatColor.WHITE);
|
bookNameComponent.setColor(ChatColor.WHITE);
|
||||||
bookComponent.append(bookNameComponent).event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
bookComponent.append(bookNameComponent).event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
||||||
"/" + command + " " + availableBooks.get(bookIndex))).event(interactSuggestion);
|
"/" + command + " " + availableBooks.get(bookIndex))).event(
|
||||||
|
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path")));
|
||||||
sender.spigot().sendMessage(bookComponent.create());
|
sender.spigot().sendMessage(bookComponent.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,13 +212,49 @@ public final class BookFileHelper {
|
|||||||
String fullCommand = "/" + command + " page" + (page + 1);
|
String fullCommand = "/" + command + " page" + (page + 1);
|
||||||
HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page + 1)));
|
HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page + 1)));
|
||||||
ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
||||||
nextComponent.append("Next [>] " + ChatColor.DARK_GRAY + fullCommand).event(nextPagePreview).event(nextPageClick);
|
nextComponent.append("Next [>]").event(nextPagePreview).event(nextPageClick).append(" " +
|
||||||
|
ChatColor.DARK_GRAY + fullCommand, ComponentBuilder.FormatRetention.NONE);
|
||||||
} else {
|
} else {
|
||||||
nextComponent.append("Next [>]").color(ChatColor.GRAY);
|
nextComponent.append("Next [>]").color(ChatColor.GRAY);
|
||||||
}
|
}
|
||||||
sender.spigot().sendMessage(nextComponent.create());
|
sender.spigot().sendMessage(nextComponent.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a nice name from a book's path
|
||||||
|
*
|
||||||
|
* @param bookPath <p>The path of a book</p>
|
||||||
|
* @return <p>The prettified book name</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static String getNiceName(@NotNull String bookPath) {
|
||||||
|
return bookPath.substring(0, bookPath.length() - 4).replace(",", " by ").replace("_", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map between characters, and the first instance of a book's title starting with that character
|
||||||
|
*
|
||||||
|
* @param books <p>The books to look through</p>
|
||||||
|
* @return <p>The map of the first index containing each character</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Map<Character, Integer> populateLetterIndices(@NotNull List<String> books) {
|
||||||
|
List<Character> firstCharacter = new ArrayList<>(books.size());
|
||||||
|
for (String bookIdentifier : books) {
|
||||||
|
firstCharacter.add(bookIdentifier.toLowerCase().charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Character, Integer> firstEncounter = new HashMap<>();
|
||||||
|
for (int characterIndex = 0; characterIndex <= 25; characterIndex++) {
|
||||||
|
char character = (char) ('a' + characterIndex);
|
||||||
|
int index = Collections.binarySearch(firstCharacter, character);
|
||||||
|
if (index >= 0) {
|
||||||
|
firstEncounter.put(character, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstEncounter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists available files
|
* Lists available files
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user