Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
b577b1ff75 | |||
c27756f7dc | |||
90bab4a148 | |||
f005b8f8e5 | |||
0fdfd81579 | |||
6820b71dde | |||
28b7849ff5 | |||
e893684dec | |||
f1a8db4f5e | |||
47fabe5806 | |||
ec6def15f9 | |||
13cbe883ed | |||
d2403d247b | |||
c6c018ee88 | |||
8b47aeb8f2 | |||
396c0a9d41 | |||
c995a4fc0f | |||
40512dd771 | |||
5d340af6f2 | |||
70ad6390db | |||
2a5c5b310d | |||
94375eee4b | |||
1eb9c370bc | |||
1100f181be | |||
7467645bcd | |||
11108011a5 | |||
ce249a93b3 | |||
7c229fb459 | |||
f243bf32e7 | |||
a7c284ade2 | |||
0d4d87373c | |||
542cd03bdc | |||
5e85dfd3e4 | |||
af0b0fd12e | |||
a963733734 | |||
7dd201d0d0 | |||
a95d737414 |
172
README.md
172
README.md
@ -1,10 +1,9 @@
|
|||||||
# Books Without Borders
|
# Books Without Borders
|
||||||
|
|
||||||
This is an attempt at a rewrite of the Books Without Borders plugin. This rewrite uses the source code given
|
This is a rewrite of the Books Without Borders plugin. This rewrite originally used the source code given
|
||||||
at [the original bukkit page](https://dev.bukkit.org/projects/books-without-borders). I'm not planning any new features
|
at [the original bukkit page](https://dev.bukkit.org/projects/books-without-borders). While the old plugin still worked
|
||||||
at this time. The only goal is to make it 1.17.1 compliant, but I'll make the code more maintainable along the way.
|
the last time I checked, this plugin does not use any depreciated function calls, making sure it works for the
|
||||||
While the original version still works, it's using a lot of depreciated function calls which will most likely break in
|
foreseeable future.
|
||||||
the future.
|
|
||||||
|
|
||||||
## Books without Borders!
|
## Books without Borders!
|
||||||
|
|
||||||
@ -13,90 +12,92 @@ Books without Borders has got your back!
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Export written books and book and quills to .txt or .yml files
|
- Export written books and book and quills to .txt or .yml files
|
||||||
* Import books from files as written books or unsigned books
|
- Import books from files as written books or unsigned books
|
||||||
* Text files can be any length, and the import process fits the content to the correct page length
|
- Text files can be any length, and the import process fits the content to the correct page length
|
||||||
* Books can be saved privately, or to a directory visible server wide
|
- Books can be saved privately, or to a directory visible server wide
|
||||||
* Encrypt books to prevent other players from reading them
|
- Encrypt books to prevent other players from reading them
|
||||||
* Give, encrypt, or decrypt held books with signs
|
- Give, encrypt, or decrypt held books with signs
|
||||||
* Give players books via command blocks
|
- Give players books via command blocks
|
||||||
* Unsign or copy held books with a simple command
|
- Unsign or copy held books with a simple command
|
||||||
* Give first time players a single book or a set of books when they join
|
- Give first time players a single book or a set of books when they join
|
||||||
* Configurable option to require certain items or pay via Vault compatible economy to create books via command
|
- Configurable option to require certain items or pay via Vault compatible economy to create books via command
|
||||||
* Add lore to any item with a simple command
|
- Add lore to any item with a simple command
|
||||||
* Supports adding and saving color to title, lore, and book contents
|
- Supports adding and saving color to title, lore, and book contents
|
||||||
* Color and formatting codes can be manually turned into formatting using /formatbook
|
- Color and formatting codes can be manually turned into formatting using /formatbook
|
||||||
* Formatting and color codes can be turned into formatting once any book is signed. This is enabled through a config
|
- Clear a book, and start anew with /clearbook
|
||||||
|
- Formatting and color codes can be turned into formatting once any book is signed. This is enabled through a config
|
||||||
value
|
value
|
||||||
|
- Change generation of books. Create tattered books for your RPG server!
|
||||||
|
- Optionally, make it impossible to duplicate the original version of a book
|
||||||
|
|
||||||
#### Group encryption
|
#### Group encryption
|
||||||
|
|
||||||
* Group encryption allows every player with the bookswithoutborders.decrypt.\<group> permission to decrypt the encrypted
|
- Group encryption allows every player with the bookswithoutborders.decrypt.\<group> permission to decrypt the encrypted
|
||||||
book without using a password.
|
book without using a password.
|
||||||
|
|
||||||
### Commands:
|
### Commands:
|
||||||
|
|
||||||
An in-game description of available commands is available through the /bwb command.
|
An in-game description of available commands is available through the /bwb command.
|
||||||
|
|
||||||
* /bookswithoutborders - Displays information about commands (and permissions if the user has bookswithoutborders.admin)
|
| Command | Arguments | Description |
|
||||||
* /copybook <# of copies> - Copies the book the player is holding
|
| --- | --- | --- |
|
||||||
* /decryptbook <key> - Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used
|
| /bookswithoutborders | None | Displays information about commands (and permissions if the user has bookswithoutborders.admin) |
|
||||||
to encrypt the held book
|
| /copybook | \<# of copies> | Copies the book the player is holding |
|
||||||
* /deletebook <file name or number> - Deletes the specified file in the player's directory
|
| /decryptbook | \<key> | Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used to encrypt the held book |
|
||||||
* /deletepublicbook <file name or number> - Same as deletebook, but deletes files in the public directory
|
| /deletebook | \<file name or number> | Deletes the specified file in the player's directory |
|
||||||
* encryptbook <key> \[encryption style] - Encrypts the book the player is holding. "key" is required and can be any
|
| /deletepublicbook | \<file name or number> | Same as deletebook, but deletes files in the public directory |
|
||||||
phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
| encryptbook | \<key> \[encryption style] | Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or "" |
|
||||||
* /formatbook - Formats the held written book (converts color and formatting codes to the corresponding formatted text)
|
| /formatbook | None | Formats the held written book (converts color and formatting codes to the corresponding formatted text) |
|
||||||
* /givebook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Gives the selected player a
|
| /givebook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Gives the selected player a book from your personal directory |
|
||||||
book from your personal directory
|
| /givepublicbook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Same as givebook, but uses books from the public directory |
|
||||||
* /givepublicbook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Same as givebook, but
|
| /loadbook | \<file name or number> \[# of copies] \[signed (true/false)] | Creates a book from the specified file and gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book will be signed, if false it will be unsigned |
|
||||||
uses books from the public directory
|
| /loadpublicbook | \<file name or number> \[# of copies] \[signed (true/false)] | Same as loadbook, but views files in the public directory |
|
||||||
* /loadbook <file name or number> \[# of copies] \[signed (true/false)] - Creates a book from the specified file and
|
| /reload | None | Reloads BwB's configuration file |
|
||||||
gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book
|
| /savebook | \[overwrite (true/false)] | Saves the book the player is holding to a text file in a private directory. If true is specified, a book of the same name by the same author will be overwritten by the new book |
|
||||||
will be signed, if false it will be unsigned
|
| /savepublicbook | \[overwrite (true/false)] | Same as savebook, but saves files in the public directory |
|
||||||
* /loadpublicbook <file name or number> \[# of copies] \[signed (true/false)] - Same as loadbook, but views files in the
|
| /setbookauthor | \<author> | Sets the author of the book the player is holding |
|
||||||
public directory
|
| /setbookgeneration | \<generation> | Sets the generation of the held book (ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED) |
|
||||||
* /reload - Reloads BwB's configuration file
|
| /setbookprice | \<item/eco> \<quantity> | Sets the per-book price to create a book via commands. If "Item", the item in the player's hand in the amount of \<quantity> will be the price. If "Eco", a Vault based economy will be used for price. If neither \<Item/Eco> nor \<quantity> are specified, the current price to create books will be removed. |
|
||||||
* /savebook \[overwrite (true/false)] - Saves the book the player is holding to a text file in a private directory. If
|
| /setlore | \<new lore> | Sets the lore of the item the player is holding. Insert the lore_line_separator character to force a new line ("~" by default) |
|
||||||
true is specified, a book of the same name by the same author will be overwritten by the new book
|
| /settitle | \<title> | Sets the title of the book/item the player is holding |
|
||||||
* /savepublicbook \[overwrite (true/false)] - Same as savebook, but saves files in the public directory
|
| /unsignbook | None | Un-signs the book the player is holding |
|
||||||
* /setbookauthor <author> - Sets the author of the book the player is holding
|
| /clearbook | None | Removes all text from the held un-signed book |
|
||||||
* /setbookprice <item/eco> <quantity> - Sets the per-book price to create a book via commands. If "Item", the item in
|
|
||||||
the player's hand in the amount of <quantity> will be the price. If "Eco", a Vault based economy will be used for
|
|
||||||
price. If neither <Item/Eco> nor <quantity> are specified, the current price to create books will be removed.
|
|
||||||
* /setlore <new lore> - Sets the lore of the item the player is holding. Insert the lore_line_separator character to
|
|
||||||
force a new line ("~" by default)
|
|
||||||
* /settitle <title> - Sets the title of the book/item the player is holding
|
|
||||||
* /unsignbook - Un-signs the book the player is holding
|
|
||||||
|
|
||||||
### Permissions:
|
### Permissions:
|
||||||
|
|
||||||
* bookswithoutborders.* - Grants all permissions
|
| Node | Description |
|
||||||
* bookswithoutborders.admin - Grants all permissions
|
| --- | --- |
|
||||||
* bookswithoutborders.use - Allows player to use commands to save/load/delete in their personal directory
|
| bookswithoutborders.* | Grants all permissions |
|
||||||
* bookswithoutborders.alterbooks - Allows player to change books' data such as lore/title/author/formatting and
|
| bookswithoutborders.admin | Grants all permissions |
|
||||||
unsigning books
|
| bookswithoutborders.use | Allows player to use commands to save/load/delete in their personal directory |
|
||||||
* bookswithoutborders.format - Allows a player to format a book
|
| bookswithoutborders.alterbooks | Allows player to change books' data such as lore/title/author/generation/formatting and un-signing books |
|
||||||
* bookswithoutborders.save - Allows a player to save books to their personal directory
|
| bookswithoutborders.reload | Allows player to reload this plugin |
|
||||||
* bookswithoutborders.load - Allows player to load books from their personal directory
|
| bookswithoutborders.format | Allows a player to format a book |
|
||||||
* bookswithoutborders.delete - Allows player to delete books from their personal directory
|
| bookswithoutborders.save | Allows a player to save books to their personal directory |
|
||||||
* bookswithoutborders.unsign - Allows player to un-sign books
|
| bookswithoutborders.load | Allows player to load books from their personal directory |
|
||||||
* bookswithoutborders.copy - Allows player to copy books
|
| bookswithoutborders.delete | Allows player to delete books from their personal directory |
|
||||||
* bookswithoutborders.loadpublic - Allows player to load from the public directory
|
| bookswithoutborders.unsign | Allows player to un-sign books |
|
||||||
* bookswithoutborders.savepublic - Allows player to save to the public directory
|
| bookswithoutborders.copy | Allows player to copy books |
|
||||||
* bookswithoutborders.encrypt - Allows player to encrypt books
|
| bookswithoutborders.loadpublic | Allows player to load from the public directory |
|
||||||
* bookswithoutborders.groupencrypt - Allows player to use group-based encryption
|
| bookswithoutborders.savepublic | Allows player to save to the public directory |
|
||||||
* bookswithoutborders.decrypt - Allows player to decrypt books
|
| bookswithoutborders.encrypt | Allows player to encrypt books |
|
||||||
* bookswithoutborders.decrypt.agroup - Allows player to decrypt books group-encrypted for group "agroup"
|
| bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
|
||||||
* bookswithoutborders.signs - Allows player to create signs that give/encrypt/decrypt books
|
| bookswithoutborders.decrypt | Allows player to decrypt books |
|
||||||
* bookswithoutborders.give - Allows player to give another player one of their privately saved books
|
| bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
|
||||||
* bookswithoutborders.givepublic - Allows a player to give another player a book from the public directory
|
| bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
|
||||||
* bookswithoutborders.settitle - Allows player to set the title of the currently held book
|
| bookswithoutborders.give | Allows player to give another player one of their privately saved books |
|
||||||
* bookswithoutborders.setauthor - Allows player to set the author of the currently held book
|
| bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
|
||||||
* bookswithoutborders.setlore - Allows player to set the lore of the currently held item
|
| bookswithoutborders.settitle | Allows player to set the title of the currently held book |
|
||||||
* bookswithoutborders.bypassauthoronlycopy - Allows player to ignore Author_Only_Copy config setting
|
| bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
|
||||||
* bookswithoutborders.bypassbookprice - Allows player to ignore Price_to_create_book config setting
|
| bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
|
||||||
* bookswithoutborders-setbookprice - Allows player to set the cost of creating a book
|
| bookswithoutborders.bypassauthoronlycopy | Allows player to ignore Author_Only_Copy config setting |
|
||||||
|
| bookswithoutborders.bypassauthoronlyunsign | Allows player to ignore Author_Only_Unsign config setting |
|
||||||
|
| bookswithoutborders.bypassauthoronlysave | Allows player to ignore Author_Only_Save config setting |
|
||||||
|
| bookswithoutborders.bypassbookprice | Allows player to ignore Price_to_create_book config setting |
|
||||||
|
| 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.clear | Allows player to clear the contents of the held writable book |
|
||||||
|
|
||||||
### Signs
|
### Signs
|
||||||
|
|
||||||
@ -117,3 +118,22 @@ fourth line can be empty or contain "dna" for dna-based encryption.
|
|||||||
#### Decrypt sign
|
#### Decrypt sign
|
||||||
|
|
||||||
The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third line must contain the decryption key
|
The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third line must contain the decryption key
|
||||||
|
|
||||||
|
### Configuration options:
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| Save_Books_in_Yaml_Format | Whether to use YAML for saved books instead of just storing them as text |
|
||||||
|
| Max_Number_of_Duplicates | The maximum number of duplicates of a saved book allowed |
|
||||||
|
| Author_Separator | The separator used to separate the book title and the book author |
|
||||||
|
| Lore_line_separator | The separator used to denote a new line in the book/item lore |
|
||||||
|
| Books_for_new_players | A list of books given to new players the first time they join the server |
|
||||||
|
| Message_for_new_players | An optional message displayed to new players the first time they join the server |
|
||||||
|
| Price_to_create_book.Item_type | The item type used as currency for copying books. Use "Economy" to use money instead of items |
|
||||||
|
| Price_to_create_book.Required_quantity | The quantity of currency required to pay for each book produced |
|
||||||
|
| Admin_Auto_Decrypt | Whether any admin can decrypt any book regardless of the group it was encrypted for |
|
||||||
|
| Author_Only_Copy | Whether to only allow the author of a book to create copies |
|
||||||
|
| 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 |
|
||||||
|
| 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. |
|
6
pom.xml
6
pom.xml
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>net.knarcraft</groupId>
|
<groupId>net.knarcraft</groupId>
|
||||||
<artifactId>BooksWithoutBorders</artifactId>
|
<artifactId>BooksWithoutBorders</artifactId>
|
||||||
<version>1.2.2</version>
|
<version>1.3.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.18.1-R0.1-SNAPSHOT</version>
|
<version>1.19.2-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
<groupId>org.jetbrains</groupId>
|
<groupId>org.jetbrains</groupId>
|
||||||
<artifactId>annotations</artifactId>
|
<artifactId>annotations</artifactId>
|
||||||
<version>23.0.0</version>
|
<version>23.0.0</version>
|
||||||
<scope>compile</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders;
|
package net.knarcraft.bookswithoutborders;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.command.CommandClear;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandCopy;
|
import net.knarcraft.bookswithoutborders.command.CommandCopy;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
|
import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandDelete;
|
import net.knarcraft.bookswithoutborders.command.CommandDelete;
|
||||||
@ -17,6 +18,7 @@ import net.knarcraft.bookswithoutborders.command.CommandSave;
|
|||||||
import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
|
import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
|
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
|
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
|
||||||
|
import net.knarcraft.bookswithoutborders.command.CommandSetGeneration;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetLore;
|
import net.knarcraft.bookswithoutborders.command.CommandSetLore;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
||||||
@ -31,6 +33,7 @@ import org.bukkit.command.CommandSender;
|
|||||||
import org.bukkit.command.ConsoleCommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemFactory;
|
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;
|
||||||
@ -41,6 +44,7 @@ import java.util.ArrayList;
|
|||||||
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.UUID;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||||
@ -53,7 +57,7 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
|
|||||||
public class BooksWithoutBorders extends JavaPlugin {
|
public class BooksWithoutBorders extends JavaPlugin {
|
||||||
|
|
||||||
private static ItemFactory itemFactory;
|
private static ItemFactory itemFactory;
|
||||||
private static Map<String, List<String>> playerBooksList;
|
private static Map<UUID, List<String>> playerBooksList;
|
||||||
private static List<String> publicBooksList;
|
private static List<String> publicBooksList;
|
||||||
private static BooksWithoutBorders booksWithoutBorders;
|
private static BooksWithoutBorders booksWithoutBorders;
|
||||||
private static ConsoleCommandSender consoleSender;
|
private static ConsoleCommandSender consoleSender;
|
||||||
@ -77,13 +81,15 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
public static List<String> getAvailableBooks(CommandSender sender, boolean getPublic) {
|
public static List<String> getAvailableBooks(CommandSender sender, boolean getPublic) {
|
||||||
if (getPublic) {
|
if (getPublic) {
|
||||||
return new ArrayList<>(publicBooksList);
|
return new ArrayList<>(publicBooksList);
|
||||||
} else {
|
} else if (sender instanceof Player player) {
|
||||||
String senderName = sender.getName();
|
UUID playerUUID = player.getUniqueId();
|
||||||
if (!playerBooksList.containsKey(senderName)) {
|
if (!playerBooksList.containsKey(playerUUID)) {
|
||||||
List<String> newFiles = FileHelper.listFiles(sender, false);
|
List<String> newFiles = FileHelper.listFiles(sender, false);
|
||||||
playerBooksList.put(senderName, newFiles);
|
playerBooksList.put(playerUUID, newFiles);
|
||||||
}
|
}
|
||||||
return playerBooksList.get(senderName);
|
return playerBooksList.get(playerUUID);
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,8 +103,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
List<String> newFiles = FileHelper.listFiles(sender, updatePublic);
|
List<String> newFiles = FileHelper.listFiles(sender, updatePublic);
|
||||||
if (updatePublic) {
|
if (updatePublic) {
|
||||||
publicBooksList = newFiles;
|
publicBooksList = newFiles;
|
||||||
} else {
|
} else if (sender instanceof Player player) {
|
||||||
playerBooksList.put(sender.getName(), newFiles);
|
playerBooksList.put(player.getUniqueId(), newFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +173,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders());
|
registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders());
|
||||||
registerCommand("reload", new CommandReload());
|
registerCommand("reload", new CommandReload());
|
||||||
registerCommand("formatBook", new CommandFormat());
|
registerCommand("formatBook", new CommandFormat());
|
||||||
|
registerCommand("setBookGeneration", new CommandSetGeneration());
|
||||||
|
registerCommand("clearBook", new CommandClear());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +84,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
showCommandInfo("saveBook", sender);
|
showCommandInfo("saveBook", sender);
|
||||||
showCommandInfo("savePublicBook", sender);
|
showCommandInfo("savePublicBook", sender);
|
||||||
showCommandInfo("setAuthor", sender);
|
showCommandInfo("setAuthor", sender);
|
||||||
|
showCommandInfo("setBookGeneration", sender);
|
||||||
showCommandInfo("setBookPrice", sender);
|
showCommandInfo("setBookPrice", sender);
|
||||||
showCommandInfo("setLore", sender);
|
showCommandInfo("setLore", sender);
|
||||||
showCommandInfo("setTitle", sender);
|
showCommandInfo("setTitle", sender);
|
||||||
@ -104,6 +105,9 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
String commandInfo = "\n" + getCommandColor() + pluginCommand.getUsage().replace("<command>",
|
String commandInfo = "\n" + getCommandColor() + pluginCommand.getUsage().replace("<command>",
|
||||||
pluginCommand.getName()) + ": " + getSuccessColor() + pluginCommand.getDescription();
|
pluginCommand.getName()) + ": " + getSuccessColor() + pluginCommand.getDescription();
|
||||||
if (sender.hasPermission("bookswithoutborders.admin")) {
|
if (sender.hasPermission("bookswithoutborders.admin")) {
|
||||||
|
if (permission == null) {
|
||||||
|
permission = "None";
|
||||||
|
}
|
||||||
commandInfo += getCommandColor() + " {" + permission + "}";
|
commandInfo += getCommandColor() + " {" + permission + "}";
|
||||||
}
|
}
|
||||||
sender.sendMessage(commandInfo);
|
sender.sendMessage(commandInfo);
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
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.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command executor for the clear command
|
||||||
|
*/
|
||||||
|
public class CommandClear implements TabExecutor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] args) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InventoryHelper.notHoldingOneWritableBookCheck(player, "You must be holding a writable book to " +
|
||||||
|
"clear it!", "You cannot clear two books at once!")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clear the player's held book
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, false);
|
||||||
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
if (bookMeta == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Unable to get metadata for the held book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bookMeta.setPages("");
|
||||||
|
bookMeta.setAuthor(null);
|
||||||
|
bookMeta.setGeneration(null);
|
||||||
|
bookMeta.setTitle(null);
|
||||||
|
heldBook.setItemMeta(bookMeta);
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(sender, "Book cleared!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] args) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,10 +2,11 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.TabExecutor;
|
import org.bukkit.command.TabExecutor;
|
||||||
@ -40,20 +41,43 @@ public class CommandCopy implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
int copies = Integer.parseInt(args[0]);
|
int copies = Integer.parseInt(args[0]);
|
||||||
if (copies > 0) {
|
if (copies <= 0) {
|
||||||
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
throw new NumberFormatException("Number of copies must be larger than 0");
|
||||||
if (!isAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
}
|
||||||
|
return performCopy(copies, player, heldBook);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
/**
|
||||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
* Performs the actual copying
|
||||||
EconomyHelper.cannotPayForBookPrinting(player, copies)) {
|
*
|
||||||
|
* @param copies <p>The number of copies to be made</p>
|
||||||
|
* @param player <p>The player requesting the copies</p>
|
||||||
|
* @param heldBook <p>The book to be copied</p>
|
||||||
|
* @return <p>True if the copying was successful</p>
|
||||||
|
*/
|
||||||
|
private boolean performCopy(int copies, Player player, ItemStack heldBook) {
|
||||||
|
//Make sure the player owns the book if authorOnlyCopy is enabled
|
||||||
|
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() &&
|
||||||
|
!player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
||||||
|
if (BookHelper.isNotAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) {
|
||||||
|
return copyNextGenerationBook(bookMeta, player, copies);
|
||||||
|
} else {
|
||||||
|
//Make sure the player can pay for the copying
|
||||||
|
if (paymentUnSuccessful(player, copies)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,33 +85,61 @@ public class CommandCopy implements TabExecutor {
|
|||||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
}
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given player is the author of a given book
|
* Tries to take payment from a player
|
||||||
*
|
*
|
||||||
* @param player <p>The player to check</p>
|
* @param player <p>The player to take the payment from</p>
|
||||||
* @param book <p>The book to check</p>
|
* @param copies <p>The number of copies to create for the player</p>
|
||||||
* @return <p>True if the player is the book's author</p>
|
* @return <p>True if the payment failed</p>
|
||||||
*/
|
*/
|
||||||
private boolean isAuthor(Player player, BookMeta book) {
|
private boolean paymentUnSuccessful(Player player, int copies) {
|
||||||
String author = book.getAuthor();
|
return BooksWithoutBordersConfig.booksHavePrice() &&
|
||||||
String playerName = InputCleaningHelper.cleanString(player.getName());
|
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||||
if (author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author))) {
|
EconomyHelper.cannotPayForBookPrinting(player, copies);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "You must be the author of this book to use this command!");
|
/**
|
||||||
|
* Copies a book with the next generation relative to the input book
|
||||||
|
*
|
||||||
|
* @param bookMeta <p>The book to copy</p>
|
||||||
|
* @param player <p>The player copying the book</p>
|
||||||
|
* @param copies <p>The number of copies requested</p>
|
||||||
|
* @return <p>True if the book was successfully copied</p>
|
||||||
|
*/
|
||||||
|
private boolean copyNextGenerationBook(BookMeta bookMeta, Player player, int copies) {
|
||||||
|
//Copy the vanilla behavior of refusing copying any further
|
||||||
|
if (bookMeta.getGeneration() == BookMeta.Generation.COPY_OF_COPY ||
|
||||||
|
bookMeta.getGeneration() == BookMeta.Generation.TATTERED) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You cannot copy this book any further. " +
|
||||||
|
"You must have the original or a direct copy.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Make sure the player can fit the book in their inventory
|
||||||
|
int nextAvailableSlot = player.getInventory().firstEmpty();
|
||||||
|
if (nextAvailableSlot == -1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You need an available slot in your inventory.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Make sure the player can pay for the copying
|
||||||
|
if (paymentUnSuccessful(player, copies)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ItemStack itemStack = new ItemStack(Material.WRITTEN_BOOK);
|
||||||
|
itemStack.setItemMeta(bookMeta);
|
||||||
|
//Increase the generation of the book
|
||||||
|
BookHelper.increaseGeneration(itemStack);
|
||||||
|
itemStack.setAmount(copies);
|
||||||
|
|
||||||
|
player.getInventory().addItem(itemStack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] args) {
|
||||||
int argumentCount = args.length;
|
int argumentCount = args.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 20), args[0]);
|
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 20), args[0]);
|
||||||
|
@ -2,6 +2,7 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@ -47,12 +48,6 @@ public class CommandDecrypt implements TabExecutor {
|
|||||||
//Warning: admin decrypt only allows decrypting files created by the same player. Not sure if intended
|
//Warning: admin decrypt only allows decrypting files created by the same player. Not sure if intended
|
||||||
if (args.length == 0 && BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
if (args.length == 0 && BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||||
String fileName;
|
|
||||||
if (bookMetadata.hasTitle()) {
|
|
||||||
fileName = bookMetadata.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
|
||||||
} else {
|
|
||||||
fileName = "Untitled," + player.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
File encryptedDirectory = new File(path);
|
File encryptedDirectory = new File(path);
|
||||||
String[] encryptedFiles = encryptedDirectory.list();
|
String[] encryptedFiles = encryptedDirectory.list();
|
||||||
@ -64,7 +59,7 @@ public class CommandDecrypt implements TabExecutor {
|
|||||||
//Get the "encryption key" from the filename
|
//Get the "encryption key" from the filename
|
||||||
String key = "";
|
String key = "";
|
||||||
for (String encryptedFile : encryptedFiles) {
|
for (String encryptedFile : encryptedFiles) {
|
||||||
if (encryptedFile.contains(fileName)) {
|
if (encryptedFile.contains(BookHelper.getBookFile(bookMetadata, player, true))) {
|
||||||
key = encryptedFile.substring(encryptedFile.indexOf("[") + 1, encryptedFile.indexOf("]"));
|
key = encryptedFile.substring(encryptedFile.indexOf("[") + 1, encryptedFile.indexOf("]"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package net.knarcraft.bookswithoutborders.command;
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
|
||||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
@ -14,9 +15,6 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the delete command
|
* Command executor for the delete command
|
||||||
*/
|
*/
|
||||||
@ -82,13 +80,9 @@ public class CommandDelete implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Get the file to be deleted
|
//Get the file to be deleted
|
||||||
File file;
|
String bookDirectory = BookHelper.getBookDirectoryPathString(
|
||||||
if (isPublic) {
|
isPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||||
file = FileHelper.getBookFile(getBookFolder() + fileName);
|
File file = FileHelper.getBookFile(bookDirectory + fileName);
|
||||||
} else {
|
|
||||||
file = FileHelper.getBookFile(getBookFolder() +
|
|
||||||
InputCleaningHelper.cleanString(sender.getName()) + getSlash() + fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send message if no such file could be found
|
//Send message if no such file could be found
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
@ -69,6 +69,20 @@ public class CommandGive implements TabExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Try and find the target player
|
||||||
|
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
||||||
|
if (receivingPlayer == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make sure the receiver is able to fit the book
|
||||||
|
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory" +
|
||||||
|
" to receive books!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//Load books available to the player
|
//Load books available to the player
|
||||||
try {
|
try {
|
||||||
Integer.parseInt(bookIdentifier);
|
Integer.parseInt(bookIdentifier);
|
||||||
@ -76,29 +90,8 @@ public class CommandGive implements TabExecutor {
|
|||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
|
||||||
if (receivingPlayer == null) {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory to receive books!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
|
||||||
try {
|
try {
|
||||||
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, folder, copies);
|
||||||
if (newBook != null) {
|
|
||||||
receivingPlayer.getInventory().addItem(newBook);
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
||||||
return false;
|
return false;
|
||||||
@ -152,4 +145,31 @@ public class CommandGive implements TabExecutor {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a book and gives it to the correct player
|
||||||
|
*
|
||||||
|
* @param bookIdentifier <p>The file name specified by the user</p>
|
||||||
|
* @param sender <p>The player trying to give the book</p>
|
||||||
|
* @param receivingPlayer <p>The player which is the receiver of the book</p>
|
||||||
|
* @param isSigned <p>The value given for if the given book should be signed or not</p>
|
||||||
|
* @param folder <p>The folder containing the book to load</p>
|
||||||
|
* @param copies <p>The number of copies the player wants to give</p>
|
||||||
|
* @return <p>True if the book was successfully given</p>
|
||||||
|
*/
|
||||||
|
private boolean loadAndGiveBook(String bookIdentifier, CommandSender sender, Player receivingPlayer,
|
||||||
|
String isSigned, String folder, String copies) throws NumberFormatException {
|
||||||
|
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
||||||
|
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
||||||
|
if (newBook != null) {
|
||||||
|
//NOTE: As this method bypasses cost, it should also bypass the generation change
|
||||||
|
receivingPlayer.getInventory().addItem(newBook);
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
|
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
@ -19,12 +21,9 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getTitleAuthorSeparator;
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the save command
|
* Command executor for the save command
|
||||||
@ -51,7 +50,7 @@ public class CommandSave implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
|
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
|
||||||
if (holdingSlot != ItemSlot.NONE) {
|
if (holdingSlot != null && holdingSlot != ItemSlot.NONE) {
|
||||||
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
|
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
|
||||||
boolean duplicate = args.length == 1 && Boolean.parseBoolean(args[0]);
|
boolean duplicate = args.length == 1 && Boolean.parseBoolean(args[0]);
|
||||||
saveBook(player, holdingItem, duplicate, savePublic);
|
saveBook(player, holdingItem, duplicate, savePublic);
|
||||||
@ -77,32 +76,30 @@ public class CommandSave implements TabExecutor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String savePath;
|
//Only allow saving of own books if enabled
|
||||||
if (saveToPublicFolder) {
|
if (BooksWithoutBordersConfig.getAuthorOnlySave() && !saveToPublicFolder &&
|
||||||
savePath = getBookFolder();
|
(!player.hasPermission("bookswithoutborders.bypassAuthorOnlySave") &&
|
||||||
} else {
|
BookHelper.isNotAuthor(player, book))) {
|
||||||
savePath = getBookFolder() + cleanString(player.getName()) + getSlash();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String savePath = BookHelper.getBookDirectoryPathString(
|
||||||
|
saveToPublicFolder ? BookDirectory.PUBLIC : BookDirectory.PLAYER, player);
|
||||||
|
|
||||||
//Generate book filename
|
//Generate book filename
|
||||||
String fileName;
|
String fileName = BookHelper.getBookFile(book, player, saveToPublicFolder);
|
||||||
if (!book.hasTitle()) {
|
|
||||||
fileName = "Untitled," + player.getName();
|
|
||||||
} else {
|
|
||||||
fileName = book.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + book.getAuthor();
|
|
||||||
}
|
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
//Make sure the used folders exist
|
//Make sure the used folders exist
|
||||||
File file = new File(savePath);
|
File file = new File(savePath);
|
||||||
if (!file.exists() && !file.mkdir()) {
|
if (!file.exists() && !file.mkdir()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||||
|
" a server admin!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File[] foundFiles = file.listFiles();
|
File[] foundFiles = file.listFiles();
|
||||||
if (foundFiles == null) {
|
if (foundFiles == null) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||||
|
" a server admin!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,21 +110,24 @@ public class CommandSave implements TabExecutor {
|
|||||||
if (foundDuplicates > 0) {
|
if (foundDuplicates > 0) {
|
||||||
//TODO: Decide if this makes sense or needs to be changed
|
//TODO: Decide if this makes sense or needs to be changed
|
||||||
//Skip duplicate book
|
//Skip duplicate book
|
||||||
if (!fileName.contains("Untitled") && !overwrite) {
|
if (!fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Book is already saved!");
|
BooksWithoutBorders.sendErrorMessage(player, "Book is already saved!");
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " + getErrorColor() + "to overwrite!");
|
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " +
|
||||||
|
getErrorColor() + "to overwrite!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Skip if duplicate limit is reached
|
//Skip if duplicate limit is reached
|
||||||
if (foundDuplicates > BooksWithoutBordersConfig.getBookDuplicateLimit()) {
|
if (foundDuplicates > BooksWithoutBordersConfig.getBookDuplicateLimit()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName + " duplicates reached!");
|
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName +
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " + getErrorColor() + "to overwrite!");
|
" duplicates reached!");
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " +
|
||||||
|
getErrorColor() + "to overwrite!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Alter duplicated filename
|
//Alter duplicated filename
|
||||||
if (fileName.contains("Untitled") && !overwrite) {
|
if (fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||||
fileName = "(" + foundDuplicates + ")" + fileName;
|
fileName = "(" + foundDuplicates + ")" + fileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,7 +149,8 @@ public class CommandSave implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] args) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
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.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command executor for the set generation command
|
||||||
|
*/
|
||||||
|
public class CommandSetGeneration implements TabExecutor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] args) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InventoryHelper.notHoldingOneWrittenBookCheck(player, "You must be holding a written book to" +
|
||||||
|
" change its generation!", "You cannot change the generation of two books at once!")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length < 1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You must specify the new generation for your book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BookMeta.Generation generation;
|
||||||
|
try {
|
||||||
|
generation = BookMeta.Generation.valueOf(args[0]);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Invalid book generation specified!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
if (bookMeta != null) {
|
||||||
|
bookMeta.setGeneration(generation);
|
||||||
|
heldBook.setItemMeta(bookMeta);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get book metadata!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] args) {
|
||||||
|
if (args.length == 1) {
|
||||||
|
List<String> generations = new ArrayList<>();
|
||||||
|
for (BookMeta.Generation generation : BookMeta.Generation.values()) {
|
||||||
|
generations.add(generation.name());
|
||||||
|
}
|
||||||
|
return generations;
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package net.knarcraft.bookswithoutborders.command;
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@ -14,6 +16,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the unsign command
|
* Command executor for the unsign command
|
||||||
@ -47,9 +50,18 @@ public class CommandUnSign implements TabExecutor {
|
|||||||
public void unSignHeldBook(Player player, boolean mainHand) {
|
public void unSignHeldBook(Player player, boolean mainHand) {
|
||||||
//Get the old book
|
//Get the old book
|
||||||
BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand);
|
||||||
|
|
||||||
|
//Only allow the owner to un-sign the book
|
||||||
|
if (BooksWithoutBordersConfig.getAuthorOnlyUnsign() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) {
|
||||||
|
if (BookHelper.isNotAuthor(player, Objects.requireNonNull(oldBook))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//UnSign the book
|
//UnSign the book
|
||||||
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK);
|
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK);
|
||||||
|
newBook.setAmount(heldBook.getAmount());
|
||||||
newBook.setItemMeta(oldBook);
|
newBook.setItemMeta(oldBook);
|
||||||
|
|
||||||
InventoryHelper.replaceHeldItem(player, newBook, mainHand);
|
InventoryHelper.replaceHeldItem(player, newBook, mainHand);
|
||||||
|
@ -34,9 +34,12 @@ public class BooksWithoutBordersConfig {
|
|||||||
private static Material bookPriceType = null;
|
private static Material bookPriceType = null;
|
||||||
private static double bookPriceQuantity;
|
private static double bookPriceQuantity;
|
||||||
private static boolean authorOnlyCopy;
|
private static boolean authorOnlyCopy;
|
||||||
|
private static boolean authorOnlyUnsign;
|
||||||
|
private static boolean authorOnlySave;
|
||||||
private static boolean useYml;
|
private static boolean useYml;
|
||||||
private static boolean adminDecrypt;
|
private static boolean adminDecrypt;
|
||||||
private static boolean formatBooks;
|
private static boolean formatBooks;
|
||||||
|
private static boolean changeGenerationOnCopy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the books without borders settings class
|
* Initializes the books without borders settings class
|
||||||
@ -106,6 +109,24 @@ public class BooksWithoutBordersConfig {
|
|||||||
return authorOnlyCopy;
|
return authorOnlyCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether only the author of a book should be able to unsign it
|
||||||
|
*
|
||||||
|
* @return <p>Whether only the book author can unsign it</p>
|
||||||
|
*/
|
||||||
|
public static boolean getAuthorOnlyUnsign() {
|
||||||
|
return authorOnlyUnsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether a player can only save their own books with /savebook
|
||||||
|
*
|
||||||
|
* @return <p>Whether a player can only save their own books</p>
|
||||||
|
*/
|
||||||
|
public static boolean getAuthorOnlySave() {
|
||||||
|
return authorOnlySave;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets whether to use YML, not TXT, for saving books
|
* Gets whether to use YML, not TXT, for saving books
|
||||||
*
|
*
|
||||||
@ -184,6 +205,15 @@ public class BooksWithoutBordersConfig {
|
|||||||
return bookDuplicateLimit;
|
return bookDuplicateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether books should change their generation during copy
|
||||||
|
*
|
||||||
|
* @return <p>True if books should change their generation</p>
|
||||||
|
*/
|
||||||
|
public static boolean changeGenerationOnCopy() {
|
||||||
|
return changeGenerationOnCopy;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the separator used to split book title from book author
|
* Gets the separator used to split book title from book author
|
||||||
*
|
*
|
||||||
@ -257,10 +287,14 @@ public class BooksWithoutBordersConfig {
|
|||||||
config.set(ConfigOption.PRICE_QUANTITY.getConfigNode(), bookPriceQuantity);
|
config.set(ConfigOption.PRICE_QUANTITY.getConfigNode(), bookPriceQuantity);
|
||||||
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
||||||
config.set(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(), authorOnlyCopy);
|
config.set(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(), authorOnlyCopy);
|
||||||
|
config.set(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(), authorOnlyUnsign);
|
||||||
|
config.set(ConfigOption.AUTHOR_ONLY_SAVE.getConfigNode(), authorOnlySave);
|
||||||
|
config.set(ConfigOption.CHANGE_GENERATION_ON_COPY.getConfigNode(), changeGenerationOnCopy);
|
||||||
|
|
||||||
//Handles old book and quill settings
|
//Handles old book and quill settings
|
||||||
if (config.contains("Options.Require_book_and_quill_to_create_book")) {
|
if (config.contains("Options.Require_book_and_quill_to_create_book")) {
|
||||||
sendSuccessMessage(consoleSender, "[BooksWithoutBorders] Found old config setting \"Require_book_and_quill_to_create_book\"");
|
sendSuccessMessage(consoleSender, "[BooksWithoutBorders] Found old config setting " +
|
||||||
|
"\"Require_book_and_quill_to_create_book\"");
|
||||||
sendSuccessMessage(consoleSender, "Updating to \"Price_to_create_book\" settings");
|
sendSuccessMessage(consoleSender, "Updating to \"Price_to_create_book\" settings");
|
||||||
|
|
||||||
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
||||||
@ -284,32 +318,27 @@ public class BooksWithoutBordersConfig {
|
|||||||
BooksWithoutBorders.getInstance().reloadConfig();
|
BooksWithoutBorders.getInstance().reloadConfig();
|
||||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||||
try {
|
try {
|
||||||
useYml = config.getBoolean(ConfigOption.USE_YAML.getConfigNode(),
|
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
||||||
(Boolean) ConfigOption.USE_YAML.getDefaultValue());
|
bookDuplicateLimit = getInt(config, ConfigOption.MAX_DUPLICATES);
|
||||||
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
||||||
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
||||||
titleAuthorSeparator = config.getString(ConfigOption.TITLE_AUTHOR_SEPARATOR.getConfigNode(),
|
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
||||||
(String) ConfigOption.TITLE_AUTHOR_SEPARATOR.getDefaultValue());
|
authorOnlyCopy = getBoolean(config, ConfigOption.AUTHOR_ONLY_COPY);
|
||||||
loreSeparator = config.getString(ConfigOption.LORE_LINE_SEPARATOR.getConfigNode(),
|
authorOnlyUnsign = getBoolean(config, ConfigOption.AUTHOR_ONLY_UNSIGN);
|
||||||
(String) ConfigOption.LORE_LINE_SEPARATOR.getDefaultValue());
|
authorOnlySave = getBoolean(config, ConfigOption.AUTHOR_ONLY_SAVE);
|
||||||
adminDecrypt = config.getBoolean(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(),
|
|
||||||
(Boolean) ConfigOption.ADMIN_AUTO_DECRYPT.getDefaultValue());
|
|
||||||
authorOnlyCopy = config.getBoolean(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(),
|
|
||||||
(Boolean) ConfigOption.AUTHOR_ONLY_COPY.getDefaultValue());
|
|
||||||
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
||||||
welcomeMessage = config.getString(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(),
|
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
||||||
(String) ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getDefaultValue());
|
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
||||||
formatBooks = config.getBoolean(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(),
|
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||||
(Boolean) ConfigOption.FORMAT_AFTER_SIGNING.getDefaultValue());
|
|
||||||
|
|
||||||
//Convert string into material
|
//Convert string into material
|
||||||
String paymentMaterial = config.getString(ConfigOption.PRICE_ITEM_TYPE.getConfigNode(),
|
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
||||||
(String) ConfigOption.PRICE_ITEM_TYPE.getDefaultValue());
|
|
||||||
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
||||||
if (EconomyHelper.setupEconomy()) {
|
if (EconomyHelper.setupEconomy()) {
|
||||||
bookPriceType = Material.AIR;
|
bookPriceType = Material.AIR;
|
||||||
} else {
|
} else {
|
||||||
sendErrorMessage(consoleSender, "BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
sendErrorMessage(consoleSender,
|
||||||
|
"BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||||
bookPriceType = null;
|
bookPriceType = null;
|
||||||
}
|
}
|
||||||
} else if (!paymentMaterial.trim().isEmpty()) {
|
} else if (!paymentMaterial.trim().isEmpty()) {
|
||||||
@ -318,8 +347,7 @@ public class BooksWithoutBordersConfig {
|
|||||||
bookPriceType = material;
|
bookPriceType = material;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bookPriceQuantity = config.getDouble(ConfigOption.PRICE_QUANTITY.getConfigNode(),
|
bookPriceQuantity = getDouble(config, ConfigOption.PRICE_QUANTITY);
|
||||||
(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);
|
||||||
@ -338,4 +366,48 @@ 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
|
||||||
|
*
|
||||||
|
* @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 String getString(Configuration config, ConfigOption configOption) {
|
||||||
|
return config.getString(configOption.getConfigNode(), (String) configOption.getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the boolean 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 boolean getBoolean(Configuration config, ConfigOption configOption) {
|
||||||
|
return config.getBoolean(configOption.getConfigNode(), (Boolean) configOption.getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,21 @@ public enum ConfigOption {
|
|||||||
*/
|
*/
|
||||||
AUTHOR_ONLY_COPY("Options.Author_Only_Copy", false),
|
AUTHOR_ONLY_COPY("Options.Author_Only_Copy", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether only the book author should be able to unsign a book
|
||||||
|
*/
|
||||||
|
AUTHOR_ONLY_UNSIGN("Options.Author_Only_Unsign", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a player can only save their own books with /savebook
|
||||||
|
*/
|
||||||
|
AUTHOR_ONLY_SAVE("Options.Author_Only_Save", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to turn Original into Copy when copying books
|
||||||
|
*/
|
||||||
|
CHANGE_GENERATION_ON_COPY("Options.Change_Generation_On_Copy", false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to automatically format every signed book
|
* Whether to automatically format every signed book
|
||||||
*/
|
*/
|
||||||
|
@ -2,6 +2,8 @@ package net.knarcraft.bookswithoutborders.listener;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
@ -17,16 +19,13 @@ import org.bukkit.inventory.meta.BookMeta;
|
|||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.logging.Level;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for listening to player-related events such as joining or holding a book
|
* A listener for listening to player-related events such as joining or holding a book
|
||||||
*/
|
*/
|
||||||
public class PlayerEventListener implements Listener {
|
public class PlayerEventListener implements Listener {
|
||||||
|
|
||||||
private final String slash = getSlash();
|
|
||||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -58,6 +57,16 @@ public class PlayerEventListener implements Listener {
|
|||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
//If a book directory exists with this player's name, move it to this player's UUID
|
||||||
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
|
File file = new File(bookFolder + InputCleaningHelper.cleanString(player.getName()));
|
||||||
|
if (file.exists()) {
|
||||||
|
if (!file.renameTo(new File(bookFolder + player.getUniqueId()))) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.WARNING, "Unable to migrate player book " +
|
||||||
|
"directory for player " + player.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Handle new players
|
//Handle new players
|
||||||
if (!player.hasPlayedBefore()) {
|
if (!player.hasPlayedBefore()) {
|
||||||
boolean sendMessage = true;
|
boolean sendMessage = true;
|
||||||
@ -151,13 +160,14 @@ public class PlayerEventListener implements Listener {
|
|||||||
fileName = oldBook.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + oldBook.getAuthor();
|
fileName = oldBook.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + oldBook.getAuthor();
|
||||||
}
|
}
|
||||||
|
|
||||||
String cleanPlayerName = InputCleaningHelper.cleanString(player.getName());
|
String playerFolderPath = BookHelper.getBookDirectoryPathString(BookDirectory.PLAYER, player);
|
||||||
|
String publicFolderPath = BookHelper.getBookDirectoryPathString(BookDirectory.PUBLIC, player);
|
||||||
|
|
||||||
String[] possiblePaths = new String[]{
|
String[] possiblePaths = new String[]{
|
||||||
getBookFolder() + fileName + ".yml",
|
publicFolderPath + fileName + ".yml",
|
||||||
getBookFolder() + fileName + ".txt",
|
publicFolderPath + fileName + ".txt",
|
||||||
getBookFolder() + cleanPlayerName + slash + fileName + ".yml",
|
playerFolderPath + fileName + ".yml",
|
||||||
getBookFolder() + cleanPlayerName + slash + fileName + ".txt"
|
playerFolderPath + fileName + ".txt"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String path : possiblePaths) {
|
for (String path : possiblePaths) {
|
||||||
|
@ -12,6 +12,7 @@ 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.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
@ -89,10 +90,14 @@ public class SignEventListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ItemStack heldItem = playerInventory.getItem(hand);
|
ItemStack heldItem = playerInventory.getItem(hand);
|
||||||
|
if (heldItem == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Material heldItemType = heldItem.getType();
|
Material heldItemType = heldItem.getType();
|
||||||
|
|
||||||
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
|
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
|
||||||
Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
|
Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
|
||||||
|
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)) {
|
||||||
@ -182,7 +187,8 @@ public class SignEventListener implements Listener {
|
|||||||
*/
|
*/
|
||||||
private void generateGiveSign(SignChangeEvent event, String[] lines, Player player) {
|
private void generateGiveSign(SignChangeEvent event, String[] lines, Player player) {
|
||||||
if (lines[2].length() > 13 || lines[3].length() > 13) {
|
if (lines[2].length() > 13 || lines[3].length() > 13) {
|
||||||
player.sendMessage(ChatColor.RED + "[Give] signs' 3rd and 4th lines must be 13 characters or less!");
|
BooksWithoutBorders.sendErrorMessage(player,
|
||||||
|
"[Give] signs' 3rd and 4th lines must be 13 characters or less!");
|
||||||
markGiveSignValidity(event, false);
|
markGiveSignValidity(event, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -262,7 +268,7 @@ public class SignEventListener implements Listener {
|
|||||||
|
|
||||||
player.getInventory().setItem(hand, newBook);
|
player.getInventory().setItem(hand, newBook);
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
player.sendMessage(ChatColor.GREEN + "Book auto-decrypted!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Book auto-decrypted!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -313,9 +319,9 @@ public class SignEventListener implements Listener {
|
|||||||
|
|
||||||
if (newBook != null) {
|
if (newBook != null) {
|
||||||
player.getInventory().addItem(newBook);
|
player.getInventory().addItem(newBook);
|
||||||
player.sendMessage(ChatColor.GREEN + "Received book!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Received book!");
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(ChatColor.RED + "Book failed to load!");
|
BooksWithoutBorders.sendErrorMessage(player, "Book failed to load!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,170 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for getting abstract book information
|
||||||
|
*/
|
||||||
|
public final class BookHelper {
|
||||||
|
|
||||||
|
private BookHelper() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the author of a book from UUID if necessary
|
||||||
|
*
|
||||||
|
* @param author <p>The author string</p>
|
||||||
|
* @return <p>The author string, converted if it was a UUID</p>
|
||||||
|
*/
|
||||||
|
public static String authorFromUUID(String author) {
|
||||||
|
try {
|
||||||
|
UUID authorID = UUID.fromString(author);
|
||||||
|
Player player = Bukkit.getPlayer(authorID);
|
||||||
|
if (player != null) {
|
||||||
|
author = player.getName();
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file path of the selected book directory
|
||||||
|
*
|
||||||
|
* @param bookDirectory <p>The book directory to get (ENCRYPTED is not supported here)</p>
|
||||||
|
* @param sender <p>The command sender trying to get the directory</p>
|
||||||
|
* @return <p>The path of the directory, or null if not possible to get</p>
|
||||||
|
*/
|
||||||
|
public static File getBookDirectoryPath(BookDirectory bookDirectory, CommandSender sender) {
|
||||||
|
String bookFolderString = getBookDirectoryPathString(bookDirectory, sender);
|
||||||
|
if (bookFolderString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new File(bookFolderString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string path of the selected book directory
|
||||||
|
*
|
||||||
|
* @param bookDirectory <p>The book directory to get (ENCRYPTED is not supported here)</p>
|
||||||
|
* @param sender <p>The command sender trying to get the directory</p>
|
||||||
|
* @return <p>The path of the directory, or null if not possible to get</p>
|
||||||
|
*/
|
||||||
|
public static String getBookDirectoryPathString(BookDirectory bookDirectory, CommandSender sender) {
|
||||||
|
String folder = null;
|
||||||
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
|
if (bookDirectory == BookDirectory.PUBLIC) {
|
||||||
|
folder = bookFolder;
|
||||||
|
} else if (bookDirectory == BookDirectory.PLAYER && sender instanceof Player player) {
|
||||||
|
folder = bookFolder + player.getUniqueId() + getSlash();
|
||||||
|
}
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the generation of the given book, if necessary
|
||||||
|
*
|
||||||
|
* @param bookItem <p>The book item to increase the generation of</p>
|
||||||
|
*/
|
||||||
|
public static void increaseGeneration(ItemStack bookItem) {
|
||||||
|
BookMeta bookMeta = (BookMeta) bookItem.getItemMeta();
|
||||||
|
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) {
|
||||||
|
bookMeta.setGeneration(BookHelper.getNextGeneration(bookMeta.getGeneration()));
|
||||||
|
bookItem.setItemMeta(bookMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next generation of the given book
|
||||||
|
*
|
||||||
|
* <p>If an original book is given, this will yield a copy of the original. If a copy of original is given, this
|
||||||
|
* will yield a copy of a copy. In all other cases, the generation will stay the same</p>
|
||||||
|
*
|
||||||
|
* @param currentGeneration <p>The current generation of the book</p>
|
||||||
|
* @return <p>The next generation of the book</p>
|
||||||
|
*/
|
||||||
|
public static BookMeta.Generation getNextGeneration(BookMeta.Generation currentGeneration) {
|
||||||
|
if (currentGeneration == null) {
|
||||||
|
return BookMeta.Generation.COPY_OF_ORIGINAL;
|
||||||
|
}
|
||||||
|
return switch (currentGeneration) {
|
||||||
|
case ORIGINAL -> BookMeta.Generation.COPY_OF_ORIGINAL;
|
||||||
|
case COPY_OF_ORIGINAL -> BookMeta.Generation.COPY_OF_COPY;
|
||||||
|
default -> currentGeneration;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file name of the given book
|
||||||
|
*
|
||||||
|
* @param book <p>The book to get the file of</p>
|
||||||
|
* @param player <p>The player trying to do something with the book</p>
|
||||||
|
* @return <p>The book file</p>
|
||||||
|
*/
|
||||||
|
public static String getBookFile(BookMeta book, Player player, boolean isPublic) {
|
||||||
|
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
String bookName;
|
||||||
|
if (book.hasTitle()) {
|
||||||
|
bookName = book.getTitle();
|
||||||
|
} else {
|
||||||
|
bookName = "Untitled";
|
||||||
|
}
|
||||||
|
|
||||||
|
String authorName;
|
||||||
|
if ((!book.hasAuthor() || isAuthor(player.getName(), book.getAuthor())) && !isPublic) {
|
||||||
|
//Store as unique id to account for name changes
|
||||||
|
authorName = player.getUniqueId().toString();
|
||||||
|
} else if (!book.hasAuthor()) {
|
||||||
|
authorName = player.getName();
|
||||||
|
} else {
|
||||||
|
authorName = book.getAuthor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixName(cleanString(bookName + titleAuthorSeparator + authorName), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given player is the author of a given book
|
||||||
|
*
|
||||||
|
* @param player <p>The player to check</p>
|
||||||
|
* @param book <p>The book to check</p>
|
||||||
|
* @return <p>True if the player is not the book's author</p>
|
||||||
|
*/
|
||||||
|
public static boolean isNotAuthor(Player player, BookMeta book) {
|
||||||
|
if (isAuthor(player.getName(), book.getAuthor())) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player,
|
||||||
|
"You must be the author of this book to use this command!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the given player name is equal to the given book author
|
||||||
|
*
|
||||||
|
* @param playerName <p>The player name to check</p>
|
||||||
|
* @param author <p>The author to check</p>
|
||||||
|
* @return <p>True if the player is the author</p>
|
||||||
|
*/
|
||||||
|
private static boolean isAuthor(String playerName, String author) {
|
||||||
|
playerName = InputCleaningHelper.cleanString(playerName);
|
||||||
|
return author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -64,7 +64,8 @@ public final class BookLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Make sure the player can pay for the book
|
//Make sure the player can pay for the book
|
||||||
if (BooksWithoutBordersConfig.booksHavePrice() && !sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
||||||
|
!sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||||
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
||||||
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
||||||
return null;
|
return null;
|
||||||
@ -98,6 +99,8 @@ public final class BookLoader {
|
|||||||
|
|
||||||
//Set the metadata and amount to the new book
|
//Set the metadata and amount to the new book
|
||||||
book.setItemMeta(bookMetadata);
|
book.setItemMeta(bookMetadata);
|
||||||
|
//Increase book generation if enabled
|
||||||
|
BookHelper.increaseGeneration(book);
|
||||||
book.setAmount(numCopies);
|
book.setAmount(numCopies);
|
||||||
|
|
||||||
return book;
|
return book;
|
||||||
@ -113,16 +116,15 @@ public final class BookLoader {
|
|||||||
* @return <p>A file or null if it does not exist</p>
|
* @return <p>A file or null if it does not exist</p>
|
||||||
*/
|
*/
|
||||||
private static File getFullPath(CommandSender sender, String fileName, BookDirectory bookDirectory, String directory) {
|
private static File getFullPath(CommandSender sender, String fileName, BookDirectory bookDirectory, String directory) {
|
||||||
File file = null;
|
File file;
|
||||||
String slash = BooksWithoutBordersConfig.getSlash();
|
String slash = BooksWithoutBordersConfig.getSlash();
|
||||||
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
if (bookDirectory == BookDirectory.PUBLIC) {
|
if (bookDirectory == BookDirectory.ENCRYPTED) {
|
||||||
file = FileHelper.getBookFile(bookFolder + fileName);
|
|
||||||
} else if (bookDirectory == BookDirectory.PLAYER) {
|
|
||||||
file = FileHelper.getBookFile(bookFolder + InputCleaningHelper.cleanString(sender.getName()) + slash + fileName);
|
|
||||||
} else if (bookDirectory == BookDirectory.ENCRYPTED) {
|
|
||||||
file = FileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName);
|
file = FileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName);
|
||||||
|
} else {
|
||||||
|
file = FileHelper.getBookFile(BookHelper.getBookDirectoryPathString(bookDirectory, sender) + fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null || !file.isFile()) {
|
if (file == null || !file.isFile()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect file name!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect file name!");
|
||||||
return null;
|
return null;
|
||||||
|
@ -14,6 +14,7 @@ import java.io.PrintWriter;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.BookHelper.authorFromUUID;
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +42,11 @@ public final class BookToFromTextHelper {
|
|||||||
if (bookMetadata.hasAuthor()) {
|
if (bookMetadata.hasAuthor()) {
|
||||||
bookYml.set("Author", bookMetadata.getAuthor());
|
bookYml.set("Author", bookMetadata.getAuthor());
|
||||||
}
|
}
|
||||||
|
BookMeta.Generation generation = bookMetadata.getGeneration();
|
||||||
|
if (generation == null) {
|
||||||
|
generation = BookMeta.Generation.ORIGINAL;
|
||||||
|
}
|
||||||
|
bookYml.set("Generation", generation.name());
|
||||||
if (bookMetadata.hasPages()) {
|
if (bookMetadata.hasPages()) {
|
||||||
bookYml.set("Pages", bookMetadata.getPages());
|
bookYml.set("Pages", bookMetadata.getPages());
|
||||||
}
|
}
|
||||||
@ -81,8 +87,14 @@ public final class BookToFromTextHelper {
|
|||||||
PrintWriter printWriter = new PrintWriter(fileWriter);
|
PrintWriter printWriter = new PrintWriter(fileWriter);
|
||||||
List<String> pages = bookMetadata.getPages();
|
List<String> pages = bookMetadata.getPages();
|
||||||
|
|
||||||
|
BookMeta.Generation generation = bookMetadata.getGeneration();
|
||||||
|
if (generation == null) {
|
||||||
|
generation = BookMeta.Generation.ORIGINAL;
|
||||||
|
}
|
||||||
|
String generationString = ":" + generation.name();
|
||||||
|
|
||||||
//Save each page of the book as a text line
|
//Save each page of the book as a text line
|
||||||
printWriter.println("[Book]");
|
printWriter.println("[Book]" + generationString);
|
||||||
for (String page : pages) {
|
for (String page : pages) {
|
||||||
printWriter.println(page);
|
printWriter.println(page);
|
||||||
}
|
}
|
||||||
@ -100,8 +112,9 @@ public final class BookToFromTextHelper {
|
|||||||
try {
|
try {
|
||||||
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
|
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
|
||||||
|
|
||||||
|
bookMetadata.setGeneration(BookMeta.Generation.valueOf(bookYml.getString("Generation", "ORIGINAL")));
|
||||||
bookMetadata.setTitle(bookYml.getString("Title", "Untitled"));
|
bookMetadata.setTitle(bookYml.getString("Title", "Untitled"));
|
||||||
bookMetadata.setAuthor(bookYml.getString("Author", "Unknown"));
|
bookMetadata.setAuthor(authorFromUUID(bookYml.getString("Author", "Unknown")));
|
||||||
bookMetadata.setPages(bookYml.getStringList("Pages"));
|
bookMetadata.setPages(bookYml.getStringList("Pages"));
|
||||||
bookMetadata.setLore(bookYml.getStringList("Lore"));
|
bookMetadata.setLore(bookYml.getStringList("Lore"));
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -123,13 +136,16 @@ public final class BookToFromTextHelper {
|
|||||||
String title;
|
String title;
|
||||||
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
|
||||||
|
//Remove .txt extension
|
||||||
|
fileName = fileName.substring(0, fileName.length() - 4);
|
||||||
//Get title and author from the file name
|
//Get title and author from the file name
|
||||||
if (fileName.contains(titleAuthorSeparator)) {
|
if (fileName.contains(titleAuthorSeparator)) {
|
||||||
author = fileName.substring(fileName.indexOf(titleAuthorSeparator) + 1, fileName.length() - 4);
|
String[] titleAuthor = fileName.split(titleAuthorSeparator);
|
||||||
title = fileName.substring(0, fileName.indexOf(titleAuthorSeparator));
|
title = titleAuthor[0];
|
||||||
|
author = titleAuthor[1];
|
||||||
} else {
|
} else {
|
||||||
author = "Unknown";
|
author = "Unknown";
|
||||||
title = fileName.substring(0, fileName.length() - 4);
|
title = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Replace underscores with spaces
|
//Replace underscores with spaces
|
||||||
@ -144,11 +160,17 @@ public final class BookToFromTextHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Parse the generation from the book data
|
||||||
|
if (rawPages != null && rawPages.size() > 0 && rawPages.get(0).startsWith("Generation:")) {
|
||||||
|
bookMetadata.setGeneration(BookMeta.Generation.valueOf(rawPages.get(0).split(":")[1]));
|
||||||
|
rawPages.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
//Remove any empty pages
|
//Remove any empty pages
|
||||||
List<String> pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages));
|
List<String> pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages));
|
||||||
|
|
||||||
//Update the metadata of the book with its new values
|
//Update the metadata of the book with its new values
|
||||||
bookMetadata.setAuthor(author);
|
bookMetadata.setAuthor(authorFromUUID(author));
|
||||||
bookMetadata.setTitle(title);
|
bookMetadata.setTitle(title);
|
||||||
bookMetadata.setPages(pages);
|
bookMetadata.setPages(pages);
|
||||||
|
|
||||||
@ -172,8 +194,11 @@ public final class BookToFromTextHelper {
|
|||||||
bufferedReader.close();
|
bufferedReader.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (firstLine.equalsIgnoreCase("[Book]")) {
|
if (firstLine.toLowerCase().startsWith("[book]")) {
|
||||||
//Read every line directly as a page, as this is a saved book
|
//Read every line directly as a page, as this is a saved book
|
||||||
|
if (firstLine.contains(":")) {
|
||||||
|
rawPages.add("Generation:" + firstLine.split(":")[1]);
|
||||||
|
}
|
||||||
String readLine;
|
String readLine;
|
||||||
do {
|
do {
|
||||||
readLine = bufferedReader.readLine();
|
readLine = bufferedReader.readLine();
|
||||||
|
@ -8,10 +8,13 @@ import org.bukkit.Server;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||||
import org.bukkit.plugin.ServicesManager;
|
import org.bukkit.plugin.ServicesManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,6 +76,10 @@ public final class EconomyHelper {
|
|||||||
|
|
||||||
if (bookCurrency == Material.AIR) {
|
if (bookCurrency == Material.AIR) {
|
||||||
return !EconomyHelper.payForBookPrintingEconomy(player, cost, numCopies);
|
return !EconomyHelper.payForBookPrintingEconomy(player, cost, numCopies);
|
||||||
|
} else {
|
||||||
|
if (bookCurrency == Material.WRITABLE_BOOK) {
|
||||||
|
//Writable books are treated as a special case to prevent WIP books from being used
|
||||||
|
return !takeWritableBookPayment(player, itemCost);
|
||||||
} else {
|
} else {
|
||||||
if (player.getInventory().contains(bookCurrency, itemCost)) {
|
if (player.getInventory().contains(bookCurrency, itemCost)) {
|
||||||
payForBookPrintingItem(player, itemCost);
|
payForBookPrintingItem(player, itemCost);
|
||||||
@ -84,6 +91,74 @@ public final class EconomyHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a writable book payment, ignoring any books containing text
|
||||||
|
*
|
||||||
|
* @param player <p>The player to take payment from</p>
|
||||||
|
* @param itemCost <p>The number of writable books to pay</p>
|
||||||
|
* @return <p>True if the payment was successful</p>
|
||||||
|
*/
|
||||||
|
private static boolean takeWritableBookPayment(Player player, int itemCost) {
|
||||||
|
List<ItemStack> books = getPlayersEmptyBooks(player);
|
||||||
|
if (countItems(books) < itemCost) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, itemCost + " empty " + Material.WRITABLE_BOOK +
|
||||||
|
"(s) are required for this command!");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
int clearedAmount = 0;
|
||||||
|
for (ItemStack itemStack : books) {
|
||||||
|
if (itemStack.getAmount() > itemCost) {
|
||||||
|
//If encountering an item stack with more than enough books, remove the necessary books
|
||||||
|
itemStack.setAmount(itemStack.getAmount() - itemCost);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
clearedAmount += itemStack.getAmount();
|
||||||
|
player.getInventory().removeItem(itemStack);
|
||||||
|
}
|
||||||
|
if (clearedAmount >= itemCost) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total number of items contained in the given list of items
|
||||||
|
*
|
||||||
|
* @param items <p>The items to count</p>
|
||||||
|
* @return <p>The total number of items</p>
|
||||||
|
*/
|
||||||
|
private static int countItems(List<ItemStack> items) {
|
||||||
|
int totalItems = 0;
|
||||||
|
for (ItemStack itemStack : items) {
|
||||||
|
totalItems += itemStack.getAmount();
|
||||||
|
}
|
||||||
|
return totalItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all empty books in a player's inventory
|
||||||
|
*
|
||||||
|
* @param player <p>The player to get books for</p>
|
||||||
|
* @return <p>The empty books in the player's inventory</p>
|
||||||
|
*/
|
||||||
|
private static List<ItemStack> getPlayersEmptyBooks(Player player) {
|
||||||
|
List<ItemStack> validBooks = new ArrayList<>();
|
||||||
|
for (ItemStack itemStack : player.getInventory().getContents()) {
|
||||||
|
if (itemStack == null || itemStack.getType() != Material.WRITABLE_BOOK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BookMeta book = (BookMeta) itemStack.getItemMeta();
|
||||||
|
//Only accept empty books
|
||||||
|
if (book != null && (!book.hasPages() || (book.getPageCount() == 1 && book.getPage(1).trim().isEmpty()))) {
|
||||||
|
validBooks.add(itemStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validBooks;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses economy to take payment for printing a number of books
|
* Uses economy to take payment for printing a number of books
|
||||||
@ -96,8 +171,10 @@ public final class EconomyHelper {
|
|||||||
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) {
|
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) {
|
||||||
if ((economy.getBalance(player) - cost) >= 0) {
|
if ((economy.getBalance(player) - cost) >= 0) {
|
||||||
economy.withdrawPlayer(player, cost);
|
economy.withdrawPlayer(player, cost);
|
||||||
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + numCopies + " book(s)");
|
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " +
|
||||||
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " + economy.format(economy.getBalance(player)));
|
numCopies + " book(s)");
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " +
|
||||||
|
economy.format(economy.getBalance(player)));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!");
|
BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!");
|
||||||
|
@ -198,11 +198,8 @@ public final class EncryptionHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() : bookMetadata.getTitle() +
|
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetadata, player, true);
|
||||||
BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
fileName = fixName(cleanString(fileName), false);
|
||||||
fileName = "[" + key + "]" + fileName;
|
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
File file = new File(path + fileName + ".yml");
|
File file = new File(path + fileName + ".yml");
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
@ -263,12 +260,8 @@ public final class EncryptionHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Creates file
|
//Generate file name
|
||||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() :
|
String fileName = BookHelper.getBookFile(bookMetadata, player, true);
|
||||||
bookMetadata.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
|
||||||
|
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
List<String> newLore = new ArrayList<>();
|
List<String> newLore = new ArrayList<>();
|
||||||
newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]");
|
newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]");
|
||||||
@ -281,7 +274,8 @@ public final class EncryptionHelper {
|
|||||||
bookMetadata.setLore(newLore);
|
bookMetadata.setLore(newLore);
|
||||||
|
|
||||||
//Save file
|
//Save file
|
||||||
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") : new File(path + fileName + ".txt");
|
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") :
|
||||||
|
new File(path + fileName + ".txt");
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
try {
|
try {
|
||||||
BookToFromTextHelper.bookToYml(path, fileName, bookMetadata);
|
BookToFromTextHelper.bookToYml(path, fileName, bookMetadata);
|
||||||
@ -305,15 +299,13 @@ public final class EncryptionHelper {
|
|||||||
*/
|
*/
|
||||||
private static Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) {
|
private static Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) {
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||||
String fileName = (!bookMetaData.hasTitle()) ? "Untitled," + player.getName() :
|
|
||||||
bookMetaData.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetaData.getAuthor();
|
|
||||||
|
|
||||||
fileName = "[" + key + "]" + fileName;
|
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetaData, player, true);
|
||||||
fileName = cleanString(fileName);
|
fileName = fixName(cleanString(fileName), false);
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
//cancels saving if file is already encrypted
|
//cancels saving if file is already encrypted
|
||||||
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") : new File(path + fileName + ".txt");
|
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") :
|
||||||
|
new File(path + fileName + ".txt");
|
||||||
if (file.isFile()) {
|
if (file.isFile()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.knarcraft.bookswithoutborders.utility;
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
@ -15,8 +17,6 @@ import java.util.Objects;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for dealing with files
|
* Helper class for dealing with files
|
||||||
@ -98,11 +98,9 @@ public final class FileHelper {
|
|||||||
* @return <p>A list of available files</p>
|
* @return <p>A list of available files</p>
|
||||||
*/
|
*/
|
||||||
public static List<String> listFiles(CommandSender sender, Boolean listPublic) {
|
public static List<String> listFiles(CommandSender sender, Boolean listPublic) {
|
||||||
File file;
|
File file = BookHelper.getBookDirectoryPath(listPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||||
if (listPublic) {
|
if (file == null) {
|
||||||
file = new File(getBookFolder());
|
return new ArrayList<>();
|
||||||
} else {
|
|
||||||
file = new File(getBookFolder() + cleanString(sender.getName()) + getSlash());
|
|
||||||
}
|
}
|
||||||
return FileHelper.listFiles(sender, file);
|
return FileHelper.listFiles(sender, file);
|
||||||
}
|
}
|
||||||
@ -152,8 +150,20 @@ public final class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (File foundFile : existingFiles) {
|
for (File foundFile : existingFiles) {
|
||||||
if (foundFile.isFile()) {
|
if (!foundFile.isFile()) {
|
||||||
fileList.add(foundFile.getName());
|
continue;
|
||||||
|
}
|
||||||
|
String fileName = foundFile.getName();
|
||||||
|
String separator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
if (fileName.contains(separator)) {
|
||||||
|
//Convert the UUID into a username if necessary
|
||||||
|
String[] data = fileName.split(separator);
|
||||||
|
String extension = data[1].substring(data[1].length() - 4);
|
||||||
|
String userName = data[1].substring(0, data[1].length() - 4);
|
||||||
|
data[1] = BookHelper.authorFromUUID(userName) + extension;
|
||||||
|
fileList.add(String.join(separator, data));
|
||||||
|
} else {
|
||||||
|
fileList.add(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,31 @@ public final class InventoryHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs checks to validate that a player contains exactly one unwritten book
|
||||||
|
*
|
||||||
|
* @param player <p>The player to validate</p>
|
||||||
|
* @param noBookMessage <p>The message to display if the player is not holding a book</p>
|
||||||
|
* @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p>
|
||||||
|
* @return <p>False if the player is holding exactly one book</p>
|
||||||
|
*/
|
||||||
|
public static boolean notHoldingOneWritableBookCheck(Player player, String noBookMessage, String twoBooksMessage) {
|
||||||
|
BookHoldingState holdingState = getBookHoldingState(player);
|
||||||
|
|
||||||
|
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.SIGNED_BOTH_HANDS ||
|
||||||
|
holdingState == BookHoldingState.SIGNED_MAIN_HAND || holdingState == BookHoldingState.SIGNED_OFF_HAND) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, noBookMessage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, twoBooksMessage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs checks to validate that a player contains exactly one written book
|
* Performs checks to validate that a player contains exactly one written book
|
||||||
*
|
*
|
||||||
@ -96,7 +121,7 @@ public final class InventoryHelper {
|
|||||||
if (state == BookHoldingState.SIGNED_BOTH_HANDS ||
|
if (state == BookHoldingState.SIGNED_BOTH_HANDS ||
|
||||||
state == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
state == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
||||||
state == BookHoldingState.NONE) {
|
state == BookHoldingState.NONE) {
|
||||||
return null;
|
return ItemSlot.NONE;
|
||||||
}
|
}
|
||||||
if (handMatters && typeMatters) {
|
if (handMatters && typeMatters) {
|
||||||
if (mainHand && mainHandItem.getType() == requiredMaterial) {
|
if (mainHand && mainHandItem.getType() == requiredMaterial) {
|
||||||
|
@ -5,7 +5,7 @@ Options:
|
|||||||
Max_Number_of_Duplicates: 5
|
Max_Number_of_Duplicates: 5
|
||||||
# The separator used to separate the book title and the book author
|
# The separator used to separate the book title and the book author
|
||||||
Title-Author_Separator: ","
|
Title-Author_Separator: ","
|
||||||
# The separator used to denote a new line in the book lore
|
# The separator used to denote a new line in the book/item lore
|
||||||
Lore_line_separator: "~"
|
Lore_line_separator: "~"
|
||||||
# A list of books given to new players the first time they join the server
|
# A list of books given to new players the first time they join the server
|
||||||
Books_for_new_players: [ ]
|
Books_for_new_players: [ ]
|
||||||
@ -21,6 +21,13 @@ Options:
|
|||||||
Admin_Auto_Decrypt: false
|
Admin_Auto_Decrypt: false
|
||||||
# Whether to only allow the author of a book to create copies
|
# Whether to only allow the author of a book to create copies
|
||||||
Author_Only_Copy: false
|
Author_Only_Copy: false
|
||||||
|
# Whether to only allow the author of a book to unsign it
|
||||||
|
Author_Only_Unsign: false
|
||||||
|
# Whether to only allow saving a player's own books with /savebook
|
||||||
|
Author_Only_Save: false
|
||||||
# Whether to automatically format every book when it's signed
|
# Whether to automatically format every book when it's signed
|
||||||
Format_Book_After_Signing: false
|
Format_Book_After_Signing: false
|
||||||
|
# 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.
|
||||||
|
Change_Generation_On_Copy: false
|
||||||
|
|
@ -45,6 +45,10 @@ commands:
|
|||||||
description: Copies the book the player is holding
|
description: Copies the book the player is holding
|
||||||
usage: /<command> <# of copies>
|
usage: /<command> <# of copies>
|
||||||
permission: bookswithoutborders.copy
|
permission: bookswithoutborders.copy
|
||||||
|
clearbook:
|
||||||
|
description: Removes all text from the book the player is holding
|
||||||
|
usage: /<command>
|
||||||
|
permission: bookswithoutborders.clear
|
||||||
unsignbook:
|
unsignbook:
|
||||||
description: Un-signs the book the player is holding
|
description: Un-signs the book the player is holding
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
@ -53,6 +57,10 @@ commands:
|
|||||||
description: Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
description: Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
||||||
usage: /<command> <key> [encryption style]
|
usage: /<command> <key> [encryption style]
|
||||||
permission: bookswithoutborders.encrypt
|
permission: bookswithoutborders.encrypt
|
||||||
|
setbookgeneration:
|
||||||
|
description: Sets the generation of the held book
|
||||||
|
usage: /<command> <generation>
|
||||||
|
permission: bookswithoutborders.setgeneration
|
||||||
setbookprice:
|
setbookprice:
|
||||||
description: Sets the per-book-price to create a book via commands. If "Item", the item in the player's hand in the amount of [quantity] will be the price. If "Eco", a Vault based economy will be used for price. If neither <Item/Eco> or <quantity> are specified, the current price to create books will be removed.
|
description: Sets the per-book-price to create a book via commands. If "Item", the item in the player's hand in the amount of [quantity] will be the price. If "Eco", a Vault based economy will be used for price. If neither <Item/Eco> or <quantity> are specified, the current price to create books will be removed.
|
||||||
usage: /<command> <item/eco> <quantity>
|
usage: /<command> <item/eco> <quantity>
|
||||||
@ -88,7 +96,7 @@ commands:
|
|||||||
reload:
|
reload:
|
||||||
description: Reloads BwB's configuration file
|
description: Reloads BwB's configuration file
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
permission: bookswithoutborders.admin
|
permission: bookswithoutborders.reload
|
||||||
permissions:
|
permissions:
|
||||||
bookswithoutborders.*:
|
bookswithoutborders.*:
|
||||||
description: Grants all permissions
|
description: Grants all permissions
|
||||||
@ -111,8 +119,12 @@ permissions:
|
|||||||
bookswithoutborders.give: true
|
bookswithoutborders.give: true
|
||||||
bookswithoutborders.givepublic: true
|
bookswithoutborders.givepublic: true
|
||||||
bookswithoutborders.bypassauthoronlycopy: true
|
bookswithoutborders.bypassauthoronlycopy: true
|
||||||
|
bookswithoutborders.bypassauthoronlyunsign: true
|
||||||
|
bookswithoutborders.bypassauthoronlysave: true
|
||||||
bookswithoutborders.bypassbookprice: true
|
bookswithoutborders.bypassbookprice: true
|
||||||
bookswithoutborders.setbookprice: true
|
bookswithoutborders.setbookprice: true
|
||||||
|
bookswithoutborders.reload: true
|
||||||
|
bookswithoutborders.setgeneration: true
|
||||||
bookswithoutborders.use:
|
bookswithoutborders.use:
|
||||||
description: Allows player to use commands to save/load/delete in their personal directory
|
description: Allows player to use commands to save/load/delete in their personal directory
|
||||||
children:
|
children:
|
||||||
@ -120,13 +132,15 @@ permissions:
|
|||||||
bookswithoutborders.load: true
|
bookswithoutborders.load: true
|
||||||
bookswithoutborders.delete: true
|
bookswithoutborders.delete: true
|
||||||
bookswithoutborders.alterbooks:
|
bookswithoutborders.alterbooks:
|
||||||
description: Allows player to change books' data such as lore/title/author/formatting and unsigning books
|
description: Allows player to change books' data such as lore/title/author/generation/formatting and unsigning books
|
||||||
children:
|
children:
|
||||||
|
bookswithoutborders.clear: true
|
||||||
bookswithoutborders.unsign: true
|
bookswithoutborders.unsign: true
|
||||||
bookswithoutborders.settitle: true
|
bookswithoutborders.settitle: true
|
||||||
bookswithoutborders.setauthor: true
|
bookswithoutborders.setauthor: true
|
||||||
bookswithoutborders.setlore: true
|
bookswithoutborders.setlore: true
|
||||||
bookswithoutborders.format: true
|
bookswithoutborders.format: true
|
||||||
|
bookswithoutborders.setgeneration: true
|
||||||
bookswithoutborders.format:
|
bookswithoutborders.format:
|
||||||
description: Allows a player to format a book
|
description: Allows a player to format a book
|
||||||
bookswithoutborders.save:
|
bookswithoutborders.save:
|
||||||
@ -136,9 +150,11 @@ permissions:
|
|||||||
bookswithoutborders.delete:
|
bookswithoutborders.delete:
|
||||||
description: Allows player to delete books from their personal directory
|
description: Allows player to delete books from their personal directory
|
||||||
bookswithoutborders.unsign:
|
bookswithoutborders.unsign:
|
||||||
description: Allows player to use unsign command
|
description: Allows player to use the unsign command
|
||||||
bookswithoutborders.copy:
|
bookswithoutborders.copy:
|
||||||
description: Allows player to use copy command
|
description: Allows player to use the copy command
|
||||||
|
bookswithoutborders.clear:
|
||||||
|
description: Allows player to use the clear command
|
||||||
bookswithoutborders.loadpublic:
|
bookswithoutborders.loadpublic:
|
||||||
description: Allows player to load from the public directory
|
description: Allows player to load from the public directory
|
||||||
bookswithoutborders.savepublic:
|
bookswithoutborders.savepublic:
|
||||||
@ -163,7 +179,15 @@ permissions:
|
|||||||
description: Allows player to set the lore of the currently held item
|
description: Allows player to set the lore of the currently held item
|
||||||
bookswithoutborders.bypassauthoronlycopy:
|
bookswithoutborders.bypassauthoronlycopy:
|
||||||
description: Allows player to ignore Author_Only_Copy config setting
|
description: Allows player to ignore Author_Only_Copy config setting
|
||||||
|
bookswithoutborders.bypassauthoronlyunsign:
|
||||||
|
description: Allows player to ignore Author_Only_Unsign config setting
|
||||||
|
bookswithoutborders.bypassauthoronlysave:
|
||||||
|
description: Allows player to ignore Author_Only_Save config setting
|
||||||
bookswithoutborders.bypassbookprice:
|
bookswithoutborders.bypassbookprice:
|
||||||
description: Allows player to ignore Price_to_create_book config setting
|
description: Allows player to ignore Price_to_create_book config setting
|
||||||
bookswithoutborders.setbookprice:
|
bookswithoutborders.setbookprice:
|
||||||
description: Allows player to set the cost of creating a book
|
description: Allows player to set the cost of creating a book
|
||||||
|
bookswithoutborders.reload:
|
||||||
|
description: Allows player to reload this plugin
|
||||||
|
bookswithoutborders.setgeneration:
|
||||||
|
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
|
Reference in New Issue
Block a user