30 Commits
1.3.0 ... 1.3.4

Author SHA1 Message Date
f5ba4db5ac Bumps version for release
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2023-06-24 12:15:22 +02:00
29d5247c9b Bumps KnarLib version
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2023-06-24 12:09:31 +02:00
c389c6fbcb Minor changes
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
Changes bookshelf peeking to require sneaking
Makes sure plain books are displayed in the book list
2023-06-24 12:03:37 +02:00
1158820f97 Fixes some issues
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
Fixes the API version being read incorrectly by Paper
Fixes the check for missing title or author for a book in a bookshelf
2023-06-23 18:10:45 +02:00
f9908db88f 1.20 update
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
Adds an optional feature which displays the contents of a bookshelf when left-clicked. Sneaking must be used to destroy the bookshelf when enabled.
Updates depreciated sign code.
Bumps the API version to 1.20
Builds against 1.20
2023-06-23 17:57:54 +02:00
738dbe6e30 Updates version to 1.3.4-SNAPSHOT
All checks were successful
EpicKnarvik97/Books-Without-Borders/pipeline/head This commit looks good
2022-11-26 17:20:41 +01:00
3838750f64 Adds Jenkinsfile
Some checks failed
EpicKnarvik97/Books-Without-Borders/pipeline/head There was a failure building this commit
2022-11-26 15:54:17 +01:00
16954f22e3 Merge branch 'master' into knarlib 2022-11-26 15:52:17 +01:00
6359af02e6 Fixes table formatting 2022-11-14 00:49:58 +00:00
6ae15acc72 Bumps version to 1.3.3 2022-11-08 20:37:06 +01:00
2c80e4117e Lists real file names, in addition to converted file names 2022-11-08 20:36:17 +01:00
6d539a14df Fixes the files provided in the tab-completion not translating to actual files 2022-11-08 20:34:53 +01:00
51371736e4 Removes KnarLib.setPlugin call 2022-11-07 22:24:33 +01:00
2383dfd2ef Renames FileHelper to BookFileHelper
Makes sure books are saved and read as UTF-8
2022-11-07 09:23:25 +01:00
04d4bc8303 Uses KnarLib where possible 2022-11-07 01:43:38 +01:00
184f78d935 Adds a missing / to the README 2022-11-05 12:35:27 +01:00
95b0b42fd1 Bumps version to 1.3.2 2022-11-04 18:47:31 +01:00
73eb903517 Implements RGB support for setLore and setName 2022-11-04 18:39:58 +01:00
b577b1ff75 Bumps version to 1.3.1 2022-11-04 16:19:27 +01:00
c27756f7dc Clears author, generation and title data just in case 2022-11-04 16:19:15 +01:00
90bab4a148 Fixes various bugs in the new features
Fixes the clearBook command not being aborted when no book is held.
Adds missing clearBook success message
Fixes a bug where too many empty books would be taken.
Adds some missing null checks when looking for empty books.
2022-11-04 16:06:27 +01:00
f005b8f8e5 Adds clearBook command #7 and improves README 2022-11-04 13:01:56 +01:00
0fdfd81579 Prevents WIP books from being lost
This change adds a workaround for writable books used for payment. Only books that don't contain any text can be used for payment, thus preventing any books with important text from being lost.
2022-11-04 03:37:38 +01:00
6820b71dde Updates spigot version to 1.19.2 2022-11-04 03:07:32 +01:00
28b7849ff5 Tries to fix #9
Sets the amount of books to the same amount as the old book
2022-11-04 03:05:30 +01:00
e893684dec Prevents consumption of dye when clicking plugin signs 2022-08-20 14:54:40 +02:00
f1a8db4f5e Merge branch 'master' of https://git.knarcraft.net/EpicKnarvik97/Books-Without-Borders
 Conflicts:
	README.md
2022-08-20 14:16:12 +02:00
47fabe5806 Merge pull request 'Excapes <' (#8) from epicknarvik97-patch-1 into master
Reviewed-on: #8
2022-08-20 14:13:31 +02:00
ec6def15f9 Excapes < 2022-08-20 14:12:20 +02:00
13cbe883ed Adds missing mention of generation for alterbooks permission node 2022-08-14 11:36:46 +02:00
37 changed files with 782 additions and 375 deletions

33
Jenkinsfile vendored Normal file
View 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
}
}
}
}

156
README.md
View File

@ -25,10 +25,12 @@ Books without Borders has got your back!
- Add lore to any item with a simple command - Add lore to any item with a simple command
- Supports adding and saving color to title, lore, and book contents - Supports adding and saving color to title, lore, and book contents
- Color and formatting codes can be manually turned into formatting using /formatbook - Color and formatting codes can be manually turned into formatting using /formatbook
- 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 - Formatting and color codes can be turned into formatting once any book is signed. This is enabled through a config
value value
- Change generation of books. Create tattered books for your RPG server! - Change generation of books. Create tattered books for your RPG server!
- Optionally, make it impossible to duplicate the original version of a book - Optionally, make it impossible to duplicate the original version of a book
- Optionally, hit a bookshelf to display the contained books. Sneak to destroy bookshelves with this enabled
#### Group encryption #### Group encryption
@ -39,70 +41,65 @@ Books without Borders has got your back!
An in-game description of available commands is available through the /bwb command. An in-game description of available commands is available through the /bwb command.
- /bookswithoutborders - Displays information about commands (and permissions if the user has bookswithoutborders.admin) | Command | Arguments | Description |
- /copybook <# of copies> - Copies the book the player is holding |----------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
- /decryptbook <key> - Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used | /bookswithoutborders | None | Displays information about commands (and permissions if the user has bookswithoutborders.admin) |
to encrypt the held book | /copybook | \<# of copies> | Copies the book the player is holding |
- /deletebook <file name or number> - Deletes the specified file in the player's directory | /decryptbook | \<key> | Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used to encrypt the held book |
- /deletepublicbook <file name or number> - Same as deletebook, but deletes files in the public directory | /deletebook | \<file name or number> | Deletes the specified file in the player's directory |
- encryptbook <key> \[encryption style] - Encrypts the book the player is holding. "key" is required and can be any | /deletepublicbook | \<file name or number> | Same as deletebook, but deletes files in the public directory |
phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or "" | /encryptbook | \<key> \[encryption style] | Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or "" |
- /formatbook - Formats the held written book (converts color and formatting codes to the corresponding formatted text) | /formatbook | None | Formats the held written book (converts color and formatting codes to the corresponding formatted text) |
- /givebook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Gives the selected player a | /givebook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Gives the selected player a book from your personal directory |
book from your personal directory | /givepublicbook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Same as givebook, but uses books from the public directory |
- /givepublicbook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Same as givebook, but | /loadbook | \<file name or number> \[# of copies] \[signed (true/false)] | Creates a book from the specified file and gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book will be signed, if false it will be unsigned |
uses books from the public directory | /loadpublicbook | \<file name or number> \[# of copies] \[signed (true/false)] | Same as loadbook, but views files in the public directory |
- /loadbook <file name or number> \[# of copies] \[signed (true/false)] - Creates a book from the specified file and | /reload | None | Reloads BwB's configuration file |
gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book | /savebook | \[overwrite (true/false)] | Saves the book the player is holding to a text file in a private directory. If true is specified, a book of the same name by the same author will be overwritten by the new book |
will be signed, if false it will be unsigned | /savepublicbook | \[overwrite (true/false)] | Same as savebook, but saves files in the public directory |
- /loadpublicbook <file name or number> \[# of copies] \[signed (true/false)] - Same as loadbook, but views files in the | /setbookauthor | \<author> | Sets the author of the book the player is holding |
public directory | /setbookgeneration | \<generation> | Sets the generation of the held book (ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED) |
- /reload - Reloads BwB's configuration file | /setbookprice | \<item/eco> \<quantity> | Sets the per-book price to create a book via commands. If "Item", the item in the player's hand in the amount of \<quantity> will be the price. If "Eco", a Vault based economy will be used for price. If neither \<Item/Eco> nor \<quantity> are specified, the current price to create books will be removed. |
- /savebook \[overwrite (true/false)] - Saves the book the player is holding to a text file in a private directory. If | /setlore | \<new lore> | Sets the lore of the item the player is holding. Insert the lore_line_separator character to force a new line ("~" by default) |
true is specified, a book of the same name by the same author will be overwritten by the new book | /settitle | \<title> | Sets the title of the book/item the player is holding |
- /savepublicbook \[overwrite (true/false)] - Same as savebook, but saves files in the public directory | /unsignbook | None | Un-signs the book the player is holding |
- /setbookauthor <author> - Sets the author of the book the player is holding | /clearbook | None | Removes all text from the held un-signed book |
- /setbookgeneration <generation> - Sets the generation (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 - Un-signs the book the player is holding
### Permissions: ### Permissions:
- bookswithoutborders.* - Grants all permissions | Node | Description |
- bookswithoutborders.admin - Grants all permissions |--------------------------------------------|----------------------------------------------------------------------------------------------------------|
- bookswithoutborders.use - Allows player to use commands to save/load/delete in their personal directory | bookswithoutborders.* | Grants all permissions |
- bookswithoutborders.alterbooks - Allows player to change books' data such as lore/title/author/formatting and | bookswithoutborders.admin | Grants all permissions |
unsigning books | bookswithoutborders.use | Allows player to use commands to save/load/delete in their personal directory |
- bookswithoutborders.reload - Allows player to reload this plugin | bookswithoutborders.alterbooks | Allows player to change books' data such as lore/title/author/generation/formatting and un-signing books |
- bookswithoutborders.format - Allows a player to format a book | bookswithoutborders.reload | Allows player to reload this plugin |
- bookswithoutborders.save - Allows a player to save books to their personal directory | bookswithoutborders.format | Allows a player to format a book |
- bookswithoutborders.load - Allows player to load books from their personal directory | bookswithoutborders.save | Allows a player to save books to their personal directory |
- bookswithoutborders.delete - Allows player to delete books from their personal directory | bookswithoutborders.load | Allows player to load books from their personal directory |
- bookswithoutborders.unsign - Allows player to un-sign books | bookswithoutborders.delete | Allows player to delete books from their personal directory |
- bookswithoutborders.copy - Allows player to copy books | bookswithoutborders.unsign | Allows player to un-sign books |
- bookswithoutborders.loadpublic - Allows player to load from the public directory | bookswithoutborders.copy | Allows player to copy books |
- bookswithoutborders.savepublic - Allows player to save to the public directory | bookswithoutborders.loadpublic | Allows player to load from the public directory |
- bookswithoutborders.encrypt - Allows player to encrypt books | bookswithoutborders.savepublic | Allows player to save to the public directory |
- bookswithoutborders.groupencrypt - Allows player to use group-based encryption | bookswithoutborders.encrypt | Allows player to encrypt books |
- bookswithoutborders.decrypt - Allows player to decrypt books | bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
- bookswithoutborders.decrypt.agroup - Allows player to decrypt books group-encrypted for group "agroup" | bookswithoutborders.decrypt | Allows player to decrypt books |
- bookswithoutborders.signs - Allows player to create signs that give/encrypt/decrypt books | bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
- bookswithoutborders.give - Allows player to give another player one of their privately saved books | bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
- bookswithoutborders.givepublic - Allows a player to give another player a book from the public directory | bookswithoutborders.give | Allows player to give another player one of their privately saved books |
- bookswithoutborders.settitle - Allows player to set the title of the currently held book | bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
- bookswithoutborders.setauthor - Allows player to set the author of the currently held book | bookswithoutborders.settitle | Allows player to set the title of the currently held book |
- bookswithoutborders.setlore - Allows player to set the lore of the currently held item | bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
- bookswithoutborders.bypassauthoronlycopy - Allows player to ignore Author_Only_Copy config setting | bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
- bookswithoutborders.bypassauthoronlyunsign - Allows player to ignore Author_Only_Unsign config setting | bookswithoutborders.bypassauthoronlycopy | Allows player to ignore Author_Only_Copy config setting |
- bookswithoutborders.bypassauthoronlysave - Allows player to ignore Author_Only_Save config setting | bookswithoutborders.bypassauthoronlyunsign | Allows player to ignore Author_Only_Unsign config setting |
- bookswithoutborders.bypassbookprice - Allows player to ignore Price_to_create_book config setting | bookswithoutborders.bypassauthoronlysave | Allows player to ignore Author_Only_Save config setting |
- bookswithoutborders.setbookprice - Allows player to set the cost of creating a book | bookswithoutborders.bypassbookprice | Allows player to ignore Price_to_create_book config setting |
- bookswithoutborders.setgeneration - Allows player to change the generation of a book (Original, Copy, Copy of Copy) | 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 ### Signs
@ -111,13 +108,13 @@ line.
#### Give sign #### Give sign
The **_give_** sign must have **\[Give]** on its second line. The third and fourth line contains the book to be loaded. The **_give_**-sign must have **\[Give]** on its second line. The third and fourth line contains the book to be loaded.
This can either be a numerical id pointing to a publicly saved book, or the full text identifier of the book (book name, This can either be a numerical id pointing to a publicly saved book, or the full text identifier of the book (book name,
author). author).
#### Encrypt sign #### Encrypt sign
The **_encrypt_** sign must have **\[Encrypt]** on its second line. The third line must contain the encryption key The The **_encrypt_**-sign must have **\[Encrypt]** on its second line. The third line must contain the encryption key The
fourth line can be empty or contain "dna" for dna-based encryption. fourth line can be empty or contain "dna" for dna-based encryption.
#### Decrypt sign #### Decrypt sign
@ -126,19 +123,20 @@ The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third li
### Configuration options: ### Configuration options:
- Save_Books_in_Yaml_Format - Whether to use YAML for saved books instead of just storing them as text | Option | Description |
- 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 | Save_Books_in_Yaml_Format | Whether to use YAML for saved books instead of just storing them as text |
- Lore_line_separator - The separator used to denote a new line in the book/item lore | Max_Number_of_Duplicates | The maximum number of duplicates of a saved book allowed |
- Books_for_new_players - A list of books given to new players the first time they join the server | Author_Separator | The separator used to separate the book title and the book author |
- Message_for_new_players - An optional message displayed to new players the first time they join the server | Lore_line_separator | The separator used to denote a new line in the book/item lore |
- Price_to_create_book.Item_type - The item type used as currency for copying books. Use "Economy" to use money instead | Books_for_new_players | A list of books given to new players the first time they join the server |
of items | Message_for_new_players | An optional message displayed to new players the first time they join the server |
- Price_to_create_book.Required_quantity - The quantity of currency required to pay for each book produced | Price_to_create_book.Item_type | The item type used as currency for copying books. Use "Economy" to use money instead of items |
- Admin_Auto_Decrypt - Whether any admin can decrypt any book regardless of the group it was encrypted for | Price_to_create_book.Required_quantity | The quantity of currency required to pay for each book produced |
- Author_Only_Copy - Whether to only allow the author of a book to create copies | Admin_Auto_Decrypt | Whether any admin can decrypt any book regardless of the group it was encrypted for |
- Author_Only_Unsign - Whether to only allow the author of a book to unsign it | Author_Only_Copy | Whether to only allow the author of a book to create copies |
- Author_Only_Save - Whether to only allow saving a player's own books with /savebook | Author_Only_Unsign | Whether to only allow the author of a book to unsign it |
- Format_Book_After_Signing - Whether to automatically format every book when it's signed | Author_Only_Save | Whether to only allow saving a player's own books with /savebook |
- Change_Generation_On_Copy - Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. | Format_Book_After_Signing | Whether to automatically format every book when it's signed |
This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. | Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents. |

48
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>net.knarcraft</groupId> <groupId>net.knarcraft</groupId>
<artifactId>BooksWithoutBorders</artifactId> <artifactId>BooksWithoutBorders</artifactId>
<version>1.3.0</version> <version>1.3.4</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<licenses> <licenses>
@ -20,7 +20,7 @@
<description>A continuation of the original Books Without Borders</description> <description>A continuation of the original Books Without Borders</description>
<properties> <properties>
<java.version>17</java.version> <java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@ -31,8 +31,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>17</source> <source>${java.version}</source>
<target>17</target> <target>${java.version}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@ -47,6 +47,20 @@
</goals> </goals>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>net.knarcraft:knarlib</artifact>
<includes>
<include>net/knarcraft/knarlib/**</include>
</includes>
</filter>
<filter>
<excludes>
<exclude>*.MF</exclude>
<exclude>*.yml</exclude>
</excludes>
</filter>
</filters>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -61,6 +75,10 @@
</build> </build>
<repositories> <repositories>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<repository> <repository>
<id>spigotmc-repo</id> <id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@ -70,12 +88,22 @@
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
</repository> </repository>
</repositories> </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> <dependencies>
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.19.1-R0.1-SNAPSHOT</version> <version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -84,11 +112,17 @@
<version>1.7</version> <version>1.7</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>23.0.0</version> <version>24.0.1</version>
<scope>compile</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,6 +1,7 @@
package net.knarcraft.bookswithoutborders; package net.knarcraft.bookswithoutborders;
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders; import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
import net.knarcraft.bookswithoutborders.command.CommandClear;
import net.knarcraft.bookswithoutborders.command.CommandCopy; import net.knarcraft.bookswithoutborders.command.CommandCopy;
import net.knarcraft.bookswithoutborders.command.CommandDecrypt; import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
import net.knarcraft.bookswithoutborders.command.CommandDelete; import net.knarcraft.bookswithoutborders.command.CommandDelete;
@ -23,10 +24,11 @@ import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
import net.knarcraft.bookswithoutborders.command.CommandUnSign; import net.knarcraft.bookswithoutborders.command.CommandUnSign;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.listener.BookEventListener; import net.knarcraft.bookswithoutborders.listener.BookEventListener;
import net.knarcraft.bookswithoutborders.listener.BookshelfListener;
import net.knarcraft.bookswithoutborders.listener.PlayerEventListener; import net.knarcraft.bookswithoutborders.listener.PlayerEventListener;
import net.knarcraft.bookswithoutborders.listener.SignEventListener; import net.knarcraft.bookswithoutborders.listener.SignEventListener;
import net.knarcraft.bookswithoutborders.utility.FileHelper; import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.UpdateChecker; import net.knarcraft.knarlib.util.UpdateChecker;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
@ -83,7 +85,7 @@ public class BooksWithoutBorders extends JavaPlugin {
} else if (sender instanceof Player player) { } else if (sender instanceof Player player) {
UUID playerUUID = player.getUniqueId(); UUID playerUUID = player.getUniqueId();
if (!playerBooksList.containsKey(playerUUID)) { if (!playerBooksList.containsKey(playerUUID)) {
List<String> newFiles = FileHelper.listFiles(sender, false); List<String> newFiles = BookFileHelper.listFiles(sender, false);
playerBooksList.put(playerUUID, newFiles); playerBooksList.put(playerUUID, newFiles);
} }
return playerBooksList.get(playerUUID); return playerBooksList.get(playerUUID);
@ -99,7 +101,7 @@ public class BooksWithoutBorders extends JavaPlugin {
* @param updatePublic <p>Whether to update public books</p> * @param updatePublic <p>Whether to update public books</p>
*/ */
public static void updateBooks(CommandSender sender, boolean updatePublic) { public static void updateBooks(CommandSender sender, boolean updatePublic) {
List<String> newFiles = FileHelper.listFiles(sender, updatePublic); List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
if (updatePublic) { if (updatePublic) {
publicBooksList = newFiles; publicBooksList = newFiles;
} else if (sender instanceof Player player) { } else if (sender instanceof Player player) {
@ -121,7 +123,7 @@ public class BooksWithoutBorders extends JavaPlugin {
consoleSender = this.getServer().getConsoleSender(); consoleSender = this.getServer().getConsoleSender();
playerBooksList = new HashMap<>(); playerBooksList = new HashMap<>();
BooksWithoutBordersConfig.initialize(this); BooksWithoutBordersConfig.initialize(this);
publicBooksList = FileHelper.listFiles(consoleSender, true); publicBooksList = BookFileHelper.listFiles(consoleSender, true);
PluginManager pluginManager = this.getServer().getPluginManager(); PluginManager pluginManager = this.getServer().getPluginManager();
@ -129,6 +131,7 @@ public class BooksWithoutBorders extends JavaPlugin {
pluginManager.registerEvents(new PlayerEventListener(), this); pluginManager.registerEvents(new PlayerEventListener(), this);
pluginManager.registerEvents(new SignEventListener(), this); pluginManager.registerEvents(new SignEventListener(), this);
pluginManager.registerEvents(new BookEventListener(), this); pluginManager.registerEvents(new BookEventListener(), this);
pluginManager.registerEvents(new BookshelfListener(), this);
} else { } else {
this.getPluginLoader().disablePlugin(this); this.getPluginLoader().disablePlugin(this);
} }
@ -173,6 +176,7 @@ public class BooksWithoutBorders extends JavaPlugin {
registerCommand("reload", new CommandReload()); registerCommand("reload", new CommandReload());
registerCommand("formatBook", new CommandFormat()); registerCommand("formatBook", new CommandFormat());
registerCommand("setBookGeneration", new CommandSetGeneration()); registerCommand("setBookGeneration", new CommandSetGeneration());
registerCommand("clearBook", new CommandClear());
} }
/** /**

View File

@ -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<>();
}
}

View File

@ -5,7 +5,8 @@ import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.EconomyHelper; import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; 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.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -142,7 +143,7 @@ public class CommandCopy implements TabExecutor {
@NotNull String[] args) { @NotNull String[] args) {
int argumentCount = args.length; int argumentCount = args.length;
if (argumentCount == 1) { if (argumentCount == 1) {
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 20), args[0]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 20), args[0]);
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -2,9 +2,9 @@ package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.FileHelper; import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -41,7 +41,7 @@ public class CommandDelete implements TabExecutor {
boolean deleteBook(CommandSender sender, String[] args, boolean deletePublic) { boolean deleteBook(CommandSender sender, String[] args, boolean deletePublic) {
//List deletable files //List deletable files
if (args.length == 0) { if (args.length == 0) {
FileHelper.printBooks(sender, deletePublic); BookFileHelper.printBooks(sender, deletePublic);
return true; return true;
} }
//Delete the file //Delete the file
@ -82,7 +82,7 @@ public class CommandDelete implements TabExecutor {
//Get the file to be deleted //Get the file to be deleted
String bookDirectory = BookHelper.getBookDirectoryPathString( String bookDirectory = BookHelper.getBookDirectoryPathString(
isPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender); isPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
File file = FileHelper.getBookFile(bookDirectory + fileName); File file = BookFileHelper.getBookFile(bookDirectory + fileName);
//Send message if no such file could be found //Send message if no such file could be found
if (file == null) { if (file == null) {

View File

@ -5,7 +5,7 @@ import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.state.ItemSlot;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper; import net.knarcraft.knarlib.util.TabCompletionHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;

View File

@ -1,10 +1,11 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.FileHelper;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; 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.Server;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -49,7 +50,7 @@ public class CommandGive implements TabExecutor {
} }
if (args.length == 0) { if (args.length == 0) {
FileHelper.printBooks(sender, givePublic); BookFileHelper.printBooks(sender, givePublic);
return true; return true;
} }
@ -132,12 +133,12 @@ public class CommandGive implements TabExecutor {
return null; return null;
} else if (argumentCount == 3) { } else if (argumentCount == 3) {
//Number of copies //Number of copies
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleansAndNumbers(1, 3), args[2]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), args[2]);
} else if (argumentCount == 4) { } else if (argumentCount == 4) {
//Signed //Signed
try { try {
Integer.parseInt(args[2]); Integer.parseInt(args[2]);
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleans(), args[3]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), args[3]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -1,10 +1,11 @@
package net.knarcraft.bookswithoutborders.command; package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.FileHelper;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; 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.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -49,7 +50,7 @@ public class CommandLoad implements TabExecutor {
//Show books available to the player //Show books available to the player
if (argumentCount == 0) { if (argumentCount == 0) {
FileHelper.printBooks(sender, loadPublic); BookFileHelper.printBooks(sender, loadPublic);
return true; return true;
} }
@ -114,12 +115,12 @@ public class CommandLoad implements TabExecutor {
args[0]); args[0]);
} else if (argumentCount == 2) { } else if (argumentCount == 2) {
//Number of copies //Number of copies
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleansAndNumbers(1, 3), args[1]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleansAndNumbers(1, 3), args[1]);
} else if (argumentCount == 3) { } else if (argumentCount == 3) {
//Signed //Signed
try { try {
Integer.parseInt(args[1]); Integer.parseInt(args[1]);
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleans(), args[2]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), args[2]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,9 +4,9 @@ import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import net.knarcraft.bookswithoutborders.state.ItemSlot; import net.knarcraft.bookswithoutborders.state.ItemSlot;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookHelper; import net.knarcraft.bookswithoutborders.utility.BookHelper;
import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper; import net.knarcraft.bookswithoutborders.utility.BookToFromTextHelper;
import net.knarcraft.bookswithoutborders.utility.FileHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -104,7 +104,7 @@ public class CommandSave implements TabExecutor {
} }
//Find any duplicates of the book //Find any duplicates of the book
int foundDuplicates = FileHelper.findDuplicates(foundFiles, fileName); int foundDuplicates = BookFileHelper.findDuplicates(foundFiles, fileName);
//Deal with duplicates //Deal with duplicates
if (foundDuplicates > 0) { if (foundDuplicates > 0) {

View File

@ -4,7 +4,8 @@ import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.utility.EconomyHelper; import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; 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.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -144,7 +145,7 @@ public class CommandSetBookPrice implements TabExecutor {
if (argumentCount == 1) { if (argumentCount == 1) {
return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]); return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]);
} else if (argumentCount == 2) { } else if (argumentCount == 2) {
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 3), args[1]); return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 3), args[1]);
} }
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -3,7 +3,8 @@ package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; 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.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -44,7 +45,7 @@ public class CommandSetLore implements TabExecutor {
String rawLore = String.join(" ", args); String rawLore = String.join(" ", args);
//Format lore //Format lore
rawLore = ChatColor.translateAlternateColorCodes('&', rawLore); rawLore = ColorHelper.translateColorCodes(rawLore, ColorConversion.RGB);
String[] loreParts = rawLore.split(BooksWithoutBordersConfig.getLoreSeparator()); String[] loreParts = rawLore.split(BooksWithoutBordersConfig.getLoreSeparator());
List<String> newLore = new ArrayList<>(Arrays.asList(loreParts)); List<String> newLore = new ArrayList<>(Arrays.asList(loreParts));

View File

@ -2,7 +2,8 @@ package net.knarcraft.bookswithoutborders.command;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.InventoryHelper; 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.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -22,7 +23,8 @@ import java.util.List;
public class CommandSetTitle implements TabExecutor { public class CommandSetTitle implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!"); BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
return false; return false;
@ -40,7 +42,7 @@ public class CommandSetTitle implements TabExecutor {
} }
String title = String.join(" ", args); String title = String.join(" ", args);
title = ChatColor.translateAlternateColorCodes('&', title); title = ColorHelper.translateColorCodes(title, ColorConversion.RGB);
ItemMeta itemMetadata = heldItem.getItemMeta(); ItemMeta itemMetadata = heldItem.getItemMeta();
if (itemMetadata == null) { if (itemMetadata == null) {
@ -66,7 +68,8 @@ public class CommandSetTitle implements TabExecutor {
} }
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
@NotNull String[] args) {
List<String> options = new ArrayList<>(); List<String> options = new ArrayList<>();
options.add("<new title>"); options.add("<new title>");
return options; return options;

View File

@ -50,6 +50,7 @@ public class CommandUnSign implements TabExecutor {
public void unSignHeldBook(Player player, boolean mainHand) { public void unSignHeldBook(Player player, boolean mainHand) {
//Get the old book //Get the old book
BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand); BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand);
ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand);
//Only allow the owner to un-sign the book //Only allow the owner to un-sign the book
if (BooksWithoutBordersConfig.getAuthorOnlyUnsign() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) { if (BooksWithoutBordersConfig.getAuthorOnlyUnsign() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) {
@ -60,6 +61,7 @@ public class CommandUnSign implements TabExecutor {
//UnSign the book //UnSign the book
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK); ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK);
newBook.setAmount(heldBook.getAmount());
newBook.setItemMeta(oldBook); newBook.setItemMeta(oldBook);
InventoryHelper.replaceHeldItem(player, newBook, mainHand); InventoryHelper.replaceHeldItem(player, newBook, mainHand);

View File

@ -2,7 +2,7 @@ package net.knarcraft.bookswithoutborders.config;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.utility.EconomyHelper; import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
import org.bukkit.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.configuration.Configuration; import org.bukkit.configuration.Configuration;
@ -40,6 +40,7 @@ public class BooksWithoutBordersConfig {
private static boolean adminDecrypt; private static boolean adminDecrypt;
private static boolean formatBooks; private static boolean formatBooks;
private static boolean changeGenerationOnCopy; private static boolean changeGenerationOnCopy;
private static boolean enableBookshelfPeeking;
/** /**
* Initializes the books without borders settings class * Initializes the books without borders settings class
@ -127,6 +128,15 @@ public class BooksWithoutBordersConfig {
return authorOnlySave; return authorOnlySave;
} }
/**
* Gets whether players can left-click a bookshelf to peek at the contained books
*
* @return <p>True if players can peek at the contained books</p>
*/
public static boolean getEnableBookshelfPeeking() {
return enableBookshelfPeeking;
}
/** /**
* Gets whether to use YML, not TXT, for saving books * Gets whether to use YML, not TXT, for saving books
* *
@ -272,6 +282,7 @@ public class BooksWithoutBordersConfig {
config.set(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode(), firstBooks); config.set(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode(), firstBooks);
config.set(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(), welcomeMessage); config.set(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(), welcomeMessage);
config.set(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(), formatBooks); config.set(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(), formatBooks);
config.set(ConfigOption.ENABLE_BOOKSHELF_PEEKING.getConfigNode(), enableBookshelfPeeking);
String itemTypeNode = ConfigOption.PRICE_ITEM_TYPE.getConfigNode(); String itemTypeNode = ConfigOption.PRICE_ITEM_TYPE.getConfigNode();
if (bookPriceType != null) { if (bookPriceType != null) {
@ -319,7 +330,8 @@ public class BooksWithoutBordersConfig {
Configuration config = BooksWithoutBorders.getInstance().getConfig(); Configuration config = BooksWithoutBorders.getInstance().getConfig();
try { try {
useYml = getBoolean(config, ConfigOption.USE_YAML); useYml = getBoolean(config, ConfigOption.USE_YAML);
bookDuplicateLimit = getInt(config, ConfigOption.MAX_DUPLICATES); bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR); titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR); loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT); adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
@ -330,6 +342,7 @@ public class BooksWithoutBordersConfig {
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS); welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING); formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY); changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
//Convert string into material //Convert string into material
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE); String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
@ -347,7 +360,8 @@ public class BooksWithoutBordersConfig {
bookPriceType = material; bookPriceType = material;
} }
} }
bookPriceQuantity = getDouble(config, ConfigOption.PRICE_QUANTITY); bookPriceQuantity = config.getDouble(ConfigOption.PRICE_QUANTITY.getConfigNode(),
(Double) ConfigOption.PRICE_QUANTITY.getDefaultValue());
//Make sure titleAuthorSeparator is a valid value //Make sure titleAuthorSeparator is a valid value
titleAuthorSeparator = cleanString(titleAuthorSeparator); titleAuthorSeparator = cleanString(titleAuthorSeparator);
@ -366,28 +380,6 @@ public class BooksWithoutBordersConfig {
return true; return true;
} }
/**
* Gets the double value of the given config option
*
* @param config <p>The configuration to read from</p>
* @param configOption <p>The configuration option to get the value for</p>
* @return <p>The value of the option</p>
*/
private static double getDouble(Configuration config, ConfigOption configOption) {
return config.getDouble(configOption.getConfigNode(), (Double) configOption.getDefaultValue());
}
/**
* Gets the integer value of the given config option
*
* @param config <p>The configuration to read from</p>
* @param configOption <p>The configuration option to get the value for</p>
* @return <p>The value of the option</p>
*/
private static int getInt(Configuration config, ConfigOption configOption) {
return config.getInt(configOption.getConfigNode(), (Integer) configOption.getDefaultValue());
}
/** /**
* Gets the string value of the given config option * Gets the string value of the given config option
* *

View File

@ -73,7 +73,13 @@ public enum ConfigOption {
/** /**
* Whether to automatically format every signed book * Whether to automatically format every signed book
*/ */
FORMAT_AFTER_SIGNING("Options.Format_Book_After_Signing", false); FORMAT_AFTER_SIGNING("Options.Format_Book_After_Signing", false),
/**
* Whether hitting a bookshelf should display information about the contained books
*/
ENABLE_BOOKSHELF_PEEKING("Options.Enable_Book_Peeking", false),
;
private final String configNode; private final String configNode;
private final Object defaultValue; private final Object defaultValue;

View File

@ -97,9 +97,12 @@ public class GenenCrypt {
String s = charList[i]; String s = charList[i];
String[] sa = codonTable.get(s); String[] sa = codonTable.get(s);
switch (s) { switch (s) {
case "\t" -> System.out.println(i + "\t" + "\\t" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]); case "\t" ->
case "\n" -> System.out.println(i + "\t" + "\\n" + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]); System.out.println(i + "\t" + "\\t" + "\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 "\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]); default -> System.out.println(i + "\t" + s + "\t" + sa[0] + ", " + sa[1] + ", " + sa[2] + ", " + sa[3]);
} }
} }

View File

@ -52,6 +52,7 @@ public class SubstitutionCipher {
// but in reverse. Could probably be combined into one // but in reverse. Could probably be combined into one
// method with a flag for encryption / decryption, but // method with a flag for encryption / decryption, but
// I'm lazy. // I'm lazy.
@SuppressWarnings("unused")
public String decrypt(String in, String key) { public String decrypt(String in, String key) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
if (key != null && key.length() > 0) { if (key != null && key.length() > 0) {
@ -79,6 +80,7 @@ public class SubstitutionCipher {
// the one time pad encryption is very secure, and // the one time pad encryption is very secure, and
// encryption works just like decryption, but is // encryption works just like decryption, but is
// vulnerable if the same key is used more than once. // vulnerable if the same key is used more than once.
@SuppressWarnings("unused")
public String oneTimePad(String in, String key) { public String oneTimePad(String in, String key) {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
for (int i = 0; i < in.length(); i++) { for (int i = 0; i < in.length(); i++) {

View File

@ -0,0 +1,159 @@
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().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);
}
}

View File

@ -3,15 +3,18 @@ package net.knarcraft.bookswithoutborders.listener;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import net.knarcraft.bookswithoutborders.utility.BookLoader; import net.knarcraft.bookswithoutborders.utility.BookLoader;
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper; import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
import net.knarcraft.bookswithoutborders.utility.FileHelper;
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper; import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
import org.bukkit.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
@ -26,7 +29,7 @@ import java.io.File;
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder; import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash; import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
import static net.knarcraft.bookswithoutborders.utility.FileHelper.isBookListIndex; import static net.knarcraft.bookswithoutborders.utility.BookFileHelper.isBookListIndex;
/** /**
* A listener for relevant sign events such as clicking a decryption sign * A listener for relevant sign events such as clicking a decryption sign
@ -96,20 +99,25 @@ public class SignEventListener implements Listener {
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) || if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
Tag.WALL_SIGNS.isTagged(clickedBlockType))) { Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
event.setUseItemInHand(Event.Result.DENY);
//The player right-clicked a sign //The player right-clicked a sign
Sign sign = (Sign) event.getClickedBlock().getState(); Sign sign = (Sign) event.getClickedBlock().getState();
if (signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) { if (!signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) { return;
encryptHeldBookUsingSign(sign, heldItemType, player, hand); }
} else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
decryptHeldBookUsingSign(sign, heldItemType, player, hand); if (signLineEquals(sign, 1, "[Encrypt]", ChatColor.DARK_BLUE)) {
} else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) && encryptHeldBookUsingSign(sign, heldItemType, player, hand);
getSignLine2Color(sign) == ChatColor.DARK_GREEN) { } else if (signLineEquals(sign, 1, "[Decrypt]", ChatColor.DARK_BLUE)) {
giveBook(sign, player); decryptHeldBookUsingSign(sign, heldItemType, player, hand);
} else { } else if (signLineEquals(sign, 1, "[Give]", ChatColor.DARK_BLUE) &&
player.sendMessage("Sign command " + sign.getLine(1) + " " + sign.getLine(2) + " invalid"); getSignLine2Color(sign) == ChatColor.DARK_GREEN) {
player.sendMessage(String.valueOf(getSignLine2Color(sign))); giveBook(sign, player);
} } else {
SignSide front = sign.getSide(Side.FRONT);
player.sendMessage(String.format("Sign command %s %s is invalid", front.getLine(1),
front.getLine(2)));
player.sendMessage(String.valueOf(getSignLine2Color(sign)));
} }
} else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR } else if (heldItemType == Material.WRITTEN_BOOK && (event.getAction() == Action.LEFT_CLICK_AIR
|| event.getAction() == Action.LEFT_CLICK_BLOCK)) { || event.getAction() == Action.LEFT_CLICK_BLOCK)) {
@ -136,7 +144,7 @@ public class SignEventListener implements Listener {
player.closeInventory(); player.closeInventory();
//Converts user supplied key into integer form //Converts user supplied key into integer form
String lineText = ChatColor.stripColor(sign.getLine(2)); String lineText = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
String key = EncryptionHelper.getNumberKeyFromStringKey(lineText); String key = EncryptionHelper.getNumberKeyFromStringKey(lineText);
ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false); ItemStack book = EncryptionHelper.loadEncryptedBook(player, key, false);
@ -154,9 +162,9 @@ public class SignEventListener implements Listener {
* @return <p>The color of the sign</p> * @return <p>The color of the sign</p>
*/ */
private ChatColor getSignLine2Color(Sign sign) { private ChatColor getSignLine2Color(Sign sign) {
String line = sign.getLine(2); String line = sign.getSide(Side.FRONT).getLine(2);
if (!ChatColor.stripColor(line).equals(line)) { if (!ChatColor.stripColor(line).equals(line)) {
return ChatColor.getByChar(sign.getLine(2).substring(1, 2)); return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0));
} else { } else {
return null; return null;
} }
@ -172,7 +180,7 @@ public class SignEventListener implements Listener {
* @return <p>True if the given string is what's on the sign</p> * @return <p>True if the given string is what's on the sign</p>
*/ */
private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) { private boolean signLineEquals(Sign sign, int lineNumber, String compareTo, ChatColor color) {
String line = sign.getLine(lineNumber); String line = sign.getSide(Side.FRONT).getLine(lineNumber);
return line.equalsIgnoreCase(color + compareTo); return line.equalsIgnoreCase(color + compareTo);
} }
@ -193,7 +201,7 @@ public class SignEventListener implements Listener {
//Tests if a full file name has been supplied and points to an actual file //Tests if a full file name has been supplied and points to an actual file
String signFile = getBookFolder() + lines[2] + lines[3]; String signFile = getBookFolder() + lines[2] + lines[3];
if (FileHelper.bookFileExists(signFile)) { if (BookFileHelper.bookFileExists(signFile)) {
markGiveSignValidity(event, true); markGiveSignValidity(event, true);
return; return;
} else { } else {
@ -279,7 +287,7 @@ public class SignEventListener implements Listener {
*/ */
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) { private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
ItemStack eBook; ItemStack eBook;
String[] lines = sign.getLines(); String[] lines = sign.getSide(Side.FRONT).getLines();
boolean mainHand = hand == EquipmentSlot.HAND; boolean mainHand = hand == EquipmentSlot.HAND;
if (heldItemType == Material.WRITTEN_BOOK) { if (heldItemType == Material.WRITTEN_BOOK) {
player.closeInventory(); player.closeInventory();
@ -298,7 +306,7 @@ public class SignEventListener implements Listener {
* @param player <p>The player which clicked the sign</p> * @param player <p>The player which clicked the sign</p>
*/ */
private void giveBook(Sign sign, Player player) { private void giveBook(Sign sign, Player player) {
String fileName = ChatColor.stripColor(sign.getLine(2)); String fileName = ChatColor.stripColor(sign.getSide(Side.FRONT).getLine(2));
boolean isLoadListNumber = false; boolean isLoadListNumber = false;
try { try {
@ -308,7 +316,7 @@ public class SignEventListener implements Listener {
} }
//Add the third line to the second line for the full filename //Add the third line to the second line for the full filename
String thirdLine = sign.getLine(3); String thirdLine = sign.getSide(Side.FRONT).getLine(3);
if (!isLoadListNumber && thirdLine.length() >= 2) { if (!isLoadListNumber && thirdLine.length() >= 2) {
fileName += ChatColor.stripColor(thirdLine); fileName += ChatColor.stripColor(thirdLine);
} }

View File

@ -26,22 +26,22 @@ public enum BookHoldingState {
UNSIGNED_MAIN_HAND, UNSIGNED_MAIN_HAND,
/** /**
* The player is holding one signed book in their off hand * The player is holding one signed book in their off-hand
*/ */
SIGNED_OFF_HAND, SIGNED_OFF_HAND,
/** /**
* The player is holding one unsigned book in their off hand * The player is holding one unsigned book in their off-hand
*/ */
UNSIGNED_OFF_HAND, UNSIGNED_OFF_HAND,
/** /**
* The player is holding one signed book in their main hand and one unsigned book in their off hand * The player is holding one signed book in their main hand and one unsigned book in their off-hand
*/ */
SIGNED_MAIN_HAND_UNSIGNED_OFF_HAND, SIGNED_MAIN_HAND_UNSIGNED_OFF_HAND,
/** /**
* The player is holding one unsigned book in their main hand and one signed book in their off hand * The player is holding one unsigned book in their main hand and one signed book in their off-hand
*/ */
UNSIGNED_MAIN_HAND_SIGNED_OFF_HAND, UNSIGNED_MAIN_HAND_SIGNED_OFF_HAND,

View File

@ -11,7 +11,7 @@ public enum ItemSlot {
MAIN_HAND, MAIN_HAND,
/** /**
* The item is in the player's off hand * The item is in the player's off-hand
*/ */
OFF_HAND, OFF_HAND,

View File

@ -3,14 +3,10 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import org.bukkit.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -21,9 +17,9 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
/** /**
* Helper class for dealing with files * Helper class for dealing with files
*/ */
public final class FileHelper { public final class BookFileHelper {
private FileHelper() { private BookFileHelper() {
} }
/** /**
@ -102,7 +98,7 @@ public final class FileHelper {
if (file == null) { if (file == null) {
return new ArrayList<>(); return new ArrayList<>();
} }
return FileHelper.listFiles(sender, file); return BookFileHelper.listFiles(sender, file);
} }
/** /**
@ -113,7 +109,7 @@ public final class FileHelper {
*/ */
public static void printBooks(CommandSender sender, boolean listPublic) { public static void printBooks(CommandSender sender, boolean listPublic) {
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic); List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
FileHelper.printFiles(sender, availableBooks); BookFileHelper.printFiles(sender, availableBooks);
} }
/** /**
@ -162,9 +158,8 @@ public final class FileHelper {
String userName = data[1].substring(0, data[1].length() - 4); String userName = data[1].substring(0, data[1].length() - 4);
data[1] = BookHelper.authorFromUUID(userName) + extension; data[1] = BookHelper.authorFromUUID(userName) + extension;
fileList.add(String.join(separator, data)); fileList.add(String.join(separator, data));
} else {
fileList.add(fileName);
} }
fileList.add(fileName);
} }
return fileList; return fileList;
@ -187,15 +182,4 @@ public final class FileHelper {
return foundDuplicates; return foundDuplicates;
} }
/**
* Gets a buffered reader given an input stream
*
* @param inputStream <p>The input stream to read</p>
* @return <p>A buffered reader reading the input stream</p>
*/
public static BufferedReader getBufferedReaderFromInputStream(InputStream inputStream) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
return new BufferedReader(inputStreamReader);
}
} }

View File

@ -1,13 +1,12 @@
package net.knarcraft.bookswithoutborders.utility; package net.knarcraft.bookswithoutborders.utility;
import net.md_5.bungee.api.ChatColor; import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* A class for formatting text to fit books * A class for formatting text to fit books
@ -109,26 +108,10 @@ public final class BookFormatter {
public static BookMeta formatPages(BookMeta bookMeta) { public static BookMeta formatPages(BookMeta bookMeta) {
List<String> formattedPages = new ArrayList<>(Objects.requireNonNull(bookMeta).getPageCount()); List<String> formattedPages = new ArrayList<>(Objects.requireNonNull(bookMeta).getPageCount());
for (String page : bookMeta.getPages()) { for (String page : bookMeta.getPages()) {
formattedPages.add(BookFormatter.translateAllColorCodes(page)); formattedPages.add(ColorHelper.translateColorCodes(page, ColorConversion.RGB));
} }
bookMeta.setPages(formattedPages); bookMeta.setPages(formattedPages);
return bookMeta; return bookMeta;
} }
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
private static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
} }

View File

@ -3,6 +3,7 @@ package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.BooksWithoutBorders; import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.state.BookDirectory; import net.knarcraft.bookswithoutborders.state.BookDirectory;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -60,7 +61,22 @@ public final class BookLoader {
//Get the full path of the book to load //Get the full path of the book to load
File file = getFullPath(sender, fileName, bookDirectory, directory); File file = getFullPath(sender, fileName, bookDirectory, directory);
if (file == null) { if (file == null) {
return 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 //Make sure the player can pay for the book
@ -120,13 +136,11 @@ public final class BookLoader {
String slash = BooksWithoutBordersConfig.getSlash(); String slash = BooksWithoutBordersConfig.getSlash();
String bookFolder = BooksWithoutBordersConfig.getBookFolder(); String bookFolder = BooksWithoutBordersConfig.getBookFolder();
if (bookDirectory == BookDirectory.ENCRYPTED) { if (bookDirectory == BookDirectory.ENCRYPTED) {
file = FileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName); file = BookFileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName);
} else { } else {
file = FileHelper.getBookFile(BookHelper.getBookDirectoryPathString(bookDirectory, sender) + fileName); file = BookFileHelper.getBookFile(BookHelper.getBookDirectoryPathString(bookDirectory, sender) + fileName);
} }
if (file == null || !file.isFile()) { if (file == null || !file.isFile()) {
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect file name!");
return null; return null;
} else { } else {
return file; return file;

View File

@ -1,16 +1,18 @@
package net.knarcraft.bookswithoutborders.utility; package net.knarcraft.bookswithoutborders.utility;
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig; import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.knarlib.util.FileHelper;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.meta.BookMeta; import org.bukkit.inventory.meta.BookMeta;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileInputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -83,7 +85,7 @@ public final class BookToFromTextHelper {
* @throws IOException <p>If unable to save the book</p> * @throws IOException <p>If unable to save the book</p>
*/ */
public static void bookToTXT(String folderPath, String fileName, BookMeta bookMetadata) throws IOException { 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); PrintWriter printWriter = new PrintWriter(fileWriter);
List<String> pages = bookMetadata.getPages(); List<String> pages = bookMetadata.getPages();
@ -186,7 +188,7 @@ public final class BookToFromTextHelper {
*/ */
private static List<String> readTextFile(File file) throws IOException { private static List<String> readTextFile(File file) throws IOException {
List<String> rawPages = new ArrayList<>(); 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 //Use the first line to decide if we are loading a book, or creating a new book
String firstLine = bufferedReader.readLine(); String firstLine = bufferedReader.readLine();

View File

@ -8,10 +8,13 @@ import org.bukkit.Server;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.ServicesManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
@ -74,17 +77,89 @@ public final class EconomyHelper {
if (bookCurrency == Material.AIR) { if (bookCurrency == Material.AIR) {
return !EconomyHelper.payForBookPrintingEconomy(player, cost, numCopies); return !EconomyHelper.payForBookPrintingEconomy(player, cost, numCopies);
} else { } else {
if (player.getInventory().contains(bookCurrency, itemCost)) { if (bookCurrency == Material.WRITABLE_BOOK) {
payForBookPrintingItem(player, itemCost); //Writable books are treated as a special case to prevent WIP books from being used
return false; return !takeWritableBookPayment(player, itemCost);
} else { } else {
BooksWithoutBorders.sendErrorMessage(player, itemCost + " " + bookCurrency + if (player.getInventory().contains(bookCurrency, itemCost)) {
"(s) are required for this command!"); payForBookPrintingItem(player, itemCost);
return true; 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 * Uses economy to take payment for printing a number of books
* *
@ -96,8 +171,10 @@ public final class EconomyHelper {
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) { private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) {
if ((economy.getBalance(player) - cost) >= 0) { if ((economy.getBalance(player) - cost) >= 0) {
economy.withdrawPlayer(player, cost); economy.withdrawPlayer(player, cost);
BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " + numCopies + " book(s)"); BooksWithoutBorders.sendSuccessMessage(player, economy.format(cost) + " withdrawn to create " +
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " + economy.format(economy.getBalance(player))); numCopies + " book(s)");
BooksWithoutBorders.sendSuccessMessage(player, "New balance: " +
economy.format(economy.getBalance(player)));
return true; return true;
} else { } else {
BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!"); BooksWithoutBorders.sendErrorMessage(player, economy.format(cost) + " is required for this command!");

View File

@ -5,7 +5,7 @@ import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
import net.knarcraft.bookswithoutborders.encryption.GenenCrypt; import net.knarcraft.bookswithoutborders.encryption.GenenCrypt;
import net.knarcraft.bookswithoutborders.encryption.SubstitutionCipher; import net.knarcraft.bookswithoutborders.encryption.SubstitutionCipher;
import net.knarcraft.bookswithoutborders.state.EncryptionStyle; import net.knarcraft.bookswithoutborders.state.EncryptionStyle;
import org.bukkit.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@ -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));
}
}

View File

@ -51,6 +51,31 @@ public final class InventoryHelper {
} }
} }
/**
* Performs checks to validate that a player contains exactly one unwritten book
*
* @param player <p>The player to validate</p>
* @param noBookMessage <p>The message to display if the player is not holding a book</p>
* @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</p>
* @return <p>False if the player is holding exactly one book</p>
*/
public static boolean notHoldingOneWritableBookCheck(Player player, String noBookMessage, String twoBooksMessage) {
BookHoldingState holdingState = getBookHoldingState(player);
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.SIGNED_BOTH_HANDS ||
holdingState == BookHoldingState.SIGNED_MAIN_HAND || holdingState == BookHoldingState.SIGNED_OFF_HAND) {
BooksWithoutBorders.sendErrorMessage(player, noBookMessage);
return true;
}
if (holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS) {
BooksWithoutBorders.sendErrorMessage(player, twoBooksMessage);
return true;
}
return false;
}
/** /**
* Performs checks to validate that a player contains exactly one written book * Performs checks to validate that a player contains exactly one written book
* *

View File

@ -6,43 +6,9 @@ import java.util.List;
/** /**
* Helper class for getting string lists required for auto-completion * Helper class for getting string lists required for auto-completion
*/ */
public final class TabCompletionHelper { public final class TabCompletionTypeHelper {
private TabCompletionHelper() { private TabCompletionTypeHelper() {
}
/**
* Finds tab complete values that contain the typed text
*
* @param values <p>The values to filter</p>
* @param typedText <p>The text the player has started typing</p>
* @return <p>The given string values that contain the player's typed text</p>
*/
public static List<String> filterMatchingContains(List<String> values, String typedText) {
List<String> configValues = new ArrayList<>();
for (String value : values) {
if (value.toLowerCase().contains(typedText.toLowerCase())) {
configValues.add(value);
}
}
return configValues;
}
/**
* Finds tab complete values that match the start of the typed text
*
* @param values <p>The values to filter</p>
* @param typedText <p>The text the player has started typing</p>
* @return <p>The given string values that start with the player's typed text</p>
*/
public static List<String> filterMatchingStartsWith(List<String> values, String typedText) {
List<String> configValues = new ArrayList<>();
for (String value : values) {
if (value.toLowerCase().startsWith(typedText.toLowerCase())) {
configValues.add(value);
}
}
return configValues;
} }
/** /**

View File

@ -1,92 +0,0 @@
package net.knarcraft.bookswithoutborders.utility;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
/**
* The update checker is responsible for looking for new updates
*/
public final class UpdateChecker {
private final static String updateNotice = "A new update is available: %s (You are still on %s)";
private UpdateChecker() {
}
/**
* Checks if there's a new update available, and alerts the user if necessary
*/
public static void checkForUpdate(Plugin plugin, String apiResourceURL, Supplier<String> getVersionMethod,
Consumer<String> setVersionMethod) {
BukkitScheduler scheduler = plugin.getServer().getScheduler();
scheduler.runTaskAsynchronously(plugin, () -> UpdateChecker.queryAPI(plugin, apiResourceURL, getVersionMethod,
setVersionMethod));
}
/**
* Queries the spigot API to check for a newer version, and informs the user
*/
private static void queryAPI(Plugin plugin, String APIResourceURL, Supplier<String> getVersionMethod,
Consumer<String> setVersionMethod) {
try {
InputStream inputStream = new URL(APIResourceURL).openStream();
BufferedReader reader = FileHelper.getBufferedReaderFromInputStream(inputStream);
//There should only be one line of output
String newVersion = reader.readLine();
reader.close();
String oldVersion = getVersionMethod.get();
//If there is a newer version, notify the user
if (isVersionHigher(oldVersion, newVersion)) {
plugin.getLogger().log(Level.INFO, getUpdateAvailableString(newVersion, oldVersion));
if (setVersionMethod != null) {
setVersionMethod.accept(newVersion);
}
}
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "Unable to get newest version.");
}
}
/**
* Gets the string to display to a user to alert about a new update
*
* @param newVersion <p>The new available plugin version</p>
* @param oldVersion <p>The old (current) plugin version</p>
* @return <p>The string to display</p>
*/
public static String getUpdateAvailableString(String newVersion, String oldVersion) {
return String.format(updateNotice, newVersion, oldVersion);
}
/**
* Decides whether one version number is higher than another
*
* @param oldVersion <p>The old version to check</p>
* @param newVersion <p>The new version to check</p>
* @return <p>True if the new version is higher than the old one</p>
*/
public static boolean isVersionHigher(String oldVersion, String newVersion) {
String[] oldVersionParts = oldVersion.split("\\.");
String[] newVersionParts = newVersion.split("\\.");
int versionLength = Math.max(oldVersionParts.length, newVersionParts.length);
for (int i = 0; i < versionLength; i++) {
int oldVersionNumber = oldVersionParts.length > i ? Integer.parseInt(oldVersionParts[i]) : 0;
int newVersionNumber = newVersionParts.length > i ? Integer.parseInt(newVersionParts[i]) : 0;
if (newVersionNumber != oldVersionNumber) {
return newVersionNumber > oldVersionNumber;
}
}
return false;
}
}

View File

@ -30,4 +30,5 @@ Options:
# Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the # Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the
# vanilla behavior where a copy of a copy cannot be copied further. # vanilla behavior where a copy of a copy cannot be copied further.
Change_Generation_On_Copy: false Change_Generation_On_Copy: false
# Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents.
Enable_Book_Peeking: false

View File

@ -1,10 +1,10 @@
name: BooksWithoutBorders name: BooksWithoutBorders
version: '${project.version}' version: '${project.version}'
main: net.knarcraft.bookswithoutborders.BooksWithoutBorders main: net.knarcraft.bookswithoutborders.BooksWithoutBorders
api-version: 1.18 api-version: '1.20'
prefix: Books Without Borders prefix: 'Books Without Borders'
authors: [ EpicKnarvik97, AkiraAkiba ] authors: [ EpicKnarvik97, AkiraAkiba ]
description: A continuation of the original Books Without Borders description: 'A continuation of the original Books Without Borders'
softdepend: [ Vault ] softdepend: [ Vault ]
website: https://www.spigotmc.org/resources/books-without-borders-updated.96069/ website: https://www.spigotmc.org/resources/books-without-borders-updated.96069/
dev-url: https://git.knarcraft.net/EpicKnarvik97/Books-Without-Borders dev-url: https://git.knarcraft.net/EpicKnarvik97/Books-Without-Borders
@ -45,6 +45,10 @@ commands:
description: Copies the book the player is holding description: Copies the book the player is holding
usage: /<command> <# of copies> usage: /<command> <# of copies>
permission: bookswithoutborders.copy permission: bookswithoutborders.copy
clearbook:
description: Removes all text from the book the player is holding
usage: /<command>
permission: bookswithoutborders.clear
unsignbook: unsignbook:
description: Un-signs the book the player is holding description: Un-signs the book the player is holding
usage: /<command> usage: /<command>
@ -127,9 +131,11 @@ permissions:
bookswithoutborders.save: true bookswithoutborders.save: true
bookswithoutborders.load: true bookswithoutborders.load: true
bookswithoutborders.delete: true bookswithoutborders.delete: true
bookswithoutborders.peekbookshelf: true
bookswithoutborders.alterbooks: bookswithoutborders.alterbooks:
description: Allows player to change books' data such as lore/title/author/formatting and unsigning books description: Allows player to change books' data such as lore/title/author/generation/formatting and unsigning books
children: children:
bookswithoutborders.clear: true
bookswithoutborders.unsign: true bookswithoutborders.unsign: true
bookswithoutborders.settitle: true bookswithoutborders.settitle: true
bookswithoutborders.setauthor: true bookswithoutborders.setauthor: true
@ -145,9 +151,11 @@ permissions:
bookswithoutborders.delete: bookswithoutborders.delete:
description: Allows player to delete books from their personal directory description: Allows player to delete books from their personal directory
bookswithoutborders.unsign: bookswithoutborders.unsign:
description: Allows player to use unsign command description: Allows player to use the unsign command
bookswithoutborders.copy: bookswithoutborders.copy:
description: Allows player to use copy command description: Allows player to use the copy command
bookswithoutborders.clear:
description: Allows player to use the clear command
bookswithoutborders.loadpublic: bookswithoutborders.loadpublic:
description: Allows player to load from the public directory description: Allows player to load from the public directory
bookswithoutborders.savepublic: bookswithoutborders.savepublic:
@ -184,3 +192,5 @@ permissions:
description: Allows player to reload this plugin description: Allows player to reload this plugin
bookswithoutborders.setgeneration: bookswithoutborders.setgeneration:
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy) description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
bookswithoutborders.peekbookshelf:
description: Allows player to left-click a bookshelf to see the contents of the shelf

View File

@ -1,13 +1,13 @@
package net.knarcraft.bookswithoutborders.util; package net.knarcraft.bookswithoutborders.util;
import net.knarcraft.bookswithoutborders.utility.FileHelper; import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class FileHelperTest { public class BookFileHelperTest {
@Test @Test
public void findDuplicatesTest() { public void findDuplicatesTest() {
@ -17,9 +17,9 @@ public class FileHelperTest {
files[2] = new File("test/asd/(3)book+)Crab.yml"); files[2] = new File("test/asd/(3)book+)Crab.yml");
files[3] = new File("test/asd/(2)book+)Crab.yml"); files[3] = new File("test/asd/(2)book+)Crab.yml");
files[4] = new File("test/asd/(1)book+)Crab.yml"); files[4] = new File("test/asd/(1)book+)Crab.yml");
assertEquals(4, FileHelper.findDuplicates(files, "book+)Crab.yml")); assertEquals(4, BookFileHelper.findDuplicates(files, "book+)Crab.yml"));
assertEquals(1, FileHelper.findDuplicates(files, "book+)Fish.yml")); assertEquals(1, BookFileHelper.findDuplicates(files, "book+)Fish.yml"));
assertEquals(0, FileHelper.findDuplicates(files, "book+)Horse.yml")); assertEquals(0, BookFileHelper.findDuplicates(files, "book+)Horse.yml"));
} }
} }

View File

@ -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));
}
}