1.20 update
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
Adds an optional feature which displays the contents of a bookshelf when left-clicked. Sneaking must be used to destroy the bookshelf when enabled. Updates depreciated sign code. Bumps the API version to 1.20 Builds against 1.20
This commit is contained in:
parent
738dbe6e30
commit
f9908db88f
@ -30,6 +30,7 @@ Books without Borders has got your back!
|
|||||||
value
|
value
|
||||||
- Change generation of books. Create tattered books for your RPG server!
|
- Change generation of books. Create tattered books for your RPG server!
|
||||||
- Optionally, make it impossible to duplicate the original version of a book
|
- Optionally, make it impossible to duplicate the original version of a book
|
||||||
|
- Optionally, hit a bookshelf to display the contained books. Sneak to destroy bookshelves with this enabled
|
||||||
|
|
||||||
#### Group encryption
|
#### Group encryption
|
||||||
|
|
||||||
@ -98,6 +99,7 @@ An in-game description of available commands is available through the /bwb comma
|
|||||||
| bookswithoutborders.setbookprice | Allows player to set the cost of creating a book |
|
| bookswithoutborders.setbookprice | Allows player to set the cost of creating a book |
|
||||||
| bookswithoutborders.setgeneration | Allows player to change the generation of a book (Original, Copy, Copy of Copy) |
|
| bookswithoutborders.setgeneration | Allows player to change the generation of a book (Original, Copy, Copy of Copy) |
|
||||||
| bookswithoutborders.clear | Allows player to clear the contents of the held writable book |
|
| bookswithoutborders.clear | Allows player to clear the contents of the held writable book |
|
||||||
|
| bookswithoutborders.peekbookshelf | Allows player to left-click a bookshelf to see the contents of the shelf |
|
||||||
|
|
||||||
### Signs
|
### Signs
|
||||||
|
|
||||||
@ -106,13 +108,13 @@ line.
|
|||||||
|
|
||||||
#### Give sign
|
#### Give sign
|
||||||
|
|
||||||
The **_give_** sign must have **\[Give]** on its second line. The third and fourth line contains the book to be loaded.
|
The **_give_**-sign must have **\[Give]** on its second line. The third and fourth line contains the book to be loaded.
|
||||||
This can either be a numerical id pointing to a publicly saved book, or the full text identifier of the book (book name,
|
This can either be a numerical id pointing to a publicly saved book, or the full text identifier of the book (book name,
|
||||||
author).
|
author).
|
||||||
|
|
||||||
#### Encrypt sign
|
#### Encrypt sign
|
||||||
|
|
||||||
The **_encrypt_** sign must have **\[Encrypt]** on its second line. The third line must contain the encryption key The
|
The **_encrypt_**-sign must have **\[Encrypt]** on its second line. The third line must contain the encryption key The
|
||||||
fourth line can be empty or contain "dna" for dna-based encryption.
|
fourth line can be empty or contain "dna" for dna-based encryption.
|
||||||
|
|
||||||
#### Decrypt sign
|
#### Decrypt sign
|
||||||
@ -136,4 +138,5 @@ The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third li
|
|||||||
| Author_Only_Unsign | Whether to only allow the author of a book to unsign it |
|
| Author_Only_Unsign | Whether to only allow the author of a book to unsign it |
|
||||||
| Author_Only_Save | Whether to only allow saving a player's own books with /savebook |
|
| Author_Only_Save | Whether to only allow saving a player's own books with /savebook |
|
||||||
| Format_Book_After_Signing | Whether to automatically format every book when it's signed |
|
| Format_Book_After_Signing | Whether to automatically format every book when it's signed |
|
||||||
| Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
|
| Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
|
||||||
|
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf to see the shelf's contents. Sneaking is required to destroy a bookshelf if this is enabled. |
|
4
pom.xml
4
pom.xml
@ -103,7 +103,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.19.2-R0.1-SNAPSHOT</version>
|
<version>1.20.1-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -121,7 +121,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains</groupId>
|
<groupId>org.jetbrains</groupId>
|
||||||
<artifactId>annotations</artifactId>
|
<artifactId>annotations</artifactId>
|
||||||
<version>23.0.0</version>
|
<version>24.0.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -24,6 +24,7 @@ import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
|||||||
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
import net.knarcraft.bookswithoutborders.listener.BookEventListener;
|
import net.knarcraft.bookswithoutborders.listener.BookEventListener;
|
||||||
|
import net.knarcraft.bookswithoutborders.listener.BookshelfListener;
|
||||||
import net.knarcraft.bookswithoutborders.listener.PlayerEventListener;
|
import net.knarcraft.bookswithoutborders.listener.PlayerEventListener;
|
||||||
import net.knarcraft.bookswithoutborders.listener.SignEventListener;
|
import net.knarcraft.bookswithoutborders.listener.SignEventListener;
|
||||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||||
@ -130,6 +131,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
pluginManager.registerEvents(new PlayerEventListener(), this);
|
pluginManager.registerEvents(new PlayerEventListener(), this);
|
||||||
pluginManager.registerEvents(new SignEventListener(), this);
|
pluginManager.registerEvents(new SignEventListener(), this);
|
||||||
pluginManager.registerEvents(new BookEventListener(), this);
|
pluginManager.registerEvents(new BookEventListener(), this);
|
||||||
|
pluginManager.registerEvents(new BookshelfListener(), this);
|
||||||
} else {
|
} else {
|
||||||
this.getPluginLoader().disablePlugin(this);
|
this.getPluginLoader().disablePlugin(this);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ public class BooksWithoutBordersConfig {
|
|||||||
private static boolean adminDecrypt;
|
private static boolean adminDecrypt;
|
||||||
private static boolean formatBooks;
|
private static boolean formatBooks;
|
||||||
private static boolean changeGenerationOnCopy;
|
private static boolean changeGenerationOnCopy;
|
||||||
|
private static boolean enableBookshelfPeeking;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the books without borders settings class
|
* Initializes the books without borders settings class
|
||||||
@ -127,6 +128,15 @@ public class BooksWithoutBordersConfig {
|
|||||||
return authorOnlySave;
|
return authorOnlySave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether players can left-click a bookshelf to peek at the contained books
|
||||||
|
*
|
||||||
|
* @return <p>True if players can peek at the contained books</p>
|
||||||
|
*/
|
||||||
|
public static boolean getEnableBookshelfPeeking() {
|
||||||
|
return enableBookshelfPeeking;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether to use YML, not TXT, for saving books
|
* Gets whether to use YML, not TXT, for saving books
|
||||||
*
|
*
|
||||||
@ -272,6 +282,7 @@ public class BooksWithoutBordersConfig {
|
|||||||
config.set(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode(), firstBooks);
|
config.set(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode(), firstBooks);
|
||||||
config.set(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(), welcomeMessage);
|
config.set(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(), welcomeMessage);
|
||||||
config.set(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(), formatBooks);
|
config.set(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(), formatBooks);
|
||||||
|
config.set(ConfigOption.ENABLE_BOOKSHELF_PEEKING.getConfigNode(), enableBookshelfPeeking);
|
||||||
|
|
||||||
String itemTypeNode = ConfigOption.PRICE_ITEM_TYPE.getConfigNode();
|
String itemTypeNode = ConfigOption.PRICE_ITEM_TYPE.getConfigNode();
|
||||||
if (bookPriceType != null) {
|
if (bookPriceType != null) {
|
||||||
@ -319,7 +330,8 @@ public class BooksWithoutBordersConfig {
|
|||||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||||
try {
|
try {
|
||||||
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
||||||
bookDuplicateLimit = getInt(config, ConfigOption.MAX_DUPLICATES);
|
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
||||||
|
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
||||||
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
||||||
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
||||||
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
||||||
@ -330,6 +342,7 @@ public class BooksWithoutBordersConfig {
|
|||||||
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
||||||
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
||||||
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||||
|
enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
|
||||||
|
|
||||||
//Convert string into material
|
//Convert string into material
|
||||||
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
||||||
@ -347,7 +360,8 @@ public class BooksWithoutBordersConfig {
|
|||||||
bookPriceType = material;
|
bookPriceType = material;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bookPriceQuantity = getDouble(config, ConfigOption.PRICE_QUANTITY);
|
bookPriceQuantity = config.getDouble(ConfigOption.PRICE_QUANTITY.getConfigNode(),
|
||||||
|
(Double) ConfigOption.PRICE_QUANTITY.getDefaultValue());
|
||||||
|
|
||||||
//Make sure titleAuthorSeparator is a valid value
|
//Make sure titleAuthorSeparator is a valid value
|
||||||
titleAuthorSeparator = cleanString(titleAuthorSeparator);
|
titleAuthorSeparator = cleanString(titleAuthorSeparator);
|
||||||
@ -366,28 +380,6 @@ public class BooksWithoutBordersConfig {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the double value of the given config option
|
|
||||||
*
|
|
||||||
* @param config <p>The configuration to read from</p>
|
|
||||||
* @param configOption <p>The configuration option to get the value for</p>
|
|
||||||
* @return <p>The value of the option</p>
|
|
||||||
*/
|
|
||||||
private static double getDouble(Configuration config, ConfigOption configOption) {
|
|
||||||
return config.getDouble(configOption.getConfigNode(), (Double) configOption.getDefaultValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the integer value of the given config option
|
|
||||||
*
|
|
||||||
* @param config <p>The configuration to read from</p>
|
|
||||||
* @param configOption <p>The configuration option to get the value for</p>
|
|
||||||
* @return <p>The value of the option</p>
|
|
||||||
*/
|
|
||||||
private static int getInt(Configuration config, ConfigOption configOption) {
|
|
||||||
return config.getInt(configOption.getConfigNode(), (Integer) configOption.getDefaultValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the string value of the given config option
|
* Gets the string value of the given config option
|
||||||
*
|
*
|
||||||
|
@ -73,7 +73,13 @@ public enum ConfigOption {
|
|||||||
/**
|
/**
|
||||||
* Whether to automatically format every signed book
|
* Whether to automatically format every signed book
|
||||||
*/
|
*/
|
||||||
FORMAT_AFTER_SIGNING("Options.Format_Book_After_Signing", false);
|
FORMAT_AFTER_SIGNING("Options.Format_Book_After_Signing", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether hitting a bookshelf should display information about the contained books
|
||||||
|
*/
|
||||||
|
ENABLE_BOOKSHELF_PEEKING("Options.Enable_Book_Peeking", false),
|
||||||
|
;
|
||||||
|
|
||||||
private final String configNode;
|
private final String configNode;
|
||||||
private final Object defaultValue;
|
private final Object defaultValue;
|
||||||
|
@ -52,6 +52,7 @@ public class SubstitutionCipher {
|
|||||||
// but in reverse. Could probably be combined into one
|
// but in reverse. Could probably be combined into one
|
||||||
// method with a flag for encryption / decryption, but
|
// method with a flag for encryption / decryption, but
|
||||||
// I'm lazy.
|
// I'm lazy.
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public String decrypt(String in, String key) {
|
public String decrypt(String in, String key) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
if (key != null && key.length() > 0) {
|
if (key != null && key.length() > 0) {
|
||||||
@ -79,6 +80,7 @@ public class SubstitutionCipher {
|
|||||||
// the one time pad encryption is very secure, and
|
// the one time pad encryption is very secure, and
|
||||||
// encryption works just like decryption, but is
|
// encryption works just like decryption, but is
|
||||||
// vulnerable if the same key is used more than once.
|
// vulnerable if the same key is used more than once.
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public String oneTimePad(String in, String key) {
|
public String oneTimePad(String in, String key) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
for (int i = 0; i < in.length(); i++) {
|
for (int i = 0; i < in.length(); i++) {
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.listener;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import org.bukkit.block.ChiseledBookshelf;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.inventory.ChiseledBookshelfInventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSuccessColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener for bookshelf clicking
|
||||||
|
*/
|
||||||
|
public class BookshelfListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBookshelfClick(PlayerInteractEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
// If left-clicking a chiseled bookshelf and not sneaking, display contents
|
||||||
|
if (!event.hasBlock() || !(event.getClickedBlock().getState() instanceof ChiseledBookshelf chiseledBookshelf) ||
|
||||||
|
event.getAction() != Action.LEFT_CLICK_BLOCK || player.isSneaking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bookshelf peeking is enabled, and the player can peek
|
||||||
|
if (!BooksWithoutBordersConfig.getEnableBookshelfPeeking() ||
|
||||||
|
!event.getPlayer().hasPermission("bookswithoutborders.peekbookshelf")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setUseInteractedBlock(Event.Result.DENY);
|
||||||
|
event.setUseItemInHand(Event.Result.DENY);
|
||||||
|
|
||||||
|
ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory();
|
||||||
|
player.sendMessage(getBookshelfDescription(bookshelfInventory));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description for a bookshelf's contents
|
||||||
|
*
|
||||||
|
* @param bookshelfInventory <p>The inventory of the bookshelf to describe</p>
|
||||||
|
* @return <p>A textual description of the bookshelf's contents</p>
|
||||||
|
*/
|
||||||
|
private String getBookshelfDescription(ChiseledBookshelfInventory bookshelfInventory) {
|
||||||
|
StringBuilder builder = new StringBuilder(getSuccessColor() + "Books in shelf:");
|
||||||
|
for (ItemStack itemStack : bookshelfInventory.getStorageContents()) {
|
||||||
|
if (itemStack == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ItemMeta meta = itemStack.getItemMeta();
|
||||||
|
builder.append("\n ").append(ChatColor.GRAY).append(" - ");
|
||||||
|
if (meta instanceof BookMeta bookMeta) {
|
||||||
|
builder.append(getBookDescription(bookMeta));
|
||||||
|
} else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) {
|
||||||
|
builder.append(getEnchantedBookDescription(enchantmentStorageMeta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description of a book
|
||||||
|
*
|
||||||
|
* @param bookMeta <p>The metadata for the book to describe</p>
|
||||||
|
* @return <p>The book's description</p>
|
||||||
|
*/
|
||||||
|
private String getBookDescription(BookMeta bookMeta) {
|
||||||
|
String title;
|
||||||
|
String author;
|
||||||
|
if (!bookMeta.hasTitle()) {
|
||||||
|
title = "Untitled";
|
||||||
|
} else {
|
||||||
|
title = bookMeta.getTitle();
|
||||||
|
}
|
||||||
|
if (!bookMeta.hasAuthor()) {
|
||||||
|
author = "Unknown";
|
||||||
|
} else {
|
||||||
|
author = bookMeta.getAuthor();
|
||||||
|
}
|
||||||
|
return title + " by " + author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description of an enchanted book
|
||||||
|
*
|
||||||
|
* @param enchantmentStorageMeta <p>The metadata for the enchanted book to describe</p>
|
||||||
|
* @return <p>The enchanted book's description</p>
|
||||||
|
*/
|
||||||
|
private String getEnchantedBookDescription(EnchantmentStorageMeta enchantmentStorageMeta) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("Enchanted book (");
|
||||||
|
Map<Enchantment, Integer> enchantmentMap = enchantmentStorageMeta.getStoredEnchants();
|
||||||
|
List<String> enchantments = new ArrayList<>(enchantmentMap.size());
|
||||||
|
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentMap.entrySet()) {
|
||||||
|
enchantments.add(getEnchantmentName(enchantmentEntry.getKey()) + " " +
|
||||||
|
IntegerToRomanConverter.getRomanNumber(enchantmentEntry.getValue()));
|
||||||
|
}
|
||||||
|
builder.append(String.join(", ", enchantments));
|
||||||
|
builder.append(")");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a prettified name of an enchantment
|
||||||
|
*
|
||||||
|
* @param enchantment <p>The enchantment to get the name of</p>
|
||||||
|
* @return <p>The prettified enchantment name</p>
|
||||||
|
*/
|
||||||
|
private String getEnchantmentName(Enchantment enchantment) {
|
||||||
|
return uppercaseFirst(enchantment.getKey().getKey().replace("_", " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns the first character of each of a string's words into uppercase
|
||||||
|
*
|
||||||
|
* @param input <p>The input to uppercase</p>
|
||||||
|
* @return <p>The input string with more uppercase</p>
|
||||||
|
*/
|
||||||
|
private String uppercaseFirst(String input) {
|
||||||
|
String[] parts = input.split(" ");
|
||||||
|
for (int i = 0; i < parts.length; i++) {
|
||||||
|
parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1);
|
||||||
|
}
|
||||||
|
return String.join(" ", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,8 @@ import net.md_5.bungee.api.ChatColor;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Tag;
|
import org.bukkit.Tag;
|
||||||
import org.bukkit.block.Sign;
|
import org.bukkit.block.Sign;
|
||||||
|
import org.bukkit.block.sign.Side;
|
||||||
|
import org.bukkit.block.sign.SignSide;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@ -100,18 +102,22 @@ public class SignEventListener implements Listener {
|
|||||||
event.setUseItemInHand(Event.Result.DENY);
|
event.setUseItemInHand(Event.Result.DENY);
|
||||||
//The player right-clicked a sign
|
//The player right-clicked a sign
|
||||||
Sign sign = (Sign) event.getClickedBlock().getState();
|
Sign sign = (Sign) event.getClickedBlock().getState();
|
||||||
if (signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
if (!signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
||||||
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
|
return;
|
||||||
encryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
}
|
||||||
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
|
|
||||||
decryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
|
||||||
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
|
encryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||||
getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
|
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
|
||||||
giveBook(sign, player);
|
decryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||||
} else {
|
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
|
||||||
player.sendMessage("Sign command " + sign.getLine(1) + " " + sign.getLine(2) + " invalid");
|
getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
|
||||||
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
|
giveBook(sign, player);
|
||||||
}
|
} else {
|
||||||
|
SignSide front = sign.getSide(Side.FRONT);
|
||||||
|
player.sendMessage(String.format("Sign command %s %s is invalid", front.getLine(1),
|
||||||
|
front.getLine(2)));
|
||||||
|
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
|
||||||
}
|
}
|
||||||
} else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR
|
} else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR
|
||||||
|| event.getAction() == Action.LEFT_CLICK_BLOCK)) {
|
|| event.getAction() == Action.LEFT_CLICK_BLOCK)) {
|
||||||
@ -138,7 +144,7 @@ public class SignEventListener implements Listener {
|
|||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
|
|
||||||
//Converts user supplied key into integer form
|
//Converts user supplied key into integer form
|
||||||
String lineText = ChatColor.stripColor(sign.getLine(2));
|
String lineText = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
|
||||||
String key = EncryptionHelper.getNumberKeyFromStringKey(lineText);
|
String key = EncryptionHelper.getNumberKeyFromStringKey(lineText);
|
||||||
|
|
||||||
ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false);
|
ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false);
|
||||||
@ -156,9 +162,9 @@ public class SignEventListener implements Listener {
|
|||||||
* @return <p>The color of the sign</p>
|
* @return <p>The color of the sign</p>
|
||||||
*/
|
*/
|
||||||
private ChatColor getSignLine2Color(Sign sign) {
|
private ChatColor getSignLine2Color(Sign sign) {
|
||||||
String line = sign.getLine(2);
|
String line = sign.getSide(Side.FRONT).getLine(2);
|
||||||
if (!ChatColor.stripColor(line).equals(line)) {
|
if (!ChatColor.stripColor(line).equals(line)) {
|
||||||
return ChatColor.getByChar(sign.getLine(2).substring(1, 2).charAt(0));
|
return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -174,7 +180,7 @@ public class SignEventListener implements Listener {
|
|||||||
* @return <p>True if the given string is what's on the sign</p>
|
* @return <p>True if the given string is what's on the sign</p>
|
||||||
*/
|
*/
|
||||||
private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) {
|
private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) {
|
||||||
String line = sign.getLine(lineNumber);
|
String line = sign.getSide(Side.FRONT).getLine(lineNumber);
|
||||||
return line.equalsIgnoreCase(color + compareTo);
|
return line.equalsIgnoreCase(color + compareTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +287,7 @@ public class SignEventListener implements Listener {
|
|||||||
*/
|
*/
|
||||||
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
|
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
|
||||||
ItemStack eBook;
|
ItemStack eBook;
|
||||||
String[] lines = sign.getLines();
|
String[] lines = sign.getSide(Side.FRONT).getLines();
|
||||||
boolean mainHand = hand == EquipmentSlot.HAND;
|
boolean mainHand = hand == EquipmentSlot.HAND;
|
||||||
if (heldItemType == Material.WRITTEN_BOOK) {
|
if (heldItemType == Material.WRITTEN_BOOK) {
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
@ -300,7 +306,7 @@ public class SignEventListener implements Listener {
|
|||||||
* @param player <p>The player which clicked the sign</p>
|
* @param player <p>The player which clicked the sign</p>
|
||||||
*/
|
*/
|
||||||
private void giveBook(Sign sign, Player player) {
|
private void giveBook(Sign sign, Player player) {
|
||||||
String fileName = ChatColor.stripColor(sign.getLine(2));
|
String fileName = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
|
||||||
boolean isLoadListNumber = false;
|
boolean isLoadListNumber = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -310,7 +316,7 @@ public class SignEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Add the third line to the second line for the full filename
|
//Add the third line to the second line for the full filename
|
||||||
String thirdLine = sign.getLine(3);
|
String thirdLine = sign.getSide(Side.FRONT).getLine(3);
|
||||||
if (!isLoadListNumber && thirdLine.length() >= 2) {
|
if (!isLoadListNumber && thirdLine.length() >= 2) {
|
||||||
fileName += ChatColor.stripColor(thirdLine);
|
fileName += ChatColor.stripColor(thirdLine);
|
||||||
}
|
}
|
||||||
|
@ -26,22 +26,22 @@ public enum BookHoldingState {
|
|||||||
UNSIGNED_MAIN_HAND,
|
UNSIGNED_MAIN_HAND,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The player is holding one signed book in their off hand
|
* The player is holding one signed book in their off-hand
|
||||||
*/
|
*/
|
||||||
SIGNED_OFF_HAND,
|
SIGNED_OFF_HAND,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The player is holding one unsigned book in their off hand
|
* The player is holding one unsigned book in their off-hand
|
||||||
*/
|
*/
|
||||||
UNSIGNED_OFF_HAND,
|
UNSIGNED_OFF_HAND,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The player is holding one signed book in their main hand and one unsigned book in their off hand
|
* The player is holding one signed book in their main hand and one unsigned book in their off-hand
|
||||||
*/
|
*/
|
||||||
SIGNED_MAIN_HAND_UNSIGNED_OFF_HAND,
|
SIGNED_MAIN_HAND_UNSIGNED_OFF_HAND,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The player is holding one unsigned book in their main hand and one signed book in their off hand
|
* The player is holding one unsigned book in their main hand and one signed book in their off-hand
|
||||||
*/
|
*/
|
||||||
UNSIGNED_MAIN_HAND_SIGNED_OFF_HAND,
|
UNSIGNED_MAIN_HAND_SIGNED_OFF_HAND,
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ public enum ItemSlot {
|
|||||||
MAIN_HAND,
|
MAIN_HAND,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The item is in the player's off hand
|
* The item is in the player's off-hand
|
||||||
*/
|
*/
|
||||||
OFF_HAND,
|
OFF_HAND,
|
||||||
|
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A converter from an integer to a roman numeral
|
||||||
|
*/
|
||||||
|
public final class IntegerToRomanConverter {
|
||||||
|
|
||||||
|
private final static List<Integer> romanValues = new ArrayList<>();
|
||||||
|
private final static List<Character> romanCharacters = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Initialize the roman numbers
|
||||||
|
romanValues.add(1000);
|
||||||
|
romanValues.add(500);
|
||||||
|
romanValues.add(100);
|
||||||
|
romanValues.add(50);
|
||||||
|
romanValues.add(10);
|
||||||
|
romanValues.add(5);
|
||||||
|
romanValues.add(1);
|
||||||
|
|
||||||
|
romanCharacters.add('M');
|
||||||
|
romanCharacters.add('D');
|
||||||
|
romanCharacters.add('C');
|
||||||
|
romanCharacters.add('L');
|
||||||
|
romanCharacters.add('X');
|
||||||
|
romanCharacters.add('V');
|
||||||
|
romanCharacters.add('I');
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntegerToRomanConverter() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the given number as a roman number string
|
||||||
|
*
|
||||||
|
* @param number <p>The number to convert</p>
|
||||||
|
* @return <p>The roman representation of the number</p>
|
||||||
|
*/
|
||||||
|
public static String getRomanNumber(int number) {
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
int remainder = number;
|
||||||
|
for (int i = 0; i < romanCharacters.size(); i++) {
|
||||||
|
int romanValue = romanValues.get(i);
|
||||||
|
char romanCharacter = romanCharacters.get(i);
|
||||||
|
|
||||||
|
// Repeat the roman character, and calculate the new remainder
|
||||||
|
if (remainder >= romanValue) {
|
||||||
|
output.append(repeat(romanCharacter, remainder / romanValue));
|
||||||
|
remainder = remainder % romanValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit early to prevent unexpected trailing characters
|
||||||
|
if (remainder == 0) {
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the special case IV and similar
|
||||||
|
for (int j = i; j < romanCharacters.size(); j++) {
|
||||||
|
int value = romanValues.get(j);
|
||||||
|
int difference = Math.max(romanValue - value, 0);
|
||||||
|
|
||||||
|
/* If the remainder is "one" less than the current roman value, we hit the IV/IX/XL case.
|
||||||
|
Note that 5 triggers the special case when 10 is tested, as 5 = 10 - 5, which requires a test, so it
|
||||||
|
can be filtered out. */
|
||||||
|
if (remainder == difference && value != romanValue / 2) {
|
||||||
|
output.append(romanCharacters.get(j)).append(romanCharacter);
|
||||||
|
remainder = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeats the given character
|
||||||
|
*
|
||||||
|
* @param character <p>The character to repeat</p>
|
||||||
|
* @param times <p>The number of times to repeat the character</p>
|
||||||
|
* @return <p>The repeated string</p>
|
||||||
|
*/
|
||||||
|
private static String repeat(char character, int times) {
|
||||||
|
return String.valueOf(character).repeat(Math.max(0, times));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,4 +30,6 @@ Options:
|
|||||||
# Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the
|
# Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the
|
||||||
# vanilla behavior where a copy of a copy cannot be copied further.
|
# vanilla behavior where a copy of a copy cannot be copied further.
|
||||||
Change_Generation_On_Copy: false
|
Change_Generation_On_Copy: false
|
||||||
|
# Whether to enable hitting a chiseled bookshelf to see the shelf's contents. Sneaking is required to destroy a
|
||||||
|
# bookshelf if this is enabled.
|
||||||
|
Enable_Book_Peeking: false
|
@ -1,7 +1,7 @@
|
|||||||
name: BooksWithoutBorders
|
name: BooksWithoutBorders
|
||||||
version: '${project.version}'
|
version: '${project.version}'
|
||||||
main: net.knarcraft.bookswithoutborders.BooksWithoutBorders
|
main: net.knarcraft.bookswithoutborders.BooksWithoutBorders
|
||||||
api-version: 1.18
|
api-version: 1.20
|
||||||
prefix: Books Without Borders
|
prefix: Books Without Borders
|
||||||
authors: [ EpicKnarvik97, AkiraAkiba ]
|
authors: [ EpicKnarvik97, AkiraAkiba ]
|
||||||
description: A continuation of the original Books Without Borders
|
description: A continuation of the original Books Without Borders
|
||||||
@ -131,6 +131,7 @@ permissions:
|
|||||||
bookswithoutborders.save: true
|
bookswithoutborders.save: true
|
||||||
bookswithoutborders.load: true
|
bookswithoutborders.load: true
|
||||||
bookswithoutborders.delete: true
|
bookswithoutborders.delete: true
|
||||||
|
bookswithoutborders.peekbookshelf: true
|
||||||
bookswithoutborders.alterbooks:
|
bookswithoutborders.alterbooks:
|
||||||
description: Allows player to change books' data such as lore/title/author/generation/formatting and unsigning books
|
description: Allows player to change books' data such as lore/title/author/generation/formatting and unsigning books
|
||||||
children:
|
children:
|
||||||
@ -190,4 +191,6 @@ permissions:
|
|||||||
bookswithoutborders.reload:
|
bookswithoutborders.reload:
|
||||||
description: Allows player to reload this plugin
|
description: Allows player to reload this plugin
|
||||||
bookswithoutborders.setgeneration:
|
bookswithoutborders.setgeneration:
|
||||||
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
|
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
|
||||||
|
bookswithoutborders.peekbookshelf:
|
||||||
|
description: Allows player to left-click a bookshelf to see the contents of the shelf
|
@ -0,0 +1,40 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter.getRomanNumber;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test class for IntegerToRomanConverter
|
||||||
|
*/
|
||||||
|
public class IntegerToRomanConverterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basicNumbersTest() {
|
||||||
|
assertEquals("I", getRomanNumber(1));
|
||||||
|
assertEquals("II", getRomanNumber(2));
|
||||||
|
assertEquals("III", getRomanNumber(3));
|
||||||
|
assertEquals("IV", getRomanNumber(4));
|
||||||
|
assertEquals("V", getRomanNumber(5));
|
||||||
|
assertEquals("X", getRomanNumber(10));
|
||||||
|
assertEquals("XV", getRomanNumber(15));
|
||||||
|
assertEquals("XX", getRomanNumber(20));
|
||||||
|
assertEquals("L", getRomanNumber(50));
|
||||||
|
assertEquals("C", getRomanNumber(100));
|
||||||
|
assertEquals("D", getRomanNumber(500));
|
||||||
|
assertEquals("M", getRomanNumber(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nineFourTest() {
|
||||||
|
assertEquals("IV", getRomanNumber(4));
|
||||||
|
assertEquals("IX", getRomanNumber(9));
|
||||||
|
assertEquals("XIV", getRomanNumber(14));
|
||||||
|
assertEquals("XIX", getRomanNumber(19));
|
||||||
|
assertEquals("XXIV", getRomanNumber(24));
|
||||||
|
assertEquals("XL", getRomanNumber(40));
|
||||||
|
assertEquals("IL", getRomanNumber(49));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user