Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
0b701ddef1 | |||
0fe9ccb590 | |||
1a7236cebd | |||
e482e494f8 | |||
0f76c8f869 | |||
6b4e87d33a | |||
40b0fa0baf | |||
31bb26b755 | |||
0e134fcdab | |||
f5ba4db5ac | |||
29d5247c9b | |||
c389c6fbcb | |||
1158820f97 | |||
f9908db88f | |||
738dbe6e30 | |||
3838750f64 | |||
16954f22e3 | |||
6359af02e6 | |||
6ae15acc72 | |||
2c80e4117e | |||
6d539a14df | |||
51371736e4 | |||
2383dfd2ef | |||
04d4bc8303 | |||
184f78d935 | |||
95b0b42fd1 | |||
73eb903517 | |||
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 | |||
0ce85af61b | |||
ed54ae84b5 | |||
27d5980fa8 | |||
544f6f69fe | |||
055d7fca60 | |||
8f7cfc591f | |||
2aa25295af | |||
d423b1e109 | |||
7acaa9fc81 | |||
94589faba0 |
33
Jenkinsfile
vendored
Normal file
33
Jenkinsfile
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
pipeline {
|
||||
agent any
|
||||
tools {
|
||||
jdk 'JDK17'
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
echo 'Building...'
|
||||
sh 'mvn clean & mvn validate & mvn compile'
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
echo 'Testing...'
|
||||
sh 'mvn test'
|
||||
}
|
||||
}
|
||||
stage('Verify') {
|
||||
steps {
|
||||
echo 'Verifying...'
|
||||
sh 'mvn verify -Dmaven.test.skip=true'
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
steps {
|
||||
echo 'Deploying...'
|
||||
sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
|
||||
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
134
README.md
134
README.md
@ -1,10 +1,9 @@
|
||||
# Books Without Borders
|
||||
|
||||
This is an attempt at a rewrite of the Books Without Borders plugin. This rewrite uses 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 this time. The only goal is to make it 1.17.1 compliant, but I'll make the code more maintainable along the way.
|
||||
While the original version still works, it's using a lot of depreciated function calls which will most likely break in
|
||||
the future.
|
||||
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). While the old plugin still worked
|
||||
the last time I checked, this plugin does not use any depreciated function calls, making sure it works for the
|
||||
foreseeable future.
|
||||
|
||||
## Books without Borders!
|
||||
|
||||
@ -13,40 +12,131 @@ Books without Borders has got your back!
|
||||
|
||||
### Features
|
||||
|
||||
* Export written books and book and quills to .txt or .yml files
|
||||
* 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
|
||||
* Books can be saved privately, or to a directory visible server wide
|
||||
* Encrypt books to prevent other players from reading them
|
||||
* Give, encrypt, or decrypt held books with signs
|
||||
* Give players books via command blocks
|
||||
* Unsign or copy held books with a simple command
|
||||
* 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
|
||||
* Add lore to any item with a simple command
|
||||
* Supports adding and saving color to title, lore, and book contents
|
||||
- Export written books and book and quills to .txt or .yml files
|
||||
- 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
|
||||
- Books can be saved privately, or to a directory visible server wide
|
||||
- Encrypt books to prevent other players from reading them
|
||||
- Give, encrypt, or decrypt held books with signs
|
||||
- Give players books via command blocks
|
||||
- Unsign or copy held books with a simple command
|
||||
- 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
|
||||
- Add lore to any item with a simple command
|
||||
- Supports adding and saving color to title, lore, and book contents
|
||||
- Color and formatting codes can be manually turned into formatting using /formatbook
|
||||
- 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
|
||||
- Change generation of books. Create tattered books for your RPG server!
|
||||
- Optionally, make it impossible to duplicate the original version of a book
|
||||
- Optionally, hit a bookshelf while sneaking to display the contained books
|
||||
|
||||
#### 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.
|
||||
|
||||
### Commands:
|
||||
|
||||
An in-game description of available commands is available through the /bwb command.
|
||||
|
||||
| Command | Arguments | Description |
|
||||
|----------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| /bookswithoutborders | None | Displays information about commands (and permissions if the user has bookswithoutborders.admin) |
|
||||
| /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 to encrypt the held book |
|
||||
| /deletebook | \<file name or number> | Deletes the specified file in the player's directory |
|
||||
| /deletepublicbook | \<file name or number> | Same as deletebook, but deletes files in the public directory |
|
||||
| /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 | 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 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 |
|
||||
| /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 |
|
||||
| /loadpublicbook | \<file name or number> \[# of copies] \[signed (true/false)] | Same as loadbook, but views files in the public directory |
|
||||
| /reload | None | Reloads BwB's configuration file |
|
||||
| /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 |
|
||||
| /savepublicbook | \[overwrite (true/false)] | Same as savebook, but saves files in the public directory |
|
||||
| /setbookauthor | \<author> | Sets the author of the book the player is holding |
|
||||
| /setbookgeneration | \<generation> | Sets the generation of the held book (ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED) |
|
||||
| /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 | None | Un-signs the book the player is holding |
|
||||
| /clearbook | None | Removes all text from the held un-signed book |
|
||||
|
||||
### Permissions:
|
||||
|
||||
| Node | Description |
|
||||
|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| bookswithoutborders.* | Grants all permissions |
|
||||
| bookswithoutborders.admin | Grants all permissions |
|
||||
| bookswithoutborders.use | bookswithoutborders.use - Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled |
|
||||
| bookswithoutborders.alterbooks | Allows player to change books' data such as lore/title/author/generation/formatting and un-signing books |
|
||||
| bookswithoutborders.reload | Allows player to reload this plugin |
|
||||
| bookswithoutborders.format | Allows a player to format a book |
|
||||
| bookswithoutborders.save | Allows a player to save books to their personal directory |
|
||||
| bookswithoutborders.load | Allows player to load books from their personal directory |
|
||||
| bookswithoutborders.delete | Allows player to delete books from their personal directory |
|
||||
| bookswithoutborders.unsign | Allows player to un-sign books |
|
||||
| bookswithoutborders.copy | Allows player to copy books |
|
||||
| bookswithoutborders.loadpublic | Allows player to load from the public directory |
|
||||
| bookswithoutborders.savepublic | Allows player to save to the public directory |
|
||||
| bookswithoutborders.encrypt | Allows player to encrypt books |
|
||||
| bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
|
||||
| bookswithoutborders.decrypt | Allows player to decrypt books |
|
||||
| bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
|
||||
| bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
|
||||
| bookswithoutborders.give | Allows player to give another player one of their privately saved books |
|
||||
| bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
|
||||
| bookswithoutborders.settitle | Allows player to set the title of the currently held book |
|
||||
| bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
|
||||
| bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
|
||||
| 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 |
|
||||
| bookswithoutborders.peekbookshelf | Allows player to left-click a bookshelf to see the contents of the shelf |
|
||||
|
||||
### Signs
|
||||
|
||||
This plugin supports several custom signs with special functionality. Each plugin sign must have [BwB] on its first
|
||||
This plugin supports several custom signs with special functionality. Each plugin sign must have \[BwB] on its first
|
||||
line.
|
||||
|
||||
#### Give sign
|
||||
|
||||
The **_give_** sign must have **[Give]** on its second line. The third and fourth line contains the book to be loaded.
|
||||
The **_give_**-sign must have **\[Give]** on its second line. The third and fourth line contains the book to be loaded.
|
||||
This can either be a numerical id pointing to a publicly saved book, or the full text identifier of the book (book name,
|
||||
author).
|
||||
|
||||
#### Encrypt sign
|
||||
|
||||
The **_encrypt_** sign must have **[Encrypt]** on its second line. The third line must contain the encryption key The
|
||||
The **_encrypt_**-sign must have **\[Encrypt]** on its second line. The third line must contain the encryption key The
|
||||
fourth line can be empty or contain "dna" for dna-based encryption.
|
||||
|
||||
#### 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. |
|
||||
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents. |
|
64
pom.xml
64
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>BooksWithoutBorders</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.3.6-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<licenses>
|
||||
@ -38,7 +38,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@ -47,6 +47,30 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.knarcraft.knarlib</pattern>
|
||||
<shadedPattern>net.knarcraft.bookswithoutborders.lib.knarlib</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.jetbrains.annotations</pattern>
|
||||
<shadedPattern>net.knarcraft.bookswithoutborders.lib.annotations</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>net.knarcraft:knarlib</artifact>
|
||||
<includes>
|
||||
<include>net/knarcraft/knarlib/**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.jetbrains:annotations</artifact>
|
||||
<includes>
|
||||
<include>org/jetbrains/annotations/**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@ -61,6 +85,10 @@
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>knarcraft-repo</id>
|
||||
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
@ -70,19 +98,47 @@
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>knarcraft-repo</id>
|
||||
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>knarcraft-repo</id>
|
||||
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.17.1-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7</version>
|
||||
<version>1.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>knarlib</artifactId>
|
||||
<version>1.2.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>24.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,11 +1,13 @@
|
||||
package net.knarcraft.bookswithoutborders;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandClear;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandCopy;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandDelete;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandDeletePublic;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandEncrypt;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandFormat;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandGive;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandGivePublic;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandGroupEncrypt;
|
||||
@ -16,25 +18,25 @@ import net.knarcraft.bookswithoutborders.command.CommandSave;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSetGeneration;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSetLore;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
||||
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.listener.BookEventListener;
|
||||
import net.knarcraft.bookswithoutborders.listener.BookshelfListener;
|
||||
import net.knarcraft.bookswithoutborders.listener.PlayerEventListener;
|
||||
import net.knarcraft.bookswithoutborders.listener.SignEventListener;
|
||||
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import org.bukkit.Material;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.knarlib.util.UpdateChecker;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemFactory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
@ -43,28 +45,21 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getErrorColor;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSuccessColor;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSuccessColor;
|
||||
|
||||
/**
|
||||
* The main Books Without Borders class
|
||||
*/
|
||||
public class BooksWithoutBorders extends JavaPlugin {
|
||||
|
||||
private static int bookDuplicateLimit;
|
||||
private static String titleAuthorSeparator;
|
||||
private static String loreSeparator;
|
||||
private static List<String> firstBooks;
|
||||
private static String welcomeMessage;
|
||||
private static Material bookPriceType = null;
|
||||
private static double bookPriceQuantity;
|
||||
private static boolean authorOnlyCopy;
|
||||
private static boolean useYml;
|
||||
private static boolean adminDecrypt;
|
||||
|
||||
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 BooksWithoutBorders booksWithoutBorders;
|
||||
private static ConsoleCommandSender consoleSender;
|
||||
@ -88,13 +83,15 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
public static List<String> getAvailableBooks(CommandSender sender, boolean getPublic) {
|
||||
if (getPublic) {
|
||||
return new ArrayList<>(publicBooksList);
|
||||
} else {
|
||||
String senderName = sender.getName();
|
||||
if (!playerBooksList.containsKey(senderName)) {
|
||||
List<String> newFiles = FileHelper.listFiles(sender, false);
|
||||
playerBooksList.put(senderName, newFiles);
|
||||
} else if (sender instanceof Player player) {
|
||||
UUID playerUUID = player.getUniqueId();
|
||||
if (!playerBooksList.containsKey(playerUUID)) {
|
||||
List<String> newFiles = BookFileHelper.listFiles(sender, false);
|
||||
playerBooksList.put(playerUUID, newFiles);
|
||||
}
|
||||
return playerBooksList.get(senderName);
|
||||
return playerBooksList.get(playerUUID);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,147 +102,45 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
* @param updatePublic <p>Whether to update public books</p>
|
||||
*/
|
||||
public static void updateBooks(CommandSender sender, boolean updatePublic) {
|
||||
List<String> newFiles = FileHelper.listFiles(sender, updatePublic);
|
||||
List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
|
||||
if (updatePublic) {
|
||||
publicBooksList = newFiles;
|
||||
} else {
|
||||
playerBooksList.put(sender.getName(), newFiles);
|
||||
} else if (sender instanceof Player player) {
|
||||
playerBooksList.put(player.getUniqueId(), newFiles);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
FileConfiguration config = this.getConfig();
|
||||
config.options().copyDefaults(true);
|
||||
this.saveDefaultConfig();
|
||||
|
||||
//Get plugin info
|
||||
PluginDescriptionFile pluginDescriptionFile = this.getDescription();
|
||||
String pluginVersion = pluginDescriptionFile.getVersion();
|
||||
|
||||
booksWithoutBorders = this;
|
||||
consoleSender = this.getServer().getConsoleSender();
|
||||
playerBooksList = new HashMap<>();
|
||||
firstBooks = new ArrayList<>();
|
||||
BooksWithoutBordersSettings.initialize(this);
|
||||
publicBooksList = FileHelper.listFiles(consoleSender, true);
|
||||
BooksWithoutBordersConfig.initialize(this);
|
||||
publicBooksList = BookFileHelper.listFiles(consoleSender, true);
|
||||
|
||||
PluginManager pluginManager = this.getServer().getPluginManager();
|
||||
|
||||
if (getSlash() != null && initialize()) {
|
||||
pluginManager.registerEvents(new PlayerEventListener(), this);
|
||||
pluginManager.registerEvents(new SignEventListener(), this);
|
||||
pluginManager.registerEvents(new BookEventListener(), this);
|
||||
pluginManager.registerEvents(new BookshelfListener(), this);
|
||||
} else {
|
||||
this.getPluginLoader().disablePlugin(this);
|
||||
}
|
||||
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether only the author of a book should be able to copy it
|
||||
*
|
||||
* @return <p>Whether only the book author can copy it</p>
|
||||
*/
|
||||
public static boolean getAuthorOnlyCopy() {
|
||||
return authorOnlyCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether to use YML, not TXT, for saving books
|
||||
*
|
||||
* @return <p>Whether to use YML for saving books</p>
|
||||
*/
|
||||
public static boolean getUseYml() {
|
||||
return useYml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether admins should be able to decrypt books without a password, and decrypt all group encrypted books
|
||||
*
|
||||
* @return <p>Whether admins can bypass the encryption password</p>
|
||||
*/
|
||||
public static boolean getAdminDecrypt() {
|
||||
return adminDecrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quantity of items/currency necessary for copying books
|
||||
*
|
||||
* @param newQuantity <p>The new quantity necessary for payment</p>
|
||||
*/
|
||||
public static void setBookPriceQuantity(double newQuantity) {
|
||||
bookPriceQuantity = newQuantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity of items/currency necessary for copying books
|
||||
*
|
||||
* @return <p>The quantity necessary for payment</p>
|
||||
*/
|
||||
public static double getBookPriceQuantity() {
|
||||
return bookPriceQuantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item type used for book pricing
|
||||
*
|
||||
* <p>This item is the one a player has to pay for copying books. AIR is used to denote economy. null is used if
|
||||
* payment is disabled. Otherwise, any item can be used.</p>
|
||||
*
|
||||
* @param newType <p>The new item type to use for book pricing</p>
|
||||
*/
|
||||
public static void setBookPriceType(Material newType) {
|
||||
bookPriceType = newType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item type used for book pricing
|
||||
*
|
||||
* <p>This item is the one a player has to pay for copying books. AIR is used to denote economy. null is used if
|
||||
* payment is disabled. Otherwise, any item can be used.</p>
|
||||
*
|
||||
* @return <p>The item type used for book pricing</p>
|
||||
*/
|
||||
public static Material getBookPriceType() {
|
||||
return bookPriceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the welcome message to show to new players
|
||||
*
|
||||
* @return <p>The welcome message to show new players</p>
|
||||
*/
|
||||
public static String getWelcomeMessage() {
|
||||
return welcomeMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit of duplicates for each book
|
||||
*
|
||||
* @return <p>The book duplicate limit</p>
|
||||
*/
|
||||
public static int getBookDuplicateLimit() {
|
||||
return bookDuplicateLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the separator used to split book title from book author
|
||||
*
|
||||
* @return <p>The separator between title and author</p>
|
||||
*/
|
||||
public static String getTitleAuthorSeparator() {
|
||||
return titleAuthorSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the separator used to denote a newline in a lore string
|
||||
*
|
||||
* @return <p>The separator used to denote lore newline</p>
|
||||
*/
|
||||
public static String getLoreSeparator() {
|
||||
return loreSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the list of books to give new players
|
||||
*
|
||||
* @return <p>The books to give new players</p>
|
||||
*/
|
||||
public static List<String> getFirstBooks() {
|
||||
return new ArrayList<>(firstBooks);
|
||||
UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=96069",
|
||||
() -> pluginVersion, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,6 +175,9 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
registerCommand("loadPublicBook", new CommandLoadPublic());
|
||||
registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders());
|
||||
registerCommand("reload", new CommandReload());
|
||||
registerCommand("formatBook", new CommandFormat());
|
||||
registerCommand("setBookGeneration", new CommandSetGeneration());
|
||||
registerCommand("clearBook", new CommandClear());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -299,6 +197,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
|
||||
/**
|
||||
* Initializes the plugin, loading and fixing the config file
|
||||
*
|
||||
* @return <p>True if successful</p>
|
||||
*/
|
||||
private boolean initialize() {
|
||||
@ -313,16 +212,25 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
}
|
||||
|
||||
//Load config
|
||||
if (!loadConfig()) {
|
||||
if (!BooksWithoutBordersConfig.loadConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Save config with loaded values to fix invalid config values
|
||||
saveConfigValues();
|
||||
BooksWithoutBordersConfig.saveConfigValues();
|
||||
|
||||
return testFileSaving();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's item factory
|
||||
*
|
||||
* @return <p>The server's item factory</p>
|
||||
*/
|
||||
public static ItemFactory getItemFactory() {
|
||||
return itemFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure necessary folders exist
|
||||
*
|
||||
@ -337,8 +245,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create necessary folders");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -348,223 +256,14 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create necessary folders");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the config
|
||||
*/
|
||||
public void saveConfigValues() {
|
||||
Configuration config = this.getConfig();
|
||||
config.set("Options.Save_Books_in_Yaml_Format", useYml);
|
||||
config.set("Options.Max_Number_of_Duplicates", bookDuplicateLimit);
|
||||
config.set("Options.Title-Author_Separator", titleAuthorSeparator);
|
||||
config.set("Options.Lore_line_separator", loreSeparator);
|
||||
config.set("Options.Books_for_new_players", firstBooks);
|
||||
config.set("Options.Message_for_new_players", welcomeMessage);
|
||||
if (bookPriceType != null) {
|
||||
if (bookPriceType != Material.AIR) {
|
||||
config.set("Options.Price_to_create_book.Item_type", bookPriceType.toString());
|
||||
} else {
|
||||
config.set("Options.Price_to_create_book.Item_type", "Economy");
|
||||
}
|
||||
} else {
|
||||
config.set("Options.Price_to_create_book.Item_type", "Item type name");
|
||||
}
|
||||
config.set("Options.Price_to_create_book.Required_quantity", bookPriceQuantity);
|
||||
config.set("Options.Admin_Auto_Decrypt", adminDecrypt);
|
||||
config.set("Options.Author_Only_Copy", authorOnlyCopy);
|
||||
|
||||
//Handles old book and quill settings
|
||||
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, "Updating to \"Price_to_create_book\" settings");
|
||||
|
||||
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
||||
bookPriceType = Material.WRITABLE_BOOK;
|
||||
bookPriceQuantity = 1;
|
||||
config.set("Options.Price_to_create_book.Item_type", bookPriceType.toString());
|
||||
config.set("Options.Price_to_create_book.Required_quantity", bookPriceQuantity);
|
||||
}
|
||||
|
||||
config.set("Options.Require_book_and_quill_to_create_book", null);
|
||||
}
|
||||
this.saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config
|
||||
* @return <p>True if the config was loaded successfully</p>
|
||||
*/
|
||||
public boolean loadConfig() {
|
||||
this.reloadConfig();
|
||||
Configuration config = this.getConfig();
|
||||
try {
|
||||
useYml = config.getBoolean("Options.Save_Books_in_Yaml_Format", true);
|
||||
bookDuplicateLimit = config.getInt("Options.Max_Number_of_Duplicates", 5);
|
||||
titleAuthorSeparator = config.getString("Options.Title-Author_Separator", ",");
|
||||
loreSeparator = config.getString("Options.Lore_line_separator", "~");
|
||||
adminDecrypt = config.getBoolean("Options.Admin_Auto_Decrypt", false);
|
||||
authorOnlyCopy = config.getBoolean("Options.Author_Only_Copy", false);
|
||||
|
||||
//Set books to give new players
|
||||
firstBooks = config.getStringList("Options.Books_for_new_players");
|
||||
if (config.contains("Options.Book_for_new_players")) {
|
||||
firstBooks.add(config.getString("Options.Book_for_new_players"));
|
||||
}
|
||||
if (firstBooks.isEmpty()) {
|
||||
firstBooks.add(" ");
|
||||
}
|
||||
|
||||
welcomeMessage = config.getString("Options.Message_for_new_players", " ");
|
||||
|
||||
//Convert string into material
|
||||
String paymentMaterial = config.getString("Options.Price_to_create_book.Item_type", " ");
|
||||
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
||||
if (EconomyHelper.setupEconomy()) {
|
||||
bookPriceType = Material.AIR;
|
||||
} else {
|
||||
sendErrorMessage(consoleSender, "BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||
bookPriceType = null;
|
||||
}
|
||||
} else if (!paymentMaterial.equalsIgnoreCase(" ")) {
|
||||
Material material = Material.matchMaterial(paymentMaterial);
|
||||
if (material != null) {
|
||||
bookPriceType = material;
|
||||
}
|
||||
}
|
||||
bookPriceQuantity = config.getDouble("Options.Price_to_create_book.Required_quantity", 0);
|
||||
|
||||
//Make sure titleAuthorSeparator is a valid value
|
||||
titleAuthorSeparator = cleanString(titleAuthorSeparator);
|
||||
if (titleAuthorSeparator.length() != 1) {
|
||||
sendErrorMessage(consoleSender, "Title-Author_Separator is set to an invalid value!");
|
||||
sendErrorMessage(consoleSender, "Reverting to default value of \",\"");
|
||||
titleAuthorSeparator = ",";
|
||||
config.set("Options.Title-Author_Separator", titleAuthorSeparator);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendErrorMessage(consoleSender, "Warning! Config.yml failed to load!");
|
||||
sendErrorMessage(consoleSender, "Try Looking for settings that are missing values!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given book
|
||||
*
|
||||
* @param sender <p>The command sender trying to load the book</p>
|
||||
* @param fileName <p>The index or file name of the book to load</p>
|
||||
* @param isSigned <p>Whether to load the book as signed, and not unsigned</p>
|
||||
* @param directory <p>The directory to save the book in</p>
|
||||
* @return <p>The loaded book</p>
|
||||
*/
|
||||
public ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory) {
|
||||
return loadBook(sender, fileName, isSigned, directory, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given book
|
||||
*
|
||||
* @param sender <p>The command sender trying to load the book</p>
|
||||
* @param fileName <p>The index or file name of the book to load</p>
|
||||
* @param isSigned <p>Whether to load the book as signed, and not unsigned</p>
|
||||
* @param directory <p>The directory to save the book in</p>
|
||||
* @param numCopies <p>The number of copies to load</p>
|
||||
* @return <p>The loaded book</p>
|
||||
*/
|
||||
public ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory, int numCopies) {
|
||||
BookDirectory bookDirectory = BookDirectory.getFromString(directory);
|
||||
|
||||
//Find the filename if a book index is given
|
||||
try {
|
||||
int bookIndex = Integer.parseInt(fileName);
|
||||
List<String> availableFiles = getAvailableBooks(sender, bookDirectory == BookDirectory.PUBLIC);
|
||||
if (bookIndex <= availableFiles.size()) {
|
||||
fileName = availableFiles.get(Integer.parseInt(fileName) - 1);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
//Get the full path of the book to load
|
||||
File file = getFullPath(sender, fileName, bookDirectory, directory);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Make sure the player can pay for the book
|
||||
if (booksHavePrice() && !sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
||||
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Generate a new empty book
|
||||
ItemStack book;
|
||||
BookMeta bookMetadata = (BookMeta) itemFactory.getItemMeta(Material.WRITTEN_BOOK);
|
||||
if (isSigned.equalsIgnoreCase("true")) {
|
||||
book = new ItemStack(Material.WRITTEN_BOOK);
|
||||
} else {
|
||||
book = new ItemStack(Material.WRITABLE_BOOK);
|
||||
}
|
||||
|
||||
//Load the book from the given file
|
||||
BookToFromTextHelper.bookFromFile(file, bookMetadata);
|
||||
if (bookMetadata == null) {
|
||||
sendErrorMessage(sender, "File was blank!!");
|
||||
return null;
|
||||
}
|
||||
|
||||
//Remove "encrypted" from the book lore
|
||||
if (bookDirectory == BookDirectory.ENCRYPTED && bookMetadata.hasLore()) {
|
||||
List<String> oldLore = bookMetadata.getLore();
|
||||
if (oldLore != null) {
|
||||
List<String> newLore = new ArrayList<>(oldLore);
|
||||
newLore.remove(0);
|
||||
bookMetadata.setLore(newLore);
|
||||
}
|
||||
}
|
||||
|
||||
//Set the metadata and amount to the new book
|
||||
book.setItemMeta(bookMetadata);
|
||||
book.setAmount(numCopies);
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a File pointing to the wanted book
|
||||
*
|
||||
* @param sender <p>The sender to send errors to</p>
|
||||
* @param fileName <p>The name of the book file</p>
|
||||
* @param bookDirectory <p>The book directory the file resides in</p>
|
||||
* @param directory <p>The relative directory given</p>
|
||||
* @return <p>A file or null if it does not exist</p>
|
||||
*/
|
||||
private File getFullPath(CommandSender sender, String fileName, BookDirectory bookDirectory, String directory) {
|
||||
File file = null;
|
||||
if (bookDirectory == BookDirectory.PUBLIC) {
|
||||
file = FileHelper.getBookFile(getBookFolder() + fileName);
|
||||
} else if (bookDirectory == BookDirectory.PLAYER) {
|
||||
file = FileHelper.getBookFile(getBookFolder() + cleanString(sender.getName()) + getSlash() + fileName);
|
||||
} else if (bookDirectory == BookDirectory.ENCRYPTED) {
|
||||
file = FileHelper.getBookFile(getBookFolder() + "Encrypted" + getSlash() + directory + getSlash() + fileName);
|
||||
}
|
||||
if (file == null || !file.isFile()) {
|
||||
sendErrorMessage(sender, "Incorrect file name!");
|
||||
return null;
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a success message to a command sender (player or a console)
|
||||
*
|
||||
@ -585,13 +284,4 @@ public class BooksWithoutBorders extends JavaPlugin {
|
||||
sender.sendMessage(getErrorColor() + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether books have a price for printing them
|
||||
*
|
||||
* @return <p>True if players need to pay for printing books</p>
|
||||
*/
|
||||
public boolean booksHavePrice() {
|
||||
return (bookPriceType != null && bookPriceQuantity > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
package net.knarcraft.bookswithoutborders;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
/**
|
||||
* Class for getting various settings
|
||||
*/
|
||||
public class BooksWithoutBordersSettings {
|
||||
|
||||
//Static settings
|
||||
private static final ChatColor errorColor = ChatColor.RED;
|
||||
private static final ChatColor successColor = ChatColor.GREEN;
|
||||
private static final ChatColor commandColor = ChatColor.YELLOW;
|
||||
private static final String SLASH = System.getProperty("file.separator");
|
||||
private static boolean isInitialized;
|
||||
public static String bookFolder;
|
||||
|
||||
/**
|
||||
* Initializes the books without borders settings class
|
||||
*
|
||||
* @param booksWithoutBorders <p>The books without borders object used for getting required data</p>
|
||||
*/
|
||||
public static void initialize(BooksWithoutBorders booksWithoutBorders) {
|
||||
if (isInitialized) {
|
||||
throw new IllegalArgumentException("Settings class initialized twice. This should not happen!");
|
||||
}
|
||||
isInitialized = true;
|
||||
bookFolder = booksWithoutBorders.getDataFolder().getAbsolutePath() + getSlash() + "Books" + getSlash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the folder used for storing books
|
||||
*
|
||||
* @return <p>The folder used for storing books</p>
|
||||
*/
|
||||
public static String getBookFolder() {
|
||||
return bookFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color to use for error messages
|
||||
*
|
||||
* @return <p>The color to use for error messages</p>
|
||||
*/
|
||||
public static ChatColor getErrorColor() {
|
||||
return errorColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color to use for success messages
|
||||
*
|
||||
* @return <p>The color to use for success messages</p>
|
||||
*/
|
||||
public static ChatColor getSuccessColor() {
|
||||
return successColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color used to color commands
|
||||
*
|
||||
* @return <p>The color used to color commands</p>
|
||||
*/
|
||||
public static ChatColor getCommandColor() {
|
||||
return commandColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the correct slash to use for the used OS
|
||||
*
|
||||
* @return <p>The slash to use for file separators</p>
|
||||
*/
|
||||
public static String getSlash() {
|
||||
return SLASH;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
@ -8,25 +9,25 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.sendErrorMessage;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getCommandColor;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSuccessColor;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSuccessColor;
|
||||
|
||||
/**
|
||||
* Command executor for the books without borders (bwb) command
|
||||
*/
|
||||
public class CommandBooksWithoutBorders implements TabExecutor {
|
||||
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
sender.sendMessage(getCommandColor() + "[] denote optional parameters");
|
||||
sender.sendMessage(getCommandColor() + "<> denote required parameters");
|
||||
sender.sendMessage(getCommandColor() + "{} denote required permission");
|
||||
sender.sendMessage(getCommandColor() + "In some cases, commands with required parameters can be called with no parameters");
|
||||
if (sender instanceof Player) {
|
||||
showPlayerCommands(sender);
|
||||
@ -43,9 +44,9 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
||||
*/
|
||||
private void showConsoleCommands(CommandSender sender) {
|
||||
sender.sendMessage(getCommandColor() + "Commands:");
|
||||
showCommandInfo("reload", sender);
|
||||
showCommandInfo("givePublicBook", sender);
|
||||
showCommandInfo("deletePublicBook", sender);
|
||||
showCommandInfo("givePublicBook", sender);
|
||||
showCommandInfo("reload", sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,9 +56,9 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
||||
*/
|
||||
private void showPlayerCommands(CommandSender sender) {
|
||||
//Lists all commands
|
||||
Material bookPriceType = BooksWithoutBorders.getBookPriceType();
|
||||
double bookPriceQuantity = BooksWithoutBorders.getBookPriceQuantity();
|
||||
if (booksWithoutBorders.booksHavePrice()) {
|
||||
Material bookPriceType = BooksWithoutBordersConfig.getBookPriceType();
|
||||
double bookPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
|
||||
if (BooksWithoutBordersConfig.booksHavePrice()) {
|
||||
if (bookPriceType != Material.AIR) {
|
||||
sendErrorMessage(sender, "[" + (int) bookPriceQuantity + " " + bookPriceType.toString() +
|
||||
"(s) are required to create a book]");
|
||||
@ -68,24 +69,26 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
||||
}
|
||||
sender.sendMessage(getCommandColor() + "Commands:");
|
||||
|
||||
showCommandInfo("loadBook", sender);
|
||||
showCommandInfo("loadPublicBook", sender);
|
||||
showCommandInfo("saveBook", sender);
|
||||
showCommandInfo("savePublicBook", sender);
|
||||
showCommandInfo("giveBook", sender);
|
||||
showCommandInfo("givePublicBook", sender);
|
||||
showCommandInfo("copyBook", sender);
|
||||
showCommandInfo("decryptBook", sender);
|
||||
showCommandInfo("deleteBook", sender);
|
||||
showCommandInfo("deletePublicBook", sender);
|
||||
showCommandInfo("unsignBook", sender);
|
||||
showCommandInfo("copyBook", sender);
|
||||
showCommandInfo("encryptBook", sender);
|
||||
showCommandInfo("formatBook", sender);
|
||||
showCommandInfo("giveBook", sender);
|
||||
showCommandInfo("givePublicBook", sender);
|
||||
showCommandInfo("groupEncryptBook", sender);
|
||||
showCommandInfo("decryptBook", sender);
|
||||
showCommandInfo("setTitle", sender);
|
||||
showCommandInfo("setAuthor", sender);
|
||||
showCommandInfo("setLore", sender);
|
||||
showCommandInfo("setBookPrice", sender);
|
||||
showCommandInfo("loadBook", sender);
|
||||
showCommandInfo("loadPublicBook", sender);
|
||||
showCommandInfo("reload", sender);
|
||||
showCommandInfo("saveBook", sender);
|
||||
showCommandInfo("savePublicBook", sender);
|
||||
showCommandInfo("setAuthor", sender);
|
||||
showCommandInfo("setBookGeneration", sender);
|
||||
showCommandInfo("setBookPrice", sender);
|
||||
showCommandInfo("setLore", sender);
|
||||
showCommandInfo("setTitle", sender);
|
||||
showCommandInfo("unsignBook", sender);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,15 +102,22 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
||||
if (pluginCommand != null) {
|
||||
String permission = pluginCommand.getPermission();
|
||||
if (permission == null || sender.hasPermission(permission)) {
|
||||
sender.sendMessage("\n" + getCommandColor() +
|
||||
pluginCommand.getUsage().replace("<command>", pluginCommand.getName()) + ": " +
|
||||
getSuccessColor() + pluginCommand.getDescription());
|
||||
String commandInfo = "\n" + getCommandColor() + pluginCommand.getUsage().replace("<command>",
|
||||
pluginCommand.getName()) + ": " + getSuccessColor() + pluginCommand.getDescription();
|
||||
if (sender.hasPermission("bookswithoutborders.admin")) {
|
||||
if (permission == null) {
|
||||
permission = "None";
|
||||
}
|
||||
commandInfo += getCommandColor() + " {" + permission + "}";
|
||||
}
|
||||
sender.sendMessage(commandInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<>();
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +1,20 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
import org.bukkit.Material;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -21,10 +25,8 @@ import java.util.Objects;
|
||||
*/
|
||||
public class CommandCopy implements TabExecutor {
|
||||
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -40,57 +42,110 @@ public class CommandCopy implements TabExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||
|
||||
try {
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||
int copies = Integer.parseInt(args[0]);
|
||||
if (copies > 0) {
|
||||
if (BooksWithoutBorders.getAuthorOnlyCopy() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
||||
if (!isAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta())))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (booksWithoutBorders.booksHavePrice() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
EconomyHelper.cannotPayForBookPrinting(player, copies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
heldBook.setAmount(heldBook.getAmount() + copies);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||
return true;
|
||||
if (copies <= 0) {
|
||||
throw new NumberFormatException("Number of copies must be larger than 0");
|
||||
}
|
||||
return performCopy(copies, player, heldBook);
|
||||
} catch (NumberFormatException ignored) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
||||
return false;
|
||||
}
|
||||
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
|
||||
* Performs the actual copying
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param book <p>The book to check</p>
|
||||
* @return <p>True if the player is the book's author</p>
|
||||
* @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 isAuthor(Player player, BookMeta book) {
|
||||
String author = book.getAuthor();
|
||||
String playerName = InputCleaningHelper.cleanString(player.getName());
|
||||
if (author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author))) {
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
BooksWithoutBorders.sendErrorMessage(player, "You must be the author of this book to use this command!");
|
||||
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;
|
||||
}
|
||||
|
||||
heldBook.setAmount(heldBook.getAmount() + copies);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to take payment from a player
|
||||
*
|
||||
* @param player <p>The player to take the payment from</p>
|
||||
* @param copies <p>The number of copies to create for the player</p>
|
||||
* @return <p>True if the payment failed</p>
|
||||
*/
|
||||
private boolean paymentUnSuccessful(Player player, int copies) {
|
||||
return BooksWithoutBordersConfig.booksHavePrice() &&
|
||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
EconomyHelper.cannotPayForBookPrinting(player, copies);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||
@NotNull String[] args) {
|
||||
int argumentCount = args.length;
|
||||
if (argumentCount == 1) {
|
||||
return TabCompletionHelper.getNumbers(1, 20);
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 20), args[0]);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.command.Command;
|
||||
@ -9,13 +11,14 @@ 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 java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||
|
||||
/**
|
||||
* Command executor for the decrypt command
|
||||
@ -23,7 +26,7 @@ import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getS
|
||||
public class CommandDecrypt implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -43,14 +46,8 @@ public class CommandDecrypt implements TabExecutor {
|
||||
}
|
||||
|
||||
//Warning: admin decrypt only allows decrypting files created by the same player. Not sure if intended
|
||||
if (args.length == 0 && BooksWithoutBorders.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
||||
if (args.length == 0 && BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||
String fileName;
|
||||
if (bookMetadata.hasTitle()) {
|
||||
fileName = bookMetadata.getTitle() + BooksWithoutBorders.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
||||
} else {
|
||||
fileName = "Untitled," + player.getName();
|
||||
}
|
||||
|
||||
File encryptedDirectory = new File(path);
|
||||
String[] encryptedFiles = encryptedDirectory.list();
|
||||
@ -62,7 +59,7 @@ public class CommandDecrypt implements TabExecutor {
|
||||
//Get the "encryption key" from the filename
|
||||
String key = "";
|
||||
for (String encryptedFile : encryptedFiles) {
|
||||
if (encryptedFile.contains(fileName)) {
|
||||
if (encryptedFile.contains(BookHelper.getBookFile(bookMetadata, player, true))) {
|
||||
key = encryptedFile.substring(encryptedFile.indexOf("[") + 1, encryptedFile.indexOf("]"));
|
||||
break;
|
||||
}
|
||||
@ -102,7 +99,7 @@ public class CommandDecrypt implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
int argumentCount = args.length;
|
||||
if (argumentCount == 1) {
|
||||
List<String> info = new ArrayList<>();
|
||||
@ -111,4 +108,5 @@ public class CommandDecrypt implements TabExecutor {
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
|
||||
/**
|
||||
* Command executor for the delete command
|
||||
*/
|
||||
public class CommandDelete implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||
return false;
|
||||
@ -41,7 +41,7 @@ public class CommandDelete implements TabExecutor {
|
||||
boolean deleteBook(CommandSender sender, String[] args, boolean deletePublic) {
|
||||
//List deletable files
|
||||
if (args.length == 0) {
|
||||
FileHelper.printBooks(sender, deletePublic);
|
||||
BookFileHelper.printBooks(sender, deletePublic);
|
||||
return true;
|
||||
}
|
||||
//Delete the file
|
||||
@ -80,13 +80,9 @@ public class CommandDelete implements TabExecutor {
|
||||
}
|
||||
|
||||
//Get the file to be deleted
|
||||
File file;
|
||||
if (isPublic) {
|
||||
file = FileHelper.getBookFile(getBookFolder() + fileName);
|
||||
} else {
|
||||
file = FileHelper.getBookFile(getBookFolder() +
|
||||
InputCleaningHelper.cleanString(sender.getName()) + getSlash() + fileName);
|
||||
}
|
||||
String bookDirectory = BookHelper.getBookDirectoryPathString(
|
||||
isPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||
File file = BookFileHelper.getBookFile(bookDirectory + fileName);
|
||||
|
||||
//Send message if no such file could be found
|
||||
if (file == null) {
|
||||
@ -107,21 +103,23 @@ public class CommandDelete implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return doTabCompletion(sender, args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs tab completion
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
*
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
* @param deletePublic <p>Whether to delete a public book</p>
|
||||
* @return <p>A list of available arguments</p>
|
||||
*/
|
||||
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean deletePublic) {
|
||||
int argumentCount = args.length;
|
||||
if (argumentCount == 1) {
|
||||
return BooksWithoutBorders.getAvailableBooks(sender, deletePublic);
|
||||
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic),
|
||||
args[0]);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.command;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,12 +13,12 @@ import java.util.List;
|
||||
public class CommandDeletePublic extends CommandDelete implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
return deleteBook(sender, args, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
return doTabCompletion(sender, args, true);
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,14 @@ import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
|
||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -21,7 +23,7 @@ import java.util.List;
|
||||
public class CommandEncrypt implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (performPreChecks(sender, args, 1, "You must specify a key to encrypt a book!") == null) {
|
||||
return false;
|
||||
}
|
||||
@ -96,13 +98,14 @@ public class CommandEncrypt implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
return doTabCompletion(args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of string for tab completions
|
||||
* @param args <p>The arguments given</p>
|
||||
*
|
||||
* @param args <p>The arguments given</p>
|
||||
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
|
||||
* @return <p>The strings to auto-complete</p>
|
||||
*/
|
||||
@ -123,12 +126,13 @@ public class CommandEncrypt implements TabExecutor {
|
||||
info.add("<group>");
|
||||
return info;
|
||||
} else {
|
||||
return encryptionStyles;
|
||||
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[1]);
|
||||
}
|
||||
} else if (argumentsCount == 3 && groupEncrypt) {
|
||||
return encryptionStyles;
|
||||
return TabCompletionHelper.filterMatchingStartsWith(encryptionStyles, args[2]);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A command for converting color codes to color formatting
|
||||
*/
|
||||
public class CommandFormat 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 format it!",
|
||||
"You cannot format two books at once!")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||
heldBook.setItemMeta(BookFormatter.formatPages((BookMeta) heldBook.getItemMeta()));
|
||||
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book formatted!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +1,18 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
import org.bukkit.Server;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -22,7 +25,7 @@ public class CommandGive implements TabExecutor {
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||
return false;
|
||||
@ -47,7 +50,7 @@ public class CommandGive implements TabExecutor {
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
FileHelper.printBooks(sender, givePublic);
|
||||
BookFileHelper.printBooks(sender, givePublic);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -67,6 +70,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
|
||||
try {
|
||||
Integer.parseInt(bookIdentifier);
|
||||
@ -74,29 +91,8 @@ public class CommandGive implements TabExecutor {
|
||||
} 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 {
|
||||
ItemStack newBook = booksWithoutBorders.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(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;
|
||||
}
|
||||
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, folder, copies);
|
||||
} catch (NumberFormatException e) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
||||
return false;
|
||||
@ -104,14 +100,15 @@ public class CommandGive implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return doTabCompletion(sender, args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual tab completion
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
*
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
* @param listPublic <p>Whether to list public files or player files</p>
|
||||
* @return <p>A list of available choices</p>
|
||||
*/
|
||||
@ -129,22 +126,51 @@ public class CommandGive implements TabExecutor {
|
||||
|
||||
if (argumentCount == 1) {
|
||||
//Return list of books
|
||||
return BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
||||
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, listPublic),
|
||||
args[0]);
|
||||
} else if (argumentCount == 2) {
|
||||
//Return online players
|
||||
return null;
|
||||
} else if (argumentCount == 3) {
|
||||
//Number of copies
|
||||
return TabCompletionHelper.getBooleansAndNumbers(1, 3);
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), args[2]);
|
||||
} else if (argumentCount == 4) {
|
||||
//Signed
|
||||
try {
|
||||
Integer.parseInt(args[2]);
|
||||
return TabCompletionHelper.getBooleans();
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), args[3]);
|
||||
} catch (NumberFormatException e) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.command;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,12 +13,12 @@ import java.util.List;
|
||||
public class CommandGivePublic extends CommandGive implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
return giveBook(sender, args, true, "public");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
return doTabCompletion(sender, args, true);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -16,7 +17,7 @@ import java.util.List;
|
||||
public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
BookMeta bookMetadata = performPreChecks(sender, args, 2,
|
||||
"You must specify a group name and key to encrypt a book!");
|
||||
|
||||
@ -36,7 +37,7 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
return doTabCompletion(args, true);
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,17 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -18,10 +21,8 @@ import java.util.List;
|
||||
*/
|
||||
public class CommandLoad implements TabExecutor {
|
||||
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return loadBook(sender, args, "player", false);
|
||||
}
|
||||
|
||||
@ -49,7 +50,7 @@ public class CommandLoad implements TabExecutor {
|
||||
|
||||
//Show books available to the player
|
||||
if (argumentCount == 0) {
|
||||
FileHelper.printBooks(sender, loadPublic);
|
||||
BookFileHelper.printBooks(sender, loadPublic);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@ public class CommandLoad implements TabExecutor {
|
||||
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
||||
try {
|
||||
//Give the new book if it can be loaded
|
||||
ItemStack newBook = booksWithoutBorders.loadBook(player, bookToLoad, isSigned, directory, Integer.parseInt(copies));
|
||||
ItemStack newBook = BookLoader.loadBook(player, bookToLoad, isSigned, directory, Integer.parseInt(copies));
|
||||
if (newBook != null) {
|
||||
player.getInventory().addItem(newBook);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book created!");
|
||||
@ -94,14 +95,15 @@ public class CommandLoad implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return doTabCompletion(sender, args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual tab completion
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
*
|
||||
* @param sender <p>The sender of the command</p>
|
||||
* @param args <p>The arguments given</p>
|
||||
* @param loadPublic <p>Whether to list public files or player files</p>
|
||||
* @return <p>A list of available choices</p>
|
||||
*/
|
||||
@ -109,19 +111,21 @@ public class CommandLoad implements TabExecutor {
|
||||
int argumentCount = args.length;
|
||||
if (argumentCount == 1) {
|
||||
//Return list of books
|
||||
return BooksWithoutBorders.getAvailableBooks(sender, loadPublic);
|
||||
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic),
|
||||
args[0]);
|
||||
} else if (argumentCount == 2) {
|
||||
//Number of copies
|
||||
return TabCompletionHelper.getBooleansAndNumbers(1, 3);
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), args[1]);
|
||||
} else if (argumentCount == 3) {
|
||||
//Signed
|
||||
try {
|
||||
Integer.parseInt(args[1]);
|
||||
return TabCompletionHelper.getBooleans();
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), args[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.command;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,12 +13,12 @@ import java.util.List;
|
||||
public class CommandLoadPublic extends CommandLoad implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
return loadBook(sender, args, "public", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
return doTabCompletion(sender, args, true);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -13,11 +15,9 @@ import java.util.List;
|
||||
*/
|
||||
public class CommandReload implements TabExecutor {
|
||||
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (booksWithoutBorders.loadConfig()) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (BooksWithoutBordersConfig.loadConfig()) {
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!");
|
||||
} else {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!");
|
||||
@ -27,7 +27,8 @@ public class CommandReload implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -11,19 +14,17 @@ 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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.getTitleAuthorSeparator;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getCommandColor;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getErrorColor;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getTitleAuthorSeparator;
|
||||
|
||||
/**
|
||||
* Command executor for the save command
|
||||
@ -31,7 +32,7 @@ import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixN
|
||||
public class CommandSave implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
return saveHeldBook(sender, args, false);
|
||||
}
|
||||
|
||||
@ -50,7 +51,7 @@ public class CommandSave implements TabExecutor {
|
||||
}
|
||||
|
||||
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);
|
||||
boolean duplicate = args.length == 1 && Boolean.parseBoolean(args[0]);
|
||||
saveBook(player, holdingItem, duplicate, savePublic);
|
||||
@ -76,63 +77,64 @@ public class CommandSave implements TabExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
String savePath;
|
||||
if (saveToPublicFolder) {
|
||||
savePath = getBookFolder();
|
||||
} else {
|
||||
savePath = getBookFolder() + cleanString(player.getName()) + getSlash();
|
||||
//Only allow saving of own books if enabled
|
||||
if (BooksWithoutBordersConfig.getAuthorOnlySave() && !saveToPublicFolder &&
|
||||
(!player.hasPermission("bookswithoutborders.bypassAuthorOnlySave") &&
|
||||
BookHelper.isNotAuthor(player, book))) {
|
||||
return;
|
||||
}
|
||||
|
||||
String savePath = BookHelper.getBookDirectoryPathString(
|
||||
saveToPublicFolder ? BookDirectory.PUBLIC : BookDirectory.PLAYER, player);
|
||||
|
||||
//Generate book filename
|
||||
String fileName;
|
||||
if (!book.hasTitle()) {
|
||||
fileName = "Untitled," + player.getName();
|
||||
} else {
|
||||
fileName = book.getTitle() + getTitleAuthorSeparator() + book.getAuthor();
|
||||
}
|
||||
fileName = cleanString(fileName);
|
||||
fileName = fixName(fileName, false);
|
||||
String fileName = BookHelper.getBookFile(book, player, saveToPublicFolder);
|
||||
|
||||
//Make sure the used folders exist
|
||||
File file = new File(savePath);
|
||||
if (!file.exists() && !file.mkdir()) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||
" a server admin!");
|
||||
return;
|
||||
}
|
||||
File[] foundFiles = file.listFiles();
|
||||
if (foundFiles == null) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||
" a server admin!");
|
||||
return;
|
||||
}
|
||||
|
||||
//Find any duplicates of the book
|
||||
int foundDuplicates = FileHelper.findDuplicates(foundFiles, fileName);
|
||||
int foundDuplicates = BookFileHelper.findDuplicates(foundFiles, fileName);
|
||||
|
||||
//Deal with duplicates
|
||||
if (foundDuplicates > 0) {
|
||||
//TODO: Decide if this makes sense or needs to be changed
|
||||
//Skip duplicate book
|
||||
if (!fileName.contains("Untitled") && !overwrite) {
|
||||
if (!fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||
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;
|
||||
}
|
||||
|
||||
//Skip if duplicate limit is reached
|
||||
if (foundDuplicates > BooksWithoutBorders.getBookDuplicateLimit()) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName + " duplicates reached!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " + getErrorColor() + "to overwrite!");
|
||||
if (foundDuplicates > BooksWithoutBordersConfig.getBookDuplicateLimit()) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName +
|
||||
" duplicates reached!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " +
|
||||
getErrorColor() + "to overwrite!");
|
||||
return;
|
||||
}
|
||||
|
||||
//Alter duplicated filename
|
||||
if (fileName.contains("Untitled") && !overwrite) {
|
||||
if (fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||
fileName = "(" + foundDuplicates + ")" + fileName;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (BooksWithoutBorders.getUseYml()) {
|
||||
if (BooksWithoutBordersConfig.getUseYml()) {
|
||||
BookToFromTextHelper.bookToYml(savePath, fileName, book);
|
||||
} else {
|
||||
BookToFromTextHelper.bookToTXT(savePath, fileName, book);
|
||||
@ -141,14 +143,16 @@ public class CommandSave implements TabExecutor {
|
||||
//Update the relevant book list
|
||||
BooksWithoutBorders.updateBooks(player, saveToPublicFolder);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + "\"");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save book");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||
@NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.command;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Command executor for the save public command
|
||||
@ -10,7 +11,7 @@ import org.bukkit.command.TabExecutor;
|
||||
public class CommandSavePublic extends CommandSave implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return saveHeldBook(sender, args, true);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -19,7 +20,7 @@ import java.util.List;
|
||||
public class CommandSetAuthor implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -49,11 +50,12 @@ public class CommandSetAuthor implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return null;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
|
||||
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||
import org.bukkit.Material;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -20,9 +23,10 @@ import java.util.List;
|
||||
public class CommandSetBookPrice implements TabExecutor {
|
||||
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
private List<String> paymentTypes;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
//Clear the current price
|
||||
if (args.length == 0) {
|
||||
clearItemPrice(sender);
|
||||
@ -65,10 +69,10 @@ public class CommandSetBookPrice implements TabExecutor {
|
||||
* @param sender <p>The sender of the command</p>
|
||||
*/
|
||||
private void clearItemPrice(CommandSender sender) {
|
||||
BooksWithoutBorders.setBookPriceType(null);
|
||||
BooksWithoutBorders.setBookPriceQuantity(0);
|
||||
BooksWithoutBordersConfig.setBookPriceType(null);
|
||||
BooksWithoutBordersConfig.setBookPriceQuantity(0);
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Item type name");
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Required_quantity", BooksWithoutBorders.getBookPriceQuantity());
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Required_quantity", BooksWithoutBordersConfig.getBookPriceQuantity());
|
||||
booksWithoutBorders.saveConfig();
|
||||
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "Price to create books removed!");
|
||||
@ -93,10 +97,10 @@ public class CommandSetBookPrice implements TabExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
BooksWithoutBorders.setBookPriceType(heldItem.getType());
|
||||
BooksWithoutBorders.setBookPriceQuantity(price);
|
||||
String newPriceType = BooksWithoutBorders.getBookPriceType().toString();
|
||||
double newPriceQuantity = BooksWithoutBorders.getBookPriceQuantity();
|
||||
BooksWithoutBordersConfig.setBookPriceType(heldItem.getType());
|
||||
BooksWithoutBordersConfig.setBookPriceQuantity(price);
|
||||
String newPriceType = BooksWithoutBordersConfig.getBookPriceType().toString();
|
||||
double newPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", newPriceType);
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Required_quantity", newPriceQuantity);
|
||||
booksWithoutBorders.saveConfig();
|
||||
@ -115,9 +119,9 @@ public class CommandSetBookPrice implements TabExecutor {
|
||||
*/
|
||||
private boolean setEconomyPrice(CommandSender sender, double price) {
|
||||
if (EconomyHelper.setupEconomy()) {
|
||||
BooksWithoutBorders.setBookPriceQuantity(price);
|
||||
BooksWithoutBorders.setBookPriceType(Material.AIR);
|
||||
double newPriceQuantity = BooksWithoutBorders.getBookPriceQuantity();
|
||||
BooksWithoutBordersConfig.setBookPriceQuantity(price);
|
||||
BooksWithoutBordersConfig.setBookPriceType(Material.AIR);
|
||||
double newPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Economy");
|
||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Required_quantity", newPriceQuantity);
|
||||
booksWithoutBorders.saveConfig();
|
||||
@ -132,16 +136,27 @@ public class CommandSetBookPrice implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
if (paymentTypes == null) {
|
||||
initializeTabCompleteLists();
|
||||
}
|
||||
|
||||
int argumentCount = args.length;
|
||||
if (argumentCount == 1) {
|
||||
List<String> paymentTypes = new ArrayList<>();
|
||||
paymentTypes.add("item");
|
||||
paymentTypes.add("eco");
|
||||
return paymentTypes;
|
||||
return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]);
|
||||
} else if (argumentCount == 2) {
|
||||
return TabCompletionHelper.getNumbers(1, 3);
|
||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 3), args[1]);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the lists of tab complete values
|
||||
*/
|
||||
private void initializeTabCompleteLists() {
|
||||
paymentTypes = new ArrayList<>();
|
||||
paymentTypes.add("item");
|
||||
paymentTypes.add("eco");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,8 +1,10 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.ChatColor;
|
||||
import net.knarcraft.knarlib.property.ColorConversion;
|
||||
import net.knarcraft.knarlib.util.ColorHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -10,6 +12,7 @@ import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -21,7 +24,7 @@ import java.util.List;
|
||||
public class CommandSetLore implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -42,8 +45,8 @@ public class CommandSetLore implements TabExecutor {
|
||||
String rawLore = String.join(" ", args);
|
||||
|
||||
//Format lore
|
||||
rawLore = ChatColor.translateAlternateColorCodes('&', rawLore);
|
||||
String[] loreParts = rawLore.split(BooksWithoutBorders.getLoreSeparator());
|
||||
rawLore = ColorHelper.translateColorCodes(rawLore, ColorConversion.RGB);
|
||||
String[] loreParts = rawLore.split(BooksWithoutBordersConfig.getLoreSeparator());
|
||||
List<String> newLore = new ArrayList<>(Arrays.asList(loreParts));
|
||||
|
||||
//Update lore
|
||||
@ -59,10 +62,11 @@ public class CommandSetLore implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
//TODO: Figure out if there is a better way to display that an argument is required
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add("<new lore>");
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.ChatColor;
|
||||
import net.knarcraft.knarlib.property.ColorConversion;
|
||||
import net.knarcraft.knarlib.util.ColorHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -11,6 +12,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -21,7 +23,8 @@ import java.util.List;
|
||||
public class CommandSetTitle implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -39,7 +42,7 @@ public class CommandSetTitle implements TabExecutor {
|
||||
}
|
||||
|
||||
String title = String.join(" ", args);
|
||||
title = ChatColor.translateAlternateColorCodes('&', title);
|
||||
title = ColorHelper.translateColorCodes(title, ColorConversion.RGB);
|
||||
|
||||
ItemMeta itemMetadata = heldItem.getItemMeta();
|
||||
if (itemMetadata == null) {
|
||||
@ -65,9 +68,11 @@ public class CommandSetTitle implements TabExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||
@NotNull String[] args) {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.add("<new title>");
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.knarcraft.bookswithoutborders.command;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
@ -10,9 +12,13 @@ import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.WritableBookMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Command executor for the unsign command
|
||||
@ -20,7 +26,7 @@ import java.util.List;
|
||||
public class CommandUnSign implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
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;
|
||||
@ -45,17 +51,42 @@ public class CommandUnSign implements TabExecutor {
|
||||
*/
|
||||
public void unSignHeldBook(Player player, boolean mainHand) {
|
||||
//Get the old book
|
||||
BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||
BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand);
|
||||
|
||||
//UnSign the book
|
||||
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK);
|
||||
newBook.setItemMeta(oldBook);
|
||||
//Only allow the owner to un-sign the book
|
||||
if (BooksWithoutBordersConfig.getAuthorOnlyUnsign() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) {
|
||||
if (BookHelper.isNotAuthor(player, Objects.requireNonNull(oldMetadata))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WritableBookMeta newMetadata = (BookMeta) BooksWithoutBorders.getItemFactory().getItemMeta(Material.WRITABLE_BOOK);
|
||||
if (newMetadata == null) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create writable book metadata");
|
||||
return;
|
||||
}
|
||||
|
||||
//Create a new unsigned book with the same data
|
||||
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK, heldBook.getAmount());
|
||||
if (oldMetadata.hasLore()) {
|
||||
List<String> oldLore = oldMetadata.getLore();
|
||||
if (oldLore != null) {
|
||||
List<String> newLore = new ArrayList<>(oldLore);
|
||||
newLore.remove(0);
|
||||
newMetadata.setLore(newLore);
|
||||
}
|
||||
}
|
||||
newMetadata.setPages(oldMetadata.getPages());
|
||||
newBook.setItemMeta(newMetadata);
|
||||
|
||||
// Give the player the new book
|
||||
InventoryHelper.replaceHeldItem(player, newBook, mainHand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,406 @@
|
||||
package net.knarcraft.bookswithoutborders.config;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.sendErrorMessage;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBorders.sendSuccessMessage;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
|
||||
/**
|
||||
* A config class that keeps track of all config values
|
||||
*/
|
||||
public class BooksWithoutBordersConfig {
|
||||
|
||||
private static final ChatColor errorColor = ChatColor.RED;
|
||||
private static final ChatColor successColor = ChatColor.GREEN;
|
||||
private static final ChatColor commandColor = ChatColor.YELLOW;
|
||||
private static final String SLASH = FileSystems.getDefault().getSeparator();
|
||||
private static boolean isInitialized;
|
||||
public static String bookFolder;
|
||||
|
||||
private static int bookDuplicateLimit;
|
||||
private static String titleAuthorSeparator;
|
||||
private static String loreSeparator;
|
||||
private static List<String> firstBooks = new ArrayList<>();
|
||||
private static String welcomeMessage;
|
||||
private static Material bookPriceType = null;
|
||||
private static double bookPriceQuantity;
|
||||
private static boolean authorOnlyCopy;
|
||||
private static boolean authorOnlyUnsign;
|
||||
private static boolean authorOnlySave;
|
||||
private static boolean useYml;
|
||||
private static boolean adminDecrypt;
|
||||
private static boolean formatBooks;
|
||||
private static boolean changeGenerationOnCopy;
|
||||
private static boolean enableBookshelfPeeking;
|
||||
|
||||
/**
|
||||
* Initializes the books without borders settings class
|
||||
*
|
||||
* @param booksWithoutBorders <p>The books without borders object used for getting required data</p>
|
||||
*/
|
||||
public static void initialize(BooksWithoutBorders booksWithoutBorders) {
|
||||
if (isInitialized) {
|
||||
throw new IllegalArgumentException("Settings class initialized twice. This should not happen!");
|
||||
}
|
||||
isInitialized = true;
|
||||
bookFolder = booksWithoutBorders.getDataFolder().getAbsolutePath() + getSlash() + "Books" + getSlash();
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the folder used for storing books
|
||||
*
|
||||
* @return <p>The folder used for storing books</p>
|
||||
*/
|
||||
public static String getBookFolder() {
|
||||
return bookFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color to use for error messages
|
||||
*
|
||||
* @return <p>The color to use for error messages</p>
|
||||
*/
|
||||
public static ChatColor getErrorColor() {
|
||||
return errorColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color to use for success messages
|
||||
*
|
||||
* @return <p>The color to use for success messages</p>
|
||||
*/
|
||||
public static ChatColor getSuccessColor() {
|
||||
return successColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color used to color commands
|
||||
*
|
||||
* @return <p>The color used to color commands</p>
|
||||
*/
|
||||
public static ChatColor getCommandColor() {
|
||||
return commandColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the correct slash to use for the used OS
|
||||
*
|
||||
* @return <p>The slash to use for file separators</p>
|
||||
*/
|
||||
public static String getSlash() {
|
||||
return SLASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether only the author of a book should be able to copy it
|
||||
*
|
||||
* @return <p>Whether only the book author can copy it</p>
|
||||
*/
|
||||
public static boolean getAuthorOnlyCopy() {
|
||||
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 players can left-click a bookshelf to peek at the contained books
|
||||
*
|
||||
* @return <p>True if players can peek at the contained books</p>
|
||||
*/
|
||||
public static boolean getEnableBookshelfPeeking() {
|
||||
return enableBookshelfPeeking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether to use YML, not TXT, for saving books
|
||||
*
|
||||
* @return <p>Whether to use YML for saving books</p>
|
||||
*/
|
||||
public static boolean getUseYml() {
|
||||
return useYml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether admins should be able to decrypt books without a password, and decrypt all group encrypted books
|
||||
*
|
||||
* @return <p>Whether admins can bypass the encryption password</p>
|
||||
*/
|
||||
public static boolean getAdminDecrypt() {
|
||||
return adminDecrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quantity of items/currency necessary for copying books
|
||||
*
|
||||
* @param newQuantity <p>The new quantity necessary for payment</p>
|
||||
*/
|
||||
public static void setBookPriceQuantity(double newQuantity) {
|
||||
bookPriceQuantity = newQuantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quantity of items/currency necessary for copying books
|
||||
*
|
||||
* @return <p>The quantity necessary for payment</p>
|
||||
*/
|
||||
public static double getBookPriceQuantity() {
|
||||
return bookPriceQuantity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item type used for book pricing
|
||||
*
|
||||
* <p>This item is the one a player has to pay for copying books. AIR is used to denote economy. null is used if
|
||||
* payment is disabled. Otherwise, any item can be used.</p>
|
||||
*
|
||||
* @param newType <p>The new item type to use for book pricing</p>
|
||||
*/
|
||||
public static void setBookPriceType(Material newType) {
|
||||
bookPriceType = newType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item type used for book pricing
|
||||
*
|
||||
* <p>This item is the one a player has to pay for copying books. AIR is used to denote economy. null is used if
|
||||
* payment is disabled. Otherwise, any item can be used.</p>
|
||||
*
|
||||
* @return <p>The item type used for book pricing</p>
|
||||
*/
|
||||
public static Material getBookPriceType() {
|
||||
return bookPriceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the welcome message to show to new players
|
||||
*
|
||||
* @return <p>The welcome message to show new players</p>
|
||||
*/
|
||||
public static String getWelcomeMessage() {
|
||||
return welcomeMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit of duplicates for each book
|
||||
*
|
||||
* @return <p>The book duplicate limit</p>
|
||||
*/
|
||||
public static int getBookDuplicateLimit() {
|
||||
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
|
||||
*
|
||||
* @return <p>The separator between title and author</p>
|
||||
*/
|
||||
public static String getTitleAuthorSeparator() {
|
||||
return titleAuthorSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the separator used to denote a newline in a lore string
|
||||
*
|
||||
* @return <p>The separator used to denote lore newline</p>
|
||||
*/
|
||||
public static String getLoreSeparator() {
|
||||
return loreSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether all books should be formatted when they are signed
|
||||
*
|
||||
* @return <p>Whether all books should be formatted</p>
|
||||
*/
|
||||
public static boolean formatBooks() {
|
||||
return formatBooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the list of books to give new players
|
||||
*
|
||||
* @return <p>The books to give new players</p>
|
||||
*/
|
||||
public static List<String> getFirstBooks() {
|
||||
return new ArrayList<>(firstBooks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether books have a price for printing them
|
||||
*
|
||||
* @return <p>True if players need to pay for printing books</p>
|
||||
*/
|
||||
public static boolean booksHavePrice() {
|
||||
return (bookPriceType != null && bookPriceQuantity > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the config
|
||||
*/
|
||||
public static void saveConfigValues() {
|
||||
ConsoleCommandSender consoleSender = BooksWithoutBorders.getInstance().getServer().getConsoleSender();
|
||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||
config.set(ConfigOption.USE_YAML.getConfigNode(), useYml);
|
||||
config.set(ConfigOption.MAX_DUPLICATES.getConfigNode(), bookDuplicateLimit);
|
||||
config.set(ConfigOption.TITLE_AUTHOR_SEPARATOR.getConfigNode(), titleAuthorSeparator);
|
||||
config.set(ConfigOption.LORE_LINE_SEPARATOR.getConfigNode(), loreSeparator);
|
||||
config.set(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode(), firstBooks);
|
||||
config.set(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(), welcomeMessage);
|
||||
config.set(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(), formatBooks);
|
||||
config.set(ConfigOption.ENABLE_BOOKSHELF_PEEKING.getConfigNode(), enableBookshelfPeeking);
|
||||
|
||||
String itemTypeNode = ConfigOption.PRICE_ITEM_TYPE.getConfigNode();
|
||||
if (bookPriceType != null) {
|
||||
if (bookPriceType != Material.AIR) {
|
||||
config.set(itemTypeNode, bookPriceType.toString());
|
||||
} else {
|
||||
config.set(itemTypeNode, "Economy");
|
||||
}
|
||||
} else {
|
||||
config.set(itemTypeNode, "Item type name");
|
||||
}
|
||||
|
||||
config.set(ConfigOption.PRICE_QUANTITY.getConfigNode(), bookPriceQuantity);
|
||||
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
||||
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
|
||||
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, "Updating to \"Price_to_create_book\" settings");
|
||||
|
||||
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
||||
bookPriceType = Material.WRITABLE_BOOK;
|
||||
bookPriceQuantity = 1;
|
||||
config.set("Options.Price_to_create_book.Item_type", bookPriceType.toString());
|
||||
config.set("Options.Price_to_create_book.Required_quantity", bookPriceQuantity);
|
||||
}
|
||||
config.set("Options.Require_book_and_quill_to_create_book", null);
|
||||
}
|
||||
BooksWithoutBorders.getInstance().saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config
|
||||
*
|
||||
* @return <p>True if the config was loaded successfully</p>
|
||||
*/
|
||||
public static boolean loadConfig() {
|
||||
ConsoleCommandSender consoleSender = BooksWithoutBorders.getInstance().getServer().getConsoleSender();
|
||||
BooksWithoutBorders.getInstance().reloadConfig();
|
||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||
try {
|
||||
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
||||
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
||||
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
||||
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
||||
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
||||
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
||||
authorOnlyCopy = getBoolean(config, ConfigOption.AUTHOR_ONLY_COPY);
|
||||
authorOnlyUnsign = getBoolean(config, ConfigOption.AUTHOR_ONLY_UNSIGN);
|
||||
authorOnlySave = getBoolean(config, ConfigOption.AUTHOR_ONLY_SAVE);
|
||||
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
||||
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
||||
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
||||
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||
enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
|
||||
|
||||
//Convert string into material
|
||||
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
||||
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
||||
if (EconomyHelper.setupEconomy()) {
|
||||
bookPriceType = Material.AIR;
|
||||
} else {
|
||||
sendErrorMessage(consoleSender,
|
||||
"BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||
bookPriceType = null;
|
||||
}
|
||||
} else if (!paymentMaterial.trim().isEmpty()) {
|
||||
Material material = Material.matchMaterial(paymentMaterial);
|
||||
if (material != null) {
|
||||
bookPriceType = material;
|
||||
}
|
||||
}
|
||||
bookPriceQuantity = config.getDouble(ConfigOption.PRICE_QUANTITY.getConfigNode(),
|
||||
(Double) ConfigOption.PRICE_QUANTITY.getDefaultValue());
|
||||
|
||||
//Make sure titleAuthorSeparator is a valid value
|
||||
titleAuthorSeparator = cleanString(titleAuthorSeparator);
|
||||
if (titleAuthorSeparator.length() != 1) {
|
||||
sendErrorMessage(consoleSender, "Title-Author_Separator is set to an invalid value!");
|
||||
sendErrorMessage(consoleSender, "Reverting to default value of \",\"");
|
||||
titleAuthorSeparator = ",";
|
||||
config.set("Options.Title-Author_Separator", titleAuthorSeparator);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendErrorMessage(consoleSender, "Warning! Config.yml failed to load!");
|
||||
sendErrorMessage(consoleSender, "Try Looking for settings that are missing values!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package net.knarcraft.bookswithoutborders.config;
|
||||
|
||||
/**
|
||||
* A representation of the different available config options
|
||||
*/
|
||||
public enum ConfigOption {
|
||||
|
||||
/**
|
||||
* Whether YAML should be used to store books instead of simple text files
|
||||
*/
|
||||
USE_YAML("Options.Save_Books_in_Yaml_Format", true),
|
||||
|
||||
/**
|
||||
* The max duplicates of a book that can be saved
|
||||
*/
|
||||
MAX_DUPLICATES("Options.Max_Number_of_Duplicates", 5),
|
||||
|
||||
/**
|
||||
* The separator used to separate book title and book author
|
||||
*/
|
||||
TITLE_AUTHOR_SEPARATOR("Options.Title-Author_Separator", ","),
|
||||
|
||||
/**
|
||||
* The separator used to specify a new line in an item's lore
|
||||
*/
|
||||
LORE_LINE_SEPARATOR("Options.Lore_line_separator", "~"),
|
||||
|
||||
/**
|
||||
* The books given to new players when they first join
|
||||
*/
|
||||
BOOKS_FOR_NEW_PLAYERS("Options.Books_for_new_players", "[]"),
|
||||
|
||||
/**
|
||||
* The message to display to new players when they first join
|
||||
*/
|
||||
MESSAGE_FOR_NEW_PLAYERS("Options.Message_for_new_players", ""),
|
||||
|
||||
/**
|
||||
* The item type used to pay for book copying
|
||||
*/
|
||||
PRICE_ITEM_TYPE("Options.Price_to_create_book.Item_type", ""),
|
||||
|
||||
/**
|
||||
* The amount of items used to pay for book copying
|
||||
*/
|
||||
PRICE_QUANTITY("Options.Price_to_create_book.Required_quantity", 0.0),
|
||||
|
||||
/**
|
||||
* Whether admins should be able to decrypt books for all groups
|
||||
*/
|
||||
ADMIN_AUTO_DECRYPT("Options.Admin_Auto_Decrypt", false),
|
||||
|
||||
/**
|
||||
* Whether only the book author should be able to copy a book
|
||||
*/
|
||||
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
|
||||
*/
|
||||
FORMAT_AFTER_SIGNING("Options.Format_Book_After_Signing", false),
|
||||
|
||||
/**
|
||||
* Whether hitting a bookshelf should display information about the contained books
|
||||
*/
|
||||
ENABLE_BOOKSHELF_PEEKING("Options.Enable_Book_Peeking", false),
|
||||
;
|
||||
|
||||
private final String configNode;
|
||||
private final Object defaultValue;
|
||||
|
||||
/**
|
||||
* Instantiates a new config option
|
||||
*
|
||||
* @param configNode <p>The config node in the config file this option represents</p>
|
||||
* @param defaultValue <p>The default value for this config option</p>
|
||||
*/
|
||||
ConfigOption(String configNode, Object defaultValue) {
|
||||
this.configNode = configNode;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config node used for loading/saving this config value
|
||||
*
|
||||
* @return <p>The config node</p>
|
||||
*/
|
||||
public String getConfigNode() {
|
||||
return this.configNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value of this config option
|
||||
*
|
||||
* @return <p>The default value of this config option</p>
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package net.knarcraft.bookswithoutborders.encryption;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
@ -15,6 +17,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Base64;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* This class represents and AES encryptor/decryptor
|
||||
@ -66,16 +69,16 @@ public class AES {
|
||||
//Initialize cipher
|
||||
try {
|
||||
aes.init(mode, secretKeySpec, ivParameterSpec);
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES input given!");
|
||||
return null;
|
||||
}
|
||||
//Perform encryption/decryption and output result
|
||||
try {
|
||||
byte[] output = aes.doFinal(getInputBytes(input, encrypt));
|
||||
return createResult(output, encrypt);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalBlockSizeException | BadPaddingException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES block size or padding");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -131,8 +134,8 @@ public class AES {
|
||||
Cipher aes;
|
||||
try {
|
||||
aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES algorithm or padding");
|
||||
return null;
|
||||
}
|
||||
return aes;
|
||||
@ -149,15 +152,15 @@ public class AES {
|
||||
SecretKeyFactory keyFactory;
|
||||
try {
|
||||
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES algorithm");
|
||||
return null;
|
||||
}
|
||||
SecretKey tmp;
|
||||
try {
|
||||
tmp = keyFactory.generateSecret(spec);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvalidKeySpecException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES key specification");
|
||||
return null;
|
||||
}
|
||||
return new SecretKeySpec(tmp.getEncoded(), "AES");
|
||||
|
@ -14,8 +14,6 @@ public class GenenCrypt {
|
||||
|
||||
private final Random ranGen;
|
||||
private final String[] bases;
|
||||
private final ArrayList<String> originalCodonList;
|
||||
private final ArrayList<String> shuffledCodonList;
|
||||
private final String[] charList;
|
||||
private final HashMap<String, String[]> codonTable;
|
||||
private final HashMap<String, String> decryptTable;
|
||||
@ -29,13 +27,13 @@ public class GenenCrypt {
|
||||
public GenenCrypt(String key) {
|
||||
|
||||
// define the initial, unshuffled codon list of 4 base codons
|
||||
originalCodonList = new ArrayList<>();
|
||||
ArrayList<String> originalCodonList = new ArrayList<>();
|
||||
bases = new String[]{"A", "T", "G", "C"};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int k = 0; k < 4; k++) {
|
||||
for (int l = 0; l < 4; l++) {
|
||||
originalCodonList.add("" + bases[i] + bases[j] + bases[k] + bases[l]);
|
||||
originalCodonList.add(bases[i] + bases[j] + bases[k] + bases[l]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,8 +48,8 @@ public class GenenCrypt {
|
||||
ranGen = new java.util.Random(longKey);
|
||||
|
||||
// use the random number generator and the originalCodonList to make a shuffled list
|
||||
shuffledCodonList = new ArrayList<>();
|
||||
while (originalCodonList.size() > 0) {
|
||||
ArrayList<String> shuffledCodonList = new ArrayList<>();
|
||||
while (!originalCodonList.isEmpty()) {
|
||||
int index = ranGen.nextInt(originalCodonList.size());
|
||||
shuffledCodonList.add(originalCodonList.get(index));
|
||||
originalCodonList.remove(index);
|
||||
@ -90,24 +88,6 @@ public class GenenCrypt {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the shuffled codon list used for generating the codon table
|
||||
*/
|
||||
public void printShuffledList() {
|
||||
for (String s : shuffledCodonList) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the original codon list before it was shuffled
|
||||
*/
|
||||
public void printOriginalList() {
|
||||
for (String s : originalCodonList) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the codon table used for encryption and decryption
|
||||
*/
|
||||
@ -117,9 +97,12 @@ public class GenenCrypt {
|
||||
String s = charList[i];
|
||||
String[] sa = codonTable.get(s);
|
||||
switch (s) {
|
||||
case "\t" -> System.out.println(i + "\t" + "\\t" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
case "\n" -> System.out.println(i + "\t" + "\\n" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
case " " -> System.out.println(i + "\t" + "\" \"" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
case "\t" ->
|
||||
System.out.println(i + "\t" + "\\t" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
case "\n" ->
|
||||
System.out.println(i + "\t" + "\\n" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
case " " ->
|
||||
System.out.println(i + "\t" + "\" \"" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
default -> System.out.println(i + "\t" + s + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
|
||||
}
|
||||
}
|
||||
@ -181,4 +164,5 @@ public class GenenCrypt {
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,9 @@ package net.knarcraft.bookswithoutborders.encryption;
|
||||
import java.math.BigInteger;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* A simple substitution cipher
|
||||
*/
|
||||
public class SubstitutionCipher {
|
||||
|
||||
public SubstitutionCipher() {
|
||||
@ -16,7 +19,7 @@ public class SubstitutionCipher {
|
||||
// original message is offset by
|
||||
public String encrypt(String in, String key) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
if (key != null && key.length() > 0) {
|
||||
if (key != null && !key.isEmpty()) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
||||
// converts each number in the key to an integer and adds to an array
|
||||
int[] offsetArray = new int[tokenizer.countTokens()];
|
||||
@ -49,9 +52,10 @@ public class SubstitutionCipher {
|
||||
// but in reverse. Could probably be combined into one
|
||||
// method with a flag for encryption / decryption, but
|
||||
// I'm lazy.
|
||||
@SuppressWarnings("unused")
|
||||
public String decrypt(String in, String key) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
if (key != null && key.length() > 0) {
|
||||
if (key != null && !key.isEmpty()) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
||||
// converts each number in the key to an integer and adds to an array
|
||||
int[] offsetArray = new int[tokenizer.countTokens()];
|
||||
@ -76,6 +80,7 @@ public class SubstitutionCipher {
|
||||
// the one time pad encryption is very secure, and
|
||||
// encryption works just like decryption, but is
|
||||
// vulnerable if the same key is used more than once.
|
||||
@SuppressWarnings("unused")
|
||||
public String oneTimePad(String in, String key) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
for (int i = 0; i < in.length(); i++) {
|
||||
@ -83,5 +88,6 @@ public class SubstitutionCipher {
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
package net.knarcraft.bookswithoutborders.listener;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerEditBookEvent;
|
||||
|
||||
/**
|
||||
* A listener for listening to book events
|
||||
*
|
||||
* <p>Mainly used for auto-formatting signed books if enabled</p>
|
||||
*/
|
||||
public class BookEventListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onBookSign(PlayerEditBookEvent event) {
|
||||
if (event.isCancelled() || !event.isSigning() || !BooksWithoutBordersConfig.formatBooks()) {
|
||||
return;
|
||||
}
|
||||
event.setNewBookMeta(BookFormatter.formatPages(event.getNewBookMeta()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package net.knarcraft.bookswithoutborders.listener;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.block.ChiseledBookshelf;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ChiseledBookshelfInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSuccessColor;
|
||||
|
||||
/**
|
||||
* A listener for bookshelf clicking
|
||||
*/
|
||||
public class BookshelfListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onBookshelfClick(PlayerInteractEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
// If left-clicking a chiseled bookshelf and sneaking, display contents
|
||||
if (!event.hasBlock() || event.getClickedBlock() == null ||
|
||||
!(event.getClickedBlock().getState() instanceof ChiseledBookshelf chiseledBookshelf) ||
|
||||
event.getAction() != Action.LEFT_CLICK_BLOCK || !player.isSneaking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if bookshelf peeking is enabled, and the player can peek
|
||||
if (!BooksWithoutBordersConfig.getEnableBookshelfPeeking() ||
|
||||
!event.getPlayer().hasPermission("bookswithoutborders.peekbookshelf")) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setUseInteractedBlock(Event.Result.DENY);
|
||||
event.setUseItemInHand(Event.Result.DENY);
|
||||
|
||||
ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory();
|
||||
player.sendMessage(getBookshelfDescription(bookshelfInventory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description for a bookshelf's contents
|
||||
*
|
||||
* @param bookshelfInventory <p>The inventory of the bookshelf to describe</p>
|
||||
* @return <p>A textual description of the bookshelf's contents</p>
|
||||
*/
|
||||
private String getBookshelfDescription(ChiseledBookshelfInventory bookshelfInventory) {
|
||||
StringBuilder builder = new StringBuilder(getSuccessColor() + "Books in shelf:");
|
||||
for (ItemStack itemStack : bookshelfInventory.getStorageContents()) {
|
||||
if (itemStack == null) {
|
||||
continue;
|
||||
}
|
||||
ItemMeta meta = itemStack.getItemMeta();
|
||||
builder.append("\n ").append(ChatColor.GRAY).append(" - ");
|
||||
if (meta instanceof BookMeta bookMeta) {
|
||||
builder.append(getBookDescription(bookMeta));
|
||||
} else if (meta instanceof EnchantmentStorageMeta enchantmentStorageMeta) {
|
||||
builder.append(getEnchantedBookDescription(enchantmentStorageMeta));
|
||||
} else if (meta != null) {
|
||||
builder.append(getPlainBookDescription(meta));
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of a plain (enchant-able) book
|
||||
*
|
||||
* @param itemMeta <p>The metadata for the book to describe</p>
|
||||
* @return <p>The description of the book</p>
|
||||
*/
|
||||
private String getPlainBookDescription(ItemMeta itemMeta) {
|
||||
String name = itemMeta.getDisplayName();
|
||||
if (name.isEmpty()) {
|
||||
name = "Plain book";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of a book
|
||||
*
|
||||
* @param bookMeta <p>The metadata for the book to describe</p>
|
||||
* @return <p>The book's description</p>
|
||||
*/
|
||||
private String getBookDescription(BookMeta bookMeta) {
|
||||
String title;
|
||||
String author;
|
||||
if (!bookMeta.hasTitle() || bookMeta.getTitle() == null) {
|
||||
title = "Untitled";
|
||||
} else {
|
||||
title = bookMeta.getTitle();
|
||||
}
|
||||
if (!bookMeta.hasAuthor() || bookMeta.getAuthor() == null) {
|
||||
author = "Unknown";
|
||||
} else {
|
||||
author = bookMeta.getAuthor();
|
||||
}
|
||||
return title + " by " + author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of an enchanted book
|
||||
*
|
||||
* @param enchantmentStorageMeta <p>The metadata for the enchanted book to describe</p>
|
||||
* @return <p>The enchanted book's description</p>
|
||||
*/
|
||||
private String getEnchantedBookDescription(EnchantmentStorageMeta enchantmentStorageMeta) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Enchanted book (");
|
||||
Map<Enchantment, Integer> enchantmentMap = enchantmentStorageMeta.getStoredEnchants();
|
||||
List<String> enchantments = new ArrayList<>(enchantmentMap.size());
|
||||
for (Map.Entry<Enchantment, Integer> enchantmentEntry : enchantmentMap.entrySet()) {
|
||||
enchantments.add(getEnchantmentName(enchantmentEntry.getKey()) + " " +
|
||||
IntegerToRomanConverter.getRomanNumber(enchantmentEntry.getValue()));
|
||||
}
|
||||
builder.append(String.join(", ", enchantments));
|
||||
builder.append(")");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prettified name of an enchantment
|
||||
*
|
||||
* @param enchantment <p>The enchantment to get the name of</p>
|
||||
* @return <p>The prettified enchantment name</p>
|
||||
*/
|
||||
private String getEnchantmentName(Enchantment enchantment) {
|
||||
return uppercaseFirst(enchantment.getKey().getKey().replace("_", " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the first character of each of a string's words into uppercase
|
||||
*
|
||||
* @param input <p>The input to uppercase</p>
|
||||
* @return <p>The input string with more uppercase</p>
|
||||
*/
|
||||
private String uppercaseFirst(String input) {
|
||||
String[] parts = input.split(" ");
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1);
|
||||
}
|
||||
return String.join(" ", parts);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package net.knarcraft.bookswithoutborders.listener;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
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.InputCleaningHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||
import org.bukkit.Material;
|
||||
@ -15,13 +19,13 @@ import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
|
||||
/**
|
||||
* A listener for listening to player-related events such as joining or holding a book
|
||||
*/
|
||||
public class PlayerEventListener implements Listener {
|
||||
|
||||
private final String slash = getSlash();
|
||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||
|
||||
@EventHandler
|
||||
@ -53,12 +57,22 @@ public class PlayerEventListener implements Listener {
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
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
|
||||
if (!player.hasPlayedBefore()) {
|
||||
boolean sendMessage = true;
|
||||
|
||||
//Gives new players necessary books
|
||||
for (String bookName : BooksWithoutBorders.getFirstBooks()) {
|
||||
for (String bookName : BooksWithoutBordersConfig.getFirstBooks()) {
|
||||
sendMessage = giveBookToNewPlayer(bookName, player, sendMessage);
|
||||
}
|
||||
}
|
||||
@ -88,13 +102,13 @@ public class PlayerEventListener implements Listener {
|
||||
if (!bookName.trim().isEmpty()) {
|
||||
|
||||
//Give the book to the player if it exists
|
||||
ItemStack newBook = booksWithoutBorders.loadBook(player, bookName, "true", "public");
|
||||
ItemStack newBook = BookLoader.loadBook(player, bookName, "true", "public");
|
||||
if (newBook != null) {
|
||||
player.getInventory().addItem(newBook);
|
||||
}
|
||||
|
||||
//Send the player a welcome message if it exists
|
||||
String welcomeMessage = BooksWithoutBorders.getWelcomeMessage();
|
||||
String welcomeMessage = BooksWithoutBordersConfig.getWelcomeMessage();
|
||||
if (!welcomeMessage.trim().isEmpty() && newBook != null && sendMessage) {
|
||||
sendMessage = false;
|
||||
booksWithoutBorders.getServer().getScheduler().scheduleSyncDelayedTask(booksWithoutBorders,
|
||||
@ -143,22 +157,23 @@ public class PlayerEventListener implements Listener {
|
||||
//Unknown author is ignored
|
||||
fileName = oldBook.getTitle();
|
||||
} else {
|
||||
fileName = oldBook.getTitle() + BooksWithoutBorders.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[]{
|
||||
getBookFolder() + fileName + ".yml",
|
||||
getBookFolder() + fileName + ".txt",
|
||||
getBookFolder() + cleanPlayerName + slash + fileName + ".yml",
|
||||
getBookFolder() + cleanPlayerName + slash + fileName + ".txt"
|
||||
publicFolderPath + fileName + ".yml",
|
||||
publicFolderPath + fileName + ".txt",
|
||||
playerFolderPath + fileName + ".yml",
|
||||
playerFolderPath + fileName + ".txt"
|
||||
};
|
||||
|
||||
for (String path : possiblePaths) {
|
||||
File file = new File(path);
|
||||
if (file.isFile()) {
|
||||
return booksWithoutBorders.loadBook(player, fileName, "true", "player");
|
||||
return BookLoader.loadBook(player, fileName, "true", "player");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -1,15 +1,20 @@
|
||||
package net.knarcraft.bookswithoutborders.listener;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
||||
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||
import org.bukkit.ChatColor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.sign.Side;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
@ -22,10 +27,13 @@ import org.bukkit.inventory.meta.BookMeta;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.utility.FileHelper.isBookListIndex;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.utility.BookFileHelper.isBookListIndex;
|
||||
|
||||
/**
|
||||
* A listener for relevant sign events such as clicking a decryption sign
|
||||
*/
|
||||
public class SignEventListener implements Listener {
|
||||
|
||||
private final String slash = getSlash();
|
||||
@ -49,7 +57,7 @@ public class SignEventListener implements Listener {
|
||||
|
||||
//Check if the sign is of a valid type
|
||||
if (!((lines[1].equalsIgnoreCase("[Encrypt]") || lines[1].equalsIgnoreCase("[Decrypt]") ||
|
||||
lines[1].equalsIgnoreCase("[Give]")) && lines[2].trim().length() > 0)) {
|
||||
lines[1].equalsIgnoreCase("[Give]")) && !lines[2].trim().isEmpty())) {
|
||||
//Mark the second line as invalid
|
||||
event.setLine(1, ChatColor.DARK_RED + lines[1]);
|
||||
return;
|
||||
@ -84,24 +92,32 @@ public class SignEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
ItemStack heldItem = playerInventory.getItem(hand);
|
||||
if (heldItem == null) {
|
||||
return;
|
||||
}
|
||||
Material heldItemType = heldItem.getType();
|
||||
|
||||
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
|
||||
Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
|
||||
event.setUseItemInHand(Event.Result.DENY);
|
||||
//The player right-clicked a sign
|
||||
Sign sign = (Sign) event.getClickedBlock().getState();
|
||||
if (signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
||||
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
|
||||
encryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
|
||||
decryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
|
||||
getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
|
||||
giveBook(sign, player);
|
||||
} else {
|
||||
player.sendMessage("Sign command " + sign.getLine(1) + " " + sign.getLine(2) + " invalid");
|
||||
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
|
||||
}
|
||||
if (!signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
|
||||
encryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
|
||||
decryptHeldBookUsingSign(sign, heldItemType, player, hand);
|
||||
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
|
||||
getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
|
||||
giveBook(sign, player);
|
||||
} else {
|
||||
SignSide front = sign.getSide(Side.FRONT);
|
||||
player.sendMessage(String.format("Sign command %s %s is invalid", front.getLine(1),
|
||||
front.getLine(2)));
|
||||
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
|
||||
}
|
||||
} else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR
|
||||
|| event.getAction() == Action.LEFT_CLICK_BLOCK)) {
|
||||
@ -128,7 +144,7 @@ public class SignEventListener implements Listener {
|
||||
player.closeInventory();
|
||||
|
||||
//Converts user supplied key into integer form
|
||||
String lineText = ChatColor.stripColor(sign.getLine(2));
|
||||
String lineText = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
|
||||
String key = EncryptionHelper.getNumberKeyFromStringKey(lineText);
|
||||
|
||||
ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false);
|
||||
@ -146,9 +162,9 @@ public class SignEventListener implements Listener {
|
||||
* @return <p>The color of the sign</p>
|
||||
*/
|
||||
private ChatColor getSignLine2Color(Sign sign) {
|
||||
String line = sign.getLine(2);
|
||||
String line = sign.getSide(Side.FRONT).getLine(2);
|
||||
if (!ChatColor.stripColor(line).equals(line)) {
|
||||
return ChatColor.getByChar(sign.getLine(2).substring(1, 2));
|
||||
return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -164,7 +180,7 @@ public class SignEventListener implements Listener {
|
||||
* @return <p>True if the given string is what's on the sign</p>
|
||||
*/
|
||||
private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) {
|
||||
String line = sign.getLine(lineNumber);
|
||||
String line = sign.getSide(Side.FRONT).getLine(lineNumber);
|
||||
return line.equalsIgnoreCase(color + compareTo);
|
||||
}
|
||||
|
||||
@ -177,14 +193,15 @@ public class SignEventListener implements Listener {
|
||||
*/
|
||||
private void generateGiveSign(SignChangeEvent event, String[] lines, Player player) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
//Tests if a full file name has been supplied and points to an actual file
|
||||
String signFile = getBookFolder() + lines[2] + lines[3];
|
||||
if (FileHelper.bookFileExists(signFile)) {
|
||||
if (BookFileHelper.bookFileExists(signFile)) {
|
||||
markGiveSignValidity(event, true);
|
||||
return;
|
||||
} else {
|
||||
@ -234,11 +251,11 @@ public class SignEventListener implements Listener {
|
||||
|
||||
//Permission check
|
||||
if (!player.hasPermission("bookswithoutborders.decrypt." + groupName) &&
|
||||
!(BooksWithoutBorders.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin"))) {
|
||||
!(BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = oldBook.getTitle() + BooksWithoutBorders.getTitleAuthorSeparator() + oldBook.getAuthor();
|
||||
String fileName = oldBook.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + oldBook.getAuthor();
|
||||
|
||||
String encryptionFile = InputCleaningHelper.cleanString(groupName) + slash + fileName + ".yml";
|
||||
|
||||
@ -249,7 +266,7 @@ public class SignEventListener implements Listener {
|
||||
return;
|
||||
}
|
||||
}
|
||||
newBook = BooksWithoutBorders.getInstance().loadBook(player, fileName, "true", groupName, heldItem.getAmount());
|
||||
newBook = BookLoader.loadBook(player, fileName, "true", groupName, heldItem.getAmount());
|
||||
|
||||
if (newBook == null) {
|
||||
return;
|
||||
@ -257,7 +274,7 @@ public class SignEventListener implements Listener {
|
||||
|
||||
player.getInventory().setItem(hand, newBook);
|
||||
player.closeInventory();
|
||||
player.sendMessage(ChatColor.GREEN + "Book auto-decrypted!");
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Book auto-decrypted!");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -270,7 +287,7 @@ public class SignEventListener implements Listener {
|
||||
*/
|
||||
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
|
||||
ItemStack eBook;
|
||||
String[] lines = sign.getLines();
|
||||
String[] lines = sign.getSide(Side.FRONT).getLines();
|
||||
boolean mainHand = hand == EquipmentSlot.HAND;
|
||||
if (heldItemType == Material.WRITTEN_BOOK) {
|
||||
player.closeInventory();
|
||||
@ -289,7 +306,7 @@ public class SignEventListener implements Listener {
|
||||
* @param player <p>The player which clicked the sign</p>
|
||||
*/
|
||||
private void giveBook(Sign sign, Player player) {
|
||||
String fileName = ChatColor.stripColor(sign.getLine(2));
|
||||
String fileName = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
|
||||
boolean isLoadListNumber = false;
|
||||
|
||||
try {
|
||||
@ -299,18 +316,18 @@ public class SignEventListener implements Listener {
|
||||
}
|
||||
|
||||
//Add the third line to the second line for the full filename
|
||||
String thirdLine = sign.getLine(3);
|
||||
String thirdLine = sign.getSide(Side.FRONT).getLine(3);
|
||||
if (!isLoadListNumber && thirdLine.length() >= 2) {
|
||||
fileName += ChatColor.stripColor(thirdLine);
|
||||
}
|
||||
|
||||
ItemStack newBook = BooksWithoutBorders.getInstance().loadBook(player, fileName, "true", "public");
|
||||
ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public");
|
||||
|
||||
if (newBook != null) {
|
||||
player.getInventory().addItem(newBook);
|
||||
player.sendMessage(ChatColor.GREEN + "Received book!");
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "Received book!");
|
||||
} else {
|
||||
player.sendMessage(ChatColor.RED + "Book failed to load!");
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Book failed to load!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,20 @@ package net.knarcraft.bookswithoutborders.state;
|
||||
* This enum represents the different directories books can be saved in
|
||||
*/
|
||||
public enum BookDirectory {
|
||||
|
||||
/**
|
||||
* The public directory
|
||||
*/
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* A player directory
|
||||
*/
|
||||
PLAYER,
|
||||
|
||||
/**
|
||||
* The encrypted directory
|
||||
*/
|
||||
ENCRYPTED;
|
||||
|
||||
/**
|
||||
@ -25,4 +37,5 @@ public enum BookDirectory {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,22 +26,22 @@ public enum BookHoldingState {
|
||||
UNSIGNED_MAIN_HAND,
|
||||
|
||||
/**
|
||||
* The player is holding one signed book in their off hand
|
||||
* The player is holding one signed book in their off-hand
|
||||
*/
|
||||
SIGNED_OFF_HAND,
|
||||
|
||||
/**
|
||||
* The player is holding one unsigned book in their off hand
|
||||
* The player is holding one unsigned book in their off-hand
|
||||
*/
|
||||
UNSIGNED_OFF_HAND,
|
||||
|
||||
/**
|
||||
* The player is holding one signed book in their main hand and one unsigned book in their off hand
|
||||
* The player is holding one signed book in their main hand and one unsigned book in their off-hand
|
||||
*/
|
||||
SIGNED_MAIN_HAND_UNSIGNED_OFF_HAND,
|
||||
|
||||
/**
|
||||
* The player is holding one unsigned book in their main hand and one signed book in their off hand
|
||||
* The player is holding one unsigned book in their main hand and one signed book in their off-hand
|
||||
*/
|
||||
UNSIGNED_MAIN_HAND_SIGNED_OFF_HAND,
|
||||
|
||||
|
@ -4,6 +4,7 @@ package net.knarcraft.bookswithoutborders.state;
|
||||
* This enum represents the different available encryption styles
|
||||
*/
|
||||
public enum EncryptionStyle {
|
||||
|
||||
DNA("dna"),
|
||||
SUBSTITUTION("substitution");
|
||||
|
||||
@ -27,4 +28,5 @@ public enum EncryptionStyle {
|
||||
}
|
||||
return SUBSTITUTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public enum ItemSlot {
|
||||
MAIN_HAND,
|
||||
|
||||
/**
|
||||
* The item is in the player's off hand
|
||||
* The item is in the player's off-hand
|
||||
*/
|
||||
OFF_HAND,
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import org.bukkit.ChatColor;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.io.File;
|
||||
@ -10,17 +12,14 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||
|
||||
/**
|
||||
* Helper class for dealing with files
|
||||
*/
|
||||
public final class FileHelper {
|
||||
|
||||
private FileHelper() {
|
||||
public final class BookFileHelper {
|
||||
|
||||
private BookFileHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,13 +94,11 @@ public final class FileHelper {
|
||||
* @return <p>A list of available files</p>
|
||||
*/
|
||||
public static List<String> listFiles(CommandSender sender, Boolean listPublic) {
|
||||
File file;
|
||||
if (listPublic) {
|
||||
file = new File(getBookFolder());
|
||||
} else {
|
||||
file = new File(getBookFolder() + cleanString(sender.getName()) + getSlash());
|
||||
File file = BookHelper.getBookDirectoryPath(listPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||
if (file == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return FileHelper.listFiles(sender, file);
|
||||
return BookFileHelper.listFiles(sender, file);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,7 +109,7 @@ public final class FileHelper {
|
||||
*/
|
||||
public static void printBooks(CommandSender sender, boolean listPublic) {
|
||||
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
||||
FileHelper.printFiles(sender, availableBooks);
|
||||
BookFileHelper.printFiles(sender, availableBooks);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,6 +120,9 @@ public final class FileHelper {
|
||||
*/
|
||||
public static void printFiles(CommandSender sender, List<String> fileList) {
|
||||
BooksWithoutBorders.sendSuccessMessage(sender, "Available Books:");
|
||||
if (fileList == null) {
|
||||
return;
|
||||
}
|
||||
int listSize = fileList.size();
|
||||
for (int fileIndex = 0; fileIndex < listSize; fileIndex++) {
|
||||
sender.sendMessage(ChatColor.GRAY + "[" + (fileIndex + 1) + "] " + fileList.get(fileIndex));
|
||||
@ -146,9 +146,20 @@ public final class FileHelper {
|
||||
}
|
||||
|
||||
for (File foundFile : existingFiles) {
|
||||
if (foundFile.isFile()) {
|
||||
fileList.add(foundFile.getName());
|
||||
if (!foundFile.isFile()) {
|
||||
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));
|
||||
}
|
||||
fileList.add(fileName);
|
||||
}
|
||||
|
||||
return fileList;
|
@ -1,6 +1,12 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import net.knarcraft.knarlib.property.ColorConversion;
|
||||
import net.knarcraft.knarlib.util.ColorHelper;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class for formatting text to fit books
|
||||
@ -8,7 +14,6 @@ import java.util.List;
|
||||
public final class BookFormatter {
|
||||
|
||||
private BookFormatter() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,4 +99,19 @@ public final class BookFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats every page in the given book meta by converting color and formatting codes
|
||||
*
|
||||
* @param bookMeta <p>The book meta to change</p>
|
||||
* @return <p>The changed book meta</p>
|
||||
*/
|
||||
public static BookMeta formatPages(BookMeta bookMeta) {
|
||||
List<String> formattedPages = new ArrayList<>(Objects.requireNonNull(bookMeta).getPageCount());
|
||||
for (String page : bookMeta.getPages()) {
|
||||
formattedPages.add(ColorHelper.translateColorCodes(page, ColorConversion.RGB));
|
||||
}
|
||||
bookMeta.setPages(formattedPages);
|
||||
return bookMeta;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
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.Material;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A helper class for loading books from files
|
||||
*/
|
||||
public final class BookLoader {
|
||||
|
||||
private BookLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given book
|
||||
*
|
||||
* @param sender <p>The command sender trying to load the book</p>
|
||||
* @param fileName <p>The index or file name of the book to load</p>
|
||||
* @param isSigned <p>Whether to load the book as signed, and not unsigned</p>
|
||||
* @param directory <p>The directory to save the book in</p>
|
||||
* @return <p>The loaded book</p>
|
||||
*/
|
||||
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory) {
|
||||
return loadBook(sender, fileName, isSigned, directory, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given book
|
||||
*
|
||||
* @param sender <p>The command sender trying to load the book</p>
|
||||
* @param fileName <p>The index or file name of the book to load</p>
|
||||
* @param isSigned <p>Whether to load the book as signed, and not unsigned</p>
|
||||
* @param directory <p>The directory to save the book in</p>
|
||||
* @param numCopies <p>The number of copies to load</p>
|
||||
* @return <p>The loaded book</p>
|
||||
*/
|
||||
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory, int numCopies) {
|
||||
BookDirectory bookDirectory = BookDirectory.getFromString(directory);
|
||||
|
||||
//Find the filename if a book index is given
|
||||
try {
|
||||
int bookIndex = Integer.parseInt(fileName);
|
||||
List<String> availableFiles = BooksWithoutBorders.getAvailableBooks(sender, bookDirectory == BookDirectory.PUBLIC);
|
||||
if (bookIndex <= availableFiles.size()) {
|
||||
fileName = availableFiles.get(Integer.parseInt(fileName) - 1);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
//Get the full path of the book to load
|
||||
File file = getFullPath(sender, fileName, bookDirectory, directory);
|
||||
if (file == null) {
|
||||
//Try converting the username to UUID
|
||||
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||
String[] data = fileName.split(titleAuthorSeparator);
|
||||
String extension = data[1].substring(data[1].length() - 4);
|
||||
String userName = data[1].substring(0, data[1].length() - 4);
|
||||
Player player = Bukkit.getPlayer(userName);
|
||||
if (player != null) {
|
||||
data[1] = player.getUniqueId() + extension;
|
||||
file = getFullPath(sender, String.join(titleAuthorSeparator, data), bookDirectory, directory);
|
||||
if (file == null) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect file name!");
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//Make sure the player can pay for the book
|
||||
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
||||
!sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
||||
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Generate a new empty book
|
||||
ItemStack book;
|
||||
BookMeta bookMetadata = (BookMeta) BooksWithoutBorders.getItemFactory().getItemMeta(Material.WRITTEN_BOOK);
|
||||
if (isSigned.equalsIgnoreCase("true")) {
|
||||
book = new ItemStack(Material.WRITTEN_BOOK);
|
||||
} else {
|
||||
book = new ItemStack(Material.WRITABLE_BOOK);
|
||||
}
|
||||
|
||||
//Load the book from the given file
|
||||
BookToFromTextHelper.bookFromFile(file, bookMetadata);
|
||||
if (bookMetadata == null) {
|
||||
BooksWithoutBorders.sendErrorMessage(sender, "File was blank!!");
|
||||
return null;
|
||||
}
|
||||
|
||||
//Remove "encrypted" from the book lore
|
||||
if (bookDirectory == BookDirectory.ENCRYPTED && bookMetadata.hasLore()) {
|
||||
List<String> oldLore = bookMetadata.getLore();
|
||||
if (oldLore != null) {
|
||||
List<String> newLore = new ArrayList<>(oldLore);
|
||||
newLore.remove(0);
|
||||
bookMetadata.setLore(newLore);
|
||||
}
|
||||
}
|
||||
|
||||
//Set the metadata and amount to the new book
|
||||
book.setItemMeta(bookMetadata);
|
||||
//Increase book generation if enabled
|
||||
BookHelper.increaseGeneration(book);
|
||||
book.setAmount(numCopies);
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a File pointing to the wanted book
|
||||
*
|
||||
* @param sender <p>The sender to send errors to</p>
|
||||
* @param fileName <p>The name of the book file</p>
|
||||
* @param bookDirectory <p>The book directory the file resides in</p>
|
||||
* @param directory <p>The relative directory given</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) {
|
||||
File file;
|
||||
String slash = BooksWithoutBordersConfig.getSlash();
|
||||
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||
if (bookDirectory == BookDirectory.ENCRYPTED) {
|
||||
file = BookFileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName);
|
||||
} else {
|
||||
file = BookFileHelper.getBookFile(BookHelper.getBookDirectoryPathString(bookDirectory, sender) + fileName);
|
||||
}
|
||||
if (file == null || !file.isFile()) {
|
||||
return null;
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,24 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.knarlib.util.FileHelper;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.utility.BookHelper.authorFromUUID;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||
|
||||
/**
|
||||
@ -41,6 +46,11 @@ public final class BookToFromTextHelper {
|
||||
if (bookMetadata.hasAuthor()) {
|
||||
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()) {
|
||||
bookYml.set("Pages", bookMetadata.getPages());
|
||||
}
|
||||
@ -77,12 +87,18 @@ public final class BookToFromTextHelper {
|
||||
* @throws IOException <p>If unable to save the book</p>
|
||||
*/
|
||||
public static void bookToTXT(String folderPath, String fileName, BookMeta bookMetadata) throws IOException {
|
||||
FileWriter fileWriter = new FileWriter(folderPath + fileName + ".txt");
|
||||
FileWriter fileWriter = new FileWriter(folderPath + fileName + ".txt", StandardCharsets.UTF_8);
|
||||
PrintWriter printWriter = new PrintWriter(fileWriter);
|
||||
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
|
||||
printWriter.println("[Book]");
|
||||
printWriter.println("[Book]" + generationString);
|
||||
for (String page : pages) {
|
||||
printWriter.println(page);
|
||||
}
|
||||
@ -100,8 +116,9 @@ public final class BookToFromTextHelper {
|
||||
try {
|
||||
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
bookMetadata.setGeneration(BookMeta.Generation.valueOf(bookYml.getString("Generation", "ORIGINAL")));
|
||||
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.setLore(bookYml.getStringList("Lore"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
@ -121,15 +138,18 @@ public final class BookToFromTextHelper {
|
||||
private static BookMeta bookFromTXT(String fileName, File file, BookMeta bookMetadata) {
|
||||
String author;
|
||||
String title;
|
||||
String titleAuthorSeparator = BooksWithoutBorders.getTitleAuthorSeparator();
|
||||
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||
|
||||
//Remove .txt extension
|
||||
fileName = fileName.substring(0, fileName.length() - 4);
|
||||
//Get title and author from the file name
|
||||
if (fileName.contains(titleAuthorSeparator)) {
|
||||
author = fileName.substring(fileName.indexOf(titleAuthorSeparator) + 1, fileName.length() - 4);
|
||||
title = fileName.substring(0, fileName.indexOf(titleAuthorSeparator));
|
||||
String[] titleAuthor = fileName.split(titleAuthorSeparator);
|
||||
title = titleAuthor[0];
|
||||
author = titleAuthor[1];
|
||||
} else {
|
||||
author = "Unknown";
|
||||
title = fileName.substring(0, fileName.length() - 4);
|
||||
title = fileName;
|
||||
}
|
||||
|
||||
//Replace underscores with spaces
|
||||
@ -139,16 +159,22 @@ public final class BookToFromTextHelper {
|
||||
List<String> rawPages;
|
||||
try {
|
||||
rawPages = readTextFile(file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to read text file");
|
||||
return null;
|
||||
}
|
||||
|
||||
//Parse the generation from the book data
|
||||
if (rawPages != null && !rawPages.isEmpty() && rawPages.get(0).startsWith("Generation:")) {
|
||||
bookMetadata.setGeneration(BookMeta.Generation.valueOf(rawPages.get(0).split(":")[1]));
|
||||
rawPages.remove(0);
|
||||
}
|
||||
|
||||
//Remove any empty pages
|
||||
List<String> pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages));
|
||||
|
||||
//Update the metadata of the book with its new values
|
||||
bookMetadata.setAuthor(author);
|
||||
bookMetadata.setAuthor(authorFromUUID(author));
|
||||
bookMetadata.setTitle(title);
|
||||
bookMetadata.setPages(pages);
|
||||
|
||||
@ -164,7 +190,7 @@ public final class BookToFromTextHelper {
|
||||
*/
|
||||
private static List<String> readTextFile(File file) throws IOException {
|
||||
List<String> rawPages = new ArrayList<>();
|
||||
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
|
||||
BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(new FileInputStream(file));
|
||||
|
||||
//Use the first line to decide if we are loading a book, or creating a new book
|
||||
String firstLine = bufferedReader.readLine();
|
||||
@ -172,8 +198,11 @@ public final class BookToFromTextHelper {
|
||||
bufferedReader.close();
|
||||
return null;
|
||||
}
|
||||
if (firstLine.equalsIgnoreCase("[Book]")) {
|
||||
if (firstLine.toLowerCase().startsWith("[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;
|
||||
do {
|
||||
readLine = bufferedReader.readLine();
|
||||
|
@ -1,16 +1,20 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicesManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -21,7 +25,6 @@ public final class EconomyHelper {
|
||||
private static Economy economy;
|
||||
|
||||
private EconomyHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,24 +70,96 @@ public final class EconomyHelper {
|
||||
public static boolean cannotPayForBookPrinting(Player player, int numCopies) {
|
||||
//BookPriceQuantity: How many items are required to pay for each book
|
||||
//BookPriceType: Which item is used to pay for the books. AIR = use economy
|
||||
Material bookCurrency = BooksWithoutBorders.getBookPriceType();
|
||||
double cost = BooksWithoutBorders.getBookPriceQuantity() * numCopies;
|
||||
Material bookCurrency = BooksWithoutBordersConfig.getBookPriceType();
|
||||
double cost = BooksWithoutBordersConfig.getBookPriceQuantity() * numCopies;
|
||||
int itemCost = (int) cost;
|
||||
|
||||
if (bookCurrency == Material.AIR) {
|
||||
return !EconomyHelper.payForBookPrintingEconomy(player, cost, numCopies);
|
||||
} else {
|
||||
if (player.getInventory().contains(bookCurrency, itemCost)) {
|
||||
payForBookPrintingItem(player, itemCost);
|
||||
return false;
|
||||
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 {
|
||||
BooksWithoutBorders.sendErrorMessage(player, itemCost + " " + bookCurrency +
|
||||
"(s) are required for this command!");
|
||||
return true;
|
||||
if (player.getInventory().contains(bookCurrency, itemCost)) {
|
||||
payForBookPrintingItem(player, itemCost);
|
||||
return false;
|
||||
} else {
|
||||
BooksWithoutBorders.sendErrorMessage(player, itemCost + " " + bookCurrency +
|
||||
"(s) are required for this command!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -96,8 +171,10 @@ public final class EconomyHelper {
|
||||
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) {
|
||||
if ((economy.getBalance(player) - cost) >= 0) {
|
||||
economy.withdrawPlayer(player, cost);
|
||||
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + numCopies + " book(s)");
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " + economy.format(economy.getBalance(player)));
|
||||
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " +
|
||||
numCopies + " book(s)");
|
||||
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " +
|
||||
economy.format(economy.getBalance(player)));
|
||||
return true;
|
||||
} else {
|
||||
BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!");
|
||||
@ -116,7 +193,7 @@ public final class EconomyHelper {
|
||||
|
||||
int clearedAmount = 0;
|
||||
while (clearedAmount < itemCost) {
|
||||
int firstItemIndex = playerInventory.first(BooksWithoutBorders.getBookPriceType());
|
||||
int firstItemIndex = playerInventory.first(BooksWithoutBordersConfig.getBookPriceType());
|
||||
ItemStack firstItem = playerInventory.getItem(firstItemIndex);
|
||||
|
||||
if (Objects.requireNonNull(firstItem).getAmount() <= itemCost - clearedAmount) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||
import net.knarcraft.bookswithoutborders.encryption.GenenCrypt;
|
||||
import net.knarcraft.bookswithoutborders.encryption.SubstitutionCipher;
|
||||
import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
|
||||
import org.bukkit.ChatColor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -15,9 +16,10 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.BooksWithoutBordersSettings.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||
|
||||
@ -27,7 +29,6 @@ import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixN
|
||||
public final class EncryptionHelper {
|
||||
|
||||
private EncryptionHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,11 +199,8 @@ public final class EncryptionHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() : bookMetadata.getTitle() +
|
||||
BooksWithoutBorders.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
||||
fileName = "[" + key + "]" + fileName;
|
||||
fileName = cleanString(fileName);
|
||||
fileName = fixName(fileName, false);
|
||||
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetadata, player, true);
|
||||
fileName = fixName(cleanString(fileName), false);
|
||||
|
||||
File file = new File(path + fileName + ".yml");
|
||||
if (!file.isFile()) {
|
||||
@ -258,17 +256,13 @@ public final class EncryptionHelper {
|
||||
BooksWithoutBorders.sendErrorMessage(BooksWithoutBorders.getConsoleSender(), "Unable to create encryption group folder!");
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save group encrypted book");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//Creates file
|
||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() :
|
||||
bookMetadata.getTitle() + BooksWithoutBorders.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
||||
|
||||
fileName = cleanString(fileName);
|
||||
fileName = fixName(fileName, false);
|
||||
//Generate file name
|
||||
String fileName = BookHelper.getBookFile(bookMetadata, player, true);
|
||||
|
||||
List<String> newLore = new ArrayList<>();
|
||||
newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]");
|
||||
@ -281,12 +275,12 @@ public final class EncryptionHelper {
|
||||
bookMetadata.setLore(newLore);
|
||||
|
||||
//Save file
|
||||
File file = (BooksWithoutBorders.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()) {
|
||||
try {
|
||||
BookToFromTextHelper.bookToYml(path, fileName, bookMetadata);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException exception) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Group encrypted failed!");
|
||||
return null;
|
||||
}
|
||||
@ -305,23 +299,20 @@ public final class EncryptionHelper {
|
||||
*/
|
||||
private static Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) {
|
||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||
String fileName = (!bookMetaData.hasTitle()) ? "Untitled," + player.getName() :
|
||||
bookMetaData.getTitle() + BooksWithoutBorders.getTitleAuthorSeparator() + bookMetaData.getAuthor();
|
||||
|
||||
fileName = "[" + key + "]" + fileName;
|
||||
fileName = cleanString(fileName);
|
||||
fileName = fixName(fileName, false);
|
||||
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetaData, player, true);
|
||||
fileName = fixName(cleanString(fileName), false);
|
||||
|
||||
//cancels saving if file is already encrypted
|
||||
File file = (BooksWithoutBorders.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()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
BookToFromTextHelper.bookToYml(path, fileName, bookMetaData);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException exception) {
|
||||
BooksWithoutBorders.sendErrorMessage(player, "Encryption failed!");
|
||||
return false;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import java.util.List;
|
||||
public final class InputCleaningHelper {
|
||||
|
||||
private InputCleaningHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,90 @@
|
||||
package net.knarcraft.bookswithoutborders.utility;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A converter from an integer to a roman numeral
|
||||
*/
|
||||
public final class IntegerToRomanConverter {
|
||||
|
||||
private final static List<Integer> romanValues = new ArrayList<>();
|
||||
private final static List<Character> romanCharacters = new ArrayList<>();
|
||||
|
||||
static {
|
||||
// Initialize the roman numbers
|
||||
romanValues.add(1000);
|
||||
romanValues.add(500);
|
||||
romanValues.add(100);
|
||||
romanValues.add(50);
|
||||
romanValues.add(10);
|
||||
romanValues.add(5);
|
||||
romanValues.add(1);
|
||||
|
||||
romanCharacters.add('M');
|
||||
romanCharacters.add('D');
|
||||
romanCharacters.add('C');
|
||||
romanCharacters.add('L');
|
||||
romanCharacters.add('X');
|
||||
romanCharacters.add('V');
|
||||
romanCharacters.add('I');
|
||||
}
|
||||
|
||||
private IntegerToRomanConverter() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the given number as a roman number string
|
||||
*
|
||||
* @param number <p>The number to convert</p>
|
||||
* @return <p>The roman representation of the number</p>
|
||||
*/
|
||||
public static String getRomanNumber(int number) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
int remainder = number;
|
||||
for (int i = 0; i < romanCharacters.size(); i++) {
|
||||
int romanValue = romanValues.get(i);
|
||||
char romanCharacter = romanCharacters.get(i);
|
||||
|
||||
// Repeat the roman character, and calculate the new remainder
|
||||
if (remainder >= romanValue) {
|
||||
output.append(repeat(romanCharacter, remainder / romanValue));
|
||||
remainder = remainder % romanValue;
|
||||
}
|
||||
|
||||
// Exit early to prevent unexpected trailing characters
|
||||
if (remainder == 0) {
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
// Generate the special case IV and similar
|
||||
for (int j = i; j < romanCharacters.size(); j++) {
|
||||
int value = romanValues.get(j);
|
||||
int difference = Math.max(romanValue - value, 0);
|
||||
|
||||
/* If the remainder is "one" less than the current roman value, we hit the IV/IX/XL case.
|
||||
Note that 5 triggers the special case when 10 is tested, as 5 = 10 - 5, which requires a test, so it
|
||||
can be filtered out. */
|
||||
if (remainder == difference && value != romanValue / 2) {
|
||||
output.append(romanCharacters.get(j)).append(romanCharacter);
|
||||
remainder = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats the given character
|
||||
*
|
||||
* @param character <p>The character to repeat</p>
|
||||
* @param times <p>The number of times to repeat the character</p>
|
||||
* @return <p>The repeated string</p>
|
||||
*/
|
||||
private static String repeat(char character, int times) {
|
||||
return String.valueOf(character).repeat(Math.max(0, times));
|
||||
}
|
||||
|
||||
}
|
@ -14,7 +14,6 @@ import org.bukkit.inventory.meta.BookMeta;
|
||||
public final class InventoryHelper {
|
||||
|
||||
private InventoryHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,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
|
||||
*
|
||||
@ -97,7 +121,7 @@ public final class InventoryHelper {
|
||||
if (state == BookHoldingState.SIGNED_BOTH_HANDS ||
|
||||
state == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
||||
state == BookHoldingState.NONE) {
|
||||
return null;
|
||||
return ItemSlot.NONE;
|
||||
}
|
||||
if (handMatters && typeMatters) {
|
||||
if (mainHand && mainHandItem.getType() == requiredMaterial) {
|
||||
|
@ -6,10 +6,9 @@ import java.util.List;
|
||||
/**
|
||||
* Helper class for getting string lists required for auto-completion
|
||||
*/
|
||||
public final class TabCompletionHelper {
|
||||
|
||||
private TabCompletionHelper() {
|
||||
public final class TabCompletionTypeHelper {
|
||||
|
||||
private TabCompletionTypeHelper() {
|
||||
}
|
||||
|
||||
/**
|
34
src/main/resources/config.yml
Normal file
34
src/main/resources/config.yml
Normal file
@ -0,0 +1,34 @@
|
||||
Options:
|
||||
# Whether to use YAML for saved books instead of just storing them as text
|
||||
Save_Books_in_Yaml_Format: true
|
||||
# The maximum number of duplicates of a saved book allowed
|
||||
Max_Number_of_Duplicates: 5
|
||||
# The separator used to separate the book title and the book author
|
||||
Title-Author_Separator: ","
|
||||
# The separator used to denote a new line in the book/item lore
|
||||
Lore_line_separator: "~"
|
||||
# A list of books given to new players the first time they join the server
|
||||
Books_for_new_players: [ ]
|
||||
# An optional message displayed to new players the first time they join the server
|
||||
Message_for_new_players: ""
|
||||
# Price settings for book copying
|
||||
Price_to_create_book:
|
||||
# The item type used as currency for copying books. Use "Economy" to use money instead of items
|
||||
Item_type: ""
|
||||
# The quantity of currency required to pay for each book produced
|
||||
Required_quantity: 0
|
||||
# Whether any admin can decrypt any book regardless of the group it was encrypted for
|
||||
Admin_Auto_Decrypt: false
|
||||
# Whether to only allow the author of a book to create copies
|
||||
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
|
||||
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
|
||||
# Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents.
|
||||
Enable_Book_Peeking: false
|
@ -1,18 +1,26 @@
|
||||
name: BooksWithoutBorders
|
||||
version: '${project.version}'
|
||||
main: net.knarcraft.bookswithoutborders.BooksWithoutBorders
|
||||
api-version: 1.17
|
||||
prefix: Books Without Borders
|
||||
api-version: '1.20'
|
||||
prefix: 'Books Without Borders'
|
||||
authors: [ EpicKnarvik97, AkiraAkiba ]
|
||||
description: A continuation of the original Books Without Borders
|
||||
description: 'A continuation of the original Books Without Borders'
|
||||
softdepend: [ Vault ]
|
||||
website: ????
|
||||
website: https://www.spigotmc.org/resources/books-without-borders-updated.96069/
|
||||
dev-url: https://git.knarcraft.net/EpicKnarvik97/Books-Without-Borders
|
||||
commands:
|
||||
bookswithoutborders:
|
||||
description: Lists Books Without Borders's commands and uses.
|
||||
aliases: [ bwb ]
|
||||
usage: /<command>
|
||||
decryptbook:
|
||||
description: Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used to encrypt held book
|
||||
usage: /<command> <key>
|
||||
permission: bookswithoutborders.decrypt
|
||||
formatbook:
|
||||
description: Replaces color/formatting codes in a written book with formatted text
|
||||
usage: /<command>
|
||||
permission: bookswithoutborders.format
|
||||
givebook:
|
||||
description: Gives the selected player a book from your personal directory
|
||||
usage: /<command> <file name or number> <playername> [# of copies (num)] [signed (true/false)]
|
||||
@ -21,10 +29,6 @@ commands:
|
||||
description: Same as givebook, but uses books from the public directory
|
||||
usage: /<command> <file name or number> <playername> [# of copies (num)] [signed (true/false)]
|
||||
permission: bookswithoutborders.givepublic
|
||||
decryptbook:
|
||||
description: Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used to encrypt held book
|
||||
usage: /<command> <key>
|
||||
permission: bookswithoutborders.decrypt
|
||||
groupencryptbook:
|
||||
description: Encrypts book so that only players with the bookswithoutborders.decrypt.<group name> permission may decrypt the book by holding and left clicking the book
|
||||
usage: /<command> <group name> <key> [encryption style]
|
||||
@ -41,6 +45,10 @@ commands:
|
||||
description: Copies the book the player is holding
|
||||
usage: /<command> <# of copies>
|
||||
permission: bookswithoutborders.copy
|
||||
clearbook:
|
||||
description: Removes all text from the book the player is holding
|
||||
usage: /<command>
|
||||
permission: bookswithoutborders.clear
|
||||
unsignbook:
|
||||
description: Un-signs the book the player is holding
|
||||
usage: /<command>
|
||||
@ -49,12 +57,16 @@ 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 ""
|
||||
usage: /<command> <key> [encryption style]
|
||||
permission: bookswithoutborders.encrypt
|
||||
setbookgeneration:
|
||||
description: Sets the generation of the held book
|
||||
usage: /<command> <generation>
|
||||
permission: bookswithoutborders.setgeneration
|
||||
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>
|
||||
permission: bookswithoutborders.setbookprice
|
||||
setlore:
|
||||
description: Sets the lore of the item the player is holding. Insert the lore_line_separator character to force a new line ("~" by default).
|
||||
description: Sets the lore of the item the player is holding. Insert the lore_line_separator character to force a new line ("~" by default)
|
||||
usage: /<command> <new lore>
|
||||
permission: bookswithoutborders.setlore
|
||||
savepublicbook:
|
||||
@ -74,7 +86,7 @@ commands:
|
||||
usage: /<command> <title>
|
||||
permission: bookswithoutborders.settitle
|
||||
loadbook:
|
||||
description: 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
|
||||
description: 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
|
||||
usage: /<command> <file name or number> [# of copies] [signed (true/false)]
|
||||
permission: bookswithoutborders.load
|
||||
loadpublicbook:
|
||||
@ -84,7 +96,7 @@ commands:
|
||||
reload:
|
||||
description: Reloads BwB's configuration file
|
||||
usage: /<command>
|
||||
permission: bookswithoutborders.admin
|
||||
permission: bookswithoutborders.reload
|
||||
permissions:
|
||||
bookswithoutborders.*:
|
||||
description: Grants all permissions
|
||||
@ -96,28 +108,42 @@ permissions:
|
||||
default: op
|
||||
children:
|
||||
bookswithoutborders.use: true
|
||||
bookswithoutborders.unsign: true
|
||||
bookswithoutborders.alterbooks: true
|
||||
bookswithoutborders.copy: true
|
||||
bookswithoutborders.loadpublic: true
|
||||
bookswithoutborders.savepublic: true
|
||||
bookswithoutborders.encrypt: true
|
||||
bookswithoutborders.decrypt: true
|
||||
bookswithoutborders.groupencrypt: true
|
||||
bookswithoutborders.signs: true
|
||||
bookswithoutborders.give: true
|
||||
bookswithoutborders.givepublic: true
|
||||
bookswithoutborders.settitle: true
|
||||
bookswithoutborders.setauthor: true
|
||||
bookswithoutborders.setlore: true
|
||||
bookswithoutborders.bypassauthoronlycopy: true
|
||||
bookswithoutborders.bypassauthoronlyunsign: true
|
||||
bookswithoutborders.bypassauthoronlysave: true
|
||||
bookswithoutborders.bypassbookprice: true
|
||||
bookswithoutborders.groupencrypt: true
|
||||
bookswithoutborders.setbookprice: true
|
||||
bookswithoutborders.reload: true
|
||||
bookswithoutborders.setgeneration: true
|
||||
bookswithoutborders.use:
|
||||
description: Allows player to use commands and to save/load/delete in their personal directory
|
||||
description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled
|
||||
children:
|
||||
bookswithoutborders.save: true
|
||||
bookswithoutborders.load: true
|
||||
bookswithoutborders.delete: true
|
||||
bookswithoutborders.peekbookshelf: true
|
||||
bookswithoutborders.alterbooks:
|
||||
description: Allows player to change books' data such as lore/title/author/generation/formatting and unsigning books
|
||||
children:
|
||||
bookswithoutborders.clear: true
|
||||
bookswithoutborders.unsign: true
|
||||
bookswithoutborders.settitle: true
|
||||
bookswithoutborders.setauthor: true
|
||||
bookswithoutborders.setlore: true
|
||||
bookswithoutborders.format: true
|
||||
bookswithoutborders.setgeneration: true
|
||||
bookswithoutborders.format:
|
||||
description: Allows a player to format a book
|
||||
bookswithoutborders.save:
|
||||
description: Allows player to save books to their personal directory
|
||||
bookswithoutborders.load:
|
||||
@ -125,17 +151,19 @@ permissions:
|
||||
bookswithoutborders.delete:
|
||||
description: Allows player to delete books from their personal directory
|
||||
bookswithoutborders.unsign:
|
||||
description: Allows player to use unsign command
|
||||
description: Allows player to use the unsign command
|
||||
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:
|
||||
description: Allows player to load in the public directory
|
||||
description: Allows player to load from the public directory
|
||||
bookswithoutborders.savepublic:
|
||||
description: Allows player to save in the public directory
|
||||
description: Allows player to save to the public directory
|
||||
bookswithoutborders.encrypt:
|
||||
description: Allows player to encrypt books
|
||||
bookswithoutborders.groupencrypt:
|
||||
description: Allows player to set group based encryption
|
||||
description: Allows player to use group-based encryption
|
||||
bookswithoutborders.decrypt:
|
||||
description: Allows player to decrypt books
|
||||
bookswithoutborders.signs:
|
||||
@ -152,7 +180,17 @@ permissions:
|
||||
description: Allows player to set the lore of the currently held item
|
||||
bookswithoutborders.bypassauthoronlycopy:
|
||||
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:
|
||||
description: Allows player to ignore Price_to_create_book config setting
|
||||
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)
|
||||
bookswithoutborders.peekbookshelf:
|
||||
description: Allows player to left-click a bookshelf to see the contents of the shelf
|
@ -3,7 +3,7 @@ package net.knarcraft.bookswithoutborders.encryption;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
public class AESTest {
|
||||
|
||||
@ -15,7 +15,7 @@ public class AESTest {
|
||||
AES aes = new AES(AES.generateIV(), AES.generateIV());
|
||||
|
||||
String encrypted = aes.encryptDecryptText(plainText, password, true);
|
||||
assertFalse(encrypted.equals(plainText));
|
||||
assertNotSame(encrypted, plainText);
|
||||
String decrypted = aes.encryptDecryptText(encrypted, password, false);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package net.knarcraft.bookswithoutborders.util;
|
||||
|
||||
import net.knarcraft.bookswithoutborders.utility.FileHelper;
|
||||
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class FileHelperTest {
|
||||
public class BookFileHelperTest {
|
||||
|
||||
@Test
|
||||
public void findDuplicatesTest() {
|
||||
@ -17,9 +17,9 @@ public class FileHelperTest {
|
||||
files[2] = new File("test/asd/(3)book+)Crab.yml");
|
||||
files[3] = new File("test/asd/(2)book+)Crab.yml");
|
||||
files[4] = new File("test/asd/(1)book+)Crab.yml");
|
||||
assertEquals(4, FileHelper.findDuplicates(files, "book+)Crab.yml"));
|
||||
assertEquals(1, FileHelper.findDuplicates(files, "book+)Fish.yml"));
|
||||
assertEquals(0, FileHelper.findDuplicates(files, "book+)Horse.yml"));
|
||||
assertEquals(4, BookFileHelper.findDuplicates(files, "book+)Crab.yml"));
|
||||
assertEquals(1, BookFileHelper.findDuplicates(files, "book+)Fish.yml"));
|
||||
assertEquals(0, BookFileHelper.findDuplicates(files, "book+)Horse.yml"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package net.knarcraft.bookswithoutborders.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter.getRomanNumber;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* A test class for IntegerToRomanConverter
|
||||
*/
|
||||
public class IntegerToRomanConverterTest {
|
||||
|
||||
@Test
|
||||
public void basicNumbersTest() {
|
||||
assertEquals("I", getRomanNumber(1));
|
||||
assertEquals("II", getRomanNumber(2));
|
||||
assertEquals("III", getRomanNumber(3));
|
||||
assertEquals("IV", getRomanNumber(4));
|
||||
assertEquals("V", getRomanNumber(5));
|
||||
assertEquals("X", getRomanNumber(10));
|
||||
assertEquals("XV", getRomanNumber(15));
|
||||
assertEquals("XX", getRomanNumber(20));
|
||||
assertEquals("L", getRomanNumber(50));
|
||||
assertEquals("C", getRomanNumber(100));
|
||||
assertEquals("D", getRomanNumber(500));
|
||||
assertEquals("M", getRomanNumber(1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nineFourTest() {
|
||||
assertEquals("IV", getRomanNumber(4));
|
||||
assertEquals("IX", getRomanNumber(9));
|
||||
assertEquals("XIV", getRomanNumber(14));
|
||||
assertEquals("XIX", getRomanNumber(19));
|
||||
assertEquals("XXIV", getRomanNumber(24));
|
||||
assertEquals("XL", getRomanNumber(40));
|
||||
assertEquals("IL", getRomanNumber(49));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user