Compare commits
96 Commits
Author | SHA1 | Date | |
---|---|---|---|
ebfca05859 | |||
3e3b8e7ad2 | |||
b2ce31234d | |||
e5aaa29c66 | |||
f9674568ba | |||
2203037b00 | |||
6b44ada84a | |||
9f979dd56e | |||
10ffd17c04 | |||
0aff3fad02 | |||
32d31fced6 | |||
2627407e6b | |||
175b66465a | |||
36b57b9191 | |||
0d5ad490ff | |||
9e300afbef | |||
a84a56391a | |||
b15ad18ae3 | |||
67ccdf3b1d | |||
b9bd686ae9 | |||
a5be6bb72c | |||
ed0a750eb4 | |||
6aa422d461 | |||
4be023bd63 | |||
35e98e0f18 | |||
74d59bf71b | |||
a1ed6b9566 | |||
3095586d2b | |||
1da61dc820 | |||
48f69000b0 | |||
4ddfafe6ec | |||
3f3566089e | |||
0b701ddef1 | |||
0fe9ccb590 | |||
1a7236cebd | |||
e482e494f8 | |||
0f76c8f869 | |||
6b4e87d33a | |||
40b0fa0baf | |||
31bb26b755 | |||
0e134fcdab | |||
f5ba4db5ac | |||
29d5247c9b | |||
c389c6fbcb | |||
1158820f97 | |||
f9908db88f | |||
738dbe6e30 | |||
3838750f64 | |||
16954f22e3 | |||
6359af02e6 | |||
6ae15acc72 | |||
2c80e4117e | |||
6d539a14df | |||
51371736e4 | |||
2383dfd2ef | |||
04d4bc8303 | |||
184f78d935 | |||
95b0b42fd1 | |||
73eb903517 | |||
b577b1ff75 | |||
c27756f7dc | |||
90bab4a148 | |||
f005b8f8e5 | |||
0fdfd81579 | |||
6820b71dde | |||
28b7849ff5 | |||
e893684dec | |||
f1a8db4f5e | |||
47fabe5806 | |||
ec6def15f9 | |||
13cbe883ed | |||
d2403d247b | |||
c6c018ee88 | |||
8b47aeb8f2 | |||
396c0a9d41 | |||
c995a4fc0f | |||
40512dd771 | |||
5d340af6f2 | |||
70ad6390db | |||
2a5c5b310d | |||
94375eee4b | |||
1eb9c370bc | |||
1100f181be | |||
7467645bcd | |||
11108011a5 | |||
ce249a93b3 | |||
7c229fb459 | |||
f243bf32e7 | |||
a7c284ade2 | |||
0d4d87373c | |||
542cd03bdc | |||
5e85dfd3e4 | |||
af0b0fd12e | |||
a963733734 | |||
7dd201d0d0 | |||
a95d737414 |
113
.gitignore
vendored
Normal file
113
.gitignore
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# User-specific stuff
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
target/
|
||||||
|
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
# Common working directory
|
||||||
|
run/
|
33
Jenkinsfile
vendored
Normal file
33
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
tools {
|
||||||
|
jdk 'JDK17'
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
echo 'Building...'
|
||||||
|
sh 'mvn clean & mvn validate & mvn compile'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Test') {
|
||||||
|
steps {
|
||||||
|
echo 'Testing...'
|
||||||
|
sh 'mvn test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Verify') {
|
||||||
|
steps {
|
||||||
|
echo 'Verifying...'
|
||||||
|
sh 'mvn verify -Dmaven.test.skip=true'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy') {
|
||||||
|
steps {
|
||||||
|
echo 'Deploying...'
|
||||||
|
sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
|
||||||
|
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
README.md
181
README.md
@@ -1,10 +1,9 @@
|
|||||||
# Books Without Borders
|
# Books Without Borders
|
||||||
|
|
||||||
This is an attempt at a rewrite of the Books Without Borders plugin. This rewrite uses the source code given
|
This is a rewrite of the Books Without Borders plugin. This rewrite originally used the source code given
|
||||||
at [the original bukkit page](https://dev.bukkit.org/projects/books-without-borders). I'm not planning any new features
|
at [the original bukkit page](https://dev.bukkit.org/projects/books-without-borders). While the old plugin still worked
|
||||||
at this time. The only goal is to make it 1.17.1 compliant, but I'll make the code more maintainable along the way.
|
the last time I checked, this plugin does not use any depreciated function calls, making sure it works for the
|
||||||
While the original version still works, it's using a lot of depreciated function calls which will most likely break in
|
foreseeable future.
|
||||||
the future.
|
|
||||||
|
|
||||||
## Books without Borders!
|
## Books without Borders!
|
||||||
|
|
||||||
@@ -13,90 +12,94 @@ Books without Borders has got your back!
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Export written books and book and quills to .txt or .yml files
|
- Export written books and book and quills to .txt or .yml files
|
||||||
* Import books from files as written books or unsigned books
|
- Import books from files as written books or unsigned books
|
||||||
* Text files can be any length, and the import process fits the content to the correct page length
|
- Text files can be any length, and the import process fits the content to the correct page length
|
||||||
* Books can be saved privately, or to a directory visible server wide
|
- Books can be saved privately, or to a directory visible server wide
|
||||||
* Encrypt books to prevent other players from reading them
|
- Encrypt books to prevent other players from reading them
|
||||||
* Give, encrypt, or decrypt held books with signs
|
- Give, encrypt, or decrypt held books with signs
|
||||||
* Give players books via command blocks
|
- Give players books via command blocks
|
||||||
* Unsign or copy held books with a simple command
|
- Unsign or copy held books with a simple command
|
||||||
* Give first time players a single book or a set of books when they join
|
- Give first time players a single book or a set of books when they join
|
||||||
* Configurable option to require certain items or pay via Vault compatible economy to create books via command
|
- Configurable option to require certain items or pay via Vault compatible economy to create books via command
|
||||||
* Add lore to any item with a simple command
|
- Add lore to any item with a simple command
|
||||||
* Supports adding and saving color to title, lore, and book contents
|
- Supports adding and saving color to title, lore, and book contents
|
||||||
* Color and formatting codes can be manually turned into formatting using /formatbook
|
- Color and formatting codes can be manually turned into formatting using /formatbook
|
||||||
* Formatting and color codes can be turned into formatting once any book is signed. This is enabled through a config
|
- Clear a book, and start anew with /clearbook
|
||||||
|
- Formatting and color codes can be turned into formatting once any book is signed. This is enabled through a config
|
||||||
value
|
value
|
||||||
|
- Change generation of books. Create tattered books for your RPG server!
|
||||||
|
- Optionally, make it impossible to duplicate the original version of a book
|
||||||
|
- Optionally, hit a bookshelf while sneaking to display the contained books
|
||||||
|
|
||||||
#### Group encryption
|
#### Group encryption
|
||||||
|
|
||||||
* Group encryption allows every player with the bookswithoutborders.decrypt.\<group> permission to decrypt the encrypted
|
- Group encryption allows every player with the bookswithoutborders.decrypt.\<group> permission to decrypt the encrypted
|
||||||
book without using a password.
|
book without using a password.
|
||||||
|
|
||||||
### Commands:
|
### Commands:
|
||||||
|
|
||||||
An in-game description of available commands is available through the /bwb command.
|
An in-game description of available commands is available through the /bwb command.
|
||||||
|
|
||||||
* /bookswithoutborders - Displays information about commands (and permissions if the user has bookswithoutborders.admin)
|
| Command | Arguments | Description |
|
||||||
* /copybook <# of copies> - Copies the book the player is holding
|
|----------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
* /decryptbook <key> - Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used
|
| /bookswithoutborders | None | Displays information about commands (and permissions if the user has bookswithoutborders.admin) |
|
||||||
to encrypt the held book
|
| /copybook | \<# of copies> | Copies the book the player is holding |
|
||||||
* /deletebook <file name or number> - Deletes the specified file in the player's directory
|
| /decryptbook | \<key> | Decrypts the book the player is holding. "key" is required and MUST be IDENTICAL to the key used to encrypt the held book |
|
||||||
* /deletepublicbook <file name or number> - Same as deletebook, but deletes files in the public directory
|
| /deletebook | \<file name or number> | Deletes the specified file in the player's directory |
|
||||||
* encryptbook <key> \[encryption style] - Encrypts the book the player is holding. "key" is required and can be any
|
| /deletepublicbook | \<file name or number> | Same as deletebook, but deletes files in the public directory |
|
||||||
phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
| /encryptbook | \<key> \[encryption style] | Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or "" |
|
||||||
* /formatbook - Formats the held written book (converts color and formatting codes to the corresponding formatted text)
|
| /formatbook | None | Formats the held written book (converts color and formatting codes to the corresponding formatted text) |
|
||||||
* /givebook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Gives the selected player a
|
| /givebook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Gives the selected player a book from your personal directory |
|
||||||
book from your personal directory
|
| /givepublicbook | \<file name or number> \<playername> \[# of copies (num)] \[signed (true/false)] | Same as givebook, but uses books from the public directory |
|
||||||
* /givepublicbook <file name or number> <playername> \[# of copies (num)] \[signed (true/false)] - Same as givebook, but
|
| /loadbook | \<file name or number> \[# of copies] \[signed (true/false)] | Creates a book from the specified file and gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book will be signed, if false it will be unsigned |
|
||||||
uses books from the public directory
|
| /loadpublicbook | \<file name or number> \[# of copies] \[signed (true/false)] | Same as loadbook, but views files in the public directory |
|
||||||
* /loadbook <file name or number> \[# of copies] \[signed (true/false)] - Creates a book from the specified file and
|
| /reload | None | Reloads BwB's configuration file |
|
||||||
gives it to the player. If no file is specified, a list of available files is returned. If true is specified, the book
|
| /savebook | \[overwrite (true/false)] | Saves the book the player is holding to a text file in a private directory. If true is specified, a book of the same name by the same author will be overwritten by the new book |
|
||||||
will be signed, if false it will be unsigned
|
| /savepublicbook | \[overwrite (true/false)] | Same as savebook, but saves files in the public directory |
|
||||||
* /loadpublicbook <file name or number> \[# of copies] \[signed (true/false)] - Same as loadbook, but views files in the
|
| /setbookauthor | \<author> | Sets the author of the book the player is holding |
|
||||||
public directory
|
| /setbookgeneration | \<generation> | Sets the generation of the held book (ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED) |
|
||||||
* /reload - Reloads BwB's configuration file
|
| /setbookprice | \<item/eco> \<quantity> | Sets the per-book price to create a book via commands. If "Item", the item in the player's hand in the amount of \<quantity> will be the price. If "Eco", a Vault based economy will be used for price. If neither \<Item/Eco> nor \<quantity> are specified, the current price to create books will be removed. |
|
||||||
* /savebook \[overwrite (true/false)] - Saves the book the player is holding to a text file in a private directory. If
|
| /setlore | \<new lore> | Sets the lore of the item the player is holding. Insert the lore_line_separator character to force a new line ("~" by default) |
|
||||||
true is specified, a book of the same name by the same author will be overwritten by the new book
|
| /settitle | \<title> | Sets the title of the book/item the player is holding |
|
||||||
* /savepublicbook \[overwrite (true/false)] - Same as savebook, but saves files in the public directory
|
| /unsignbook | None | Un-signs the book the player is holding |
|
||||||
* /setbookauthor <author> - Sets the author of the book the player is holding
|
| /clearbook | None | Removes all text from the held un-signed book |
|
||||||
* /setbookprice <item/eco> <quantity> - Sets the per-book price to create a book via commands. If "Item", the item in
|
|
||||||
the player's hand in the amount of <quantity> will be the price. If "Eco", a Vault based economy will be used for
|
|
||||||
price. If neither <Item/Eco> nor <quantity> are specified, the current price to create books will be removed.
|
|
||||||
* /setlore <new lore> - Sets the lore of the item the player is holding. Insert the lore_line_separator character to
|
|
||||||
force a new line ("~" by default)
|
|
||||||
* /settitle <title> - Sets the title of the book/item the player is holding
|
|
||||||
* /unsignbook - Un-signs the book the player is holding
|
|
||||||
|
|
||||||
### Permissions:
|
### Permissions:
|
||||||
|
|
||||||
* bookswithoutborders.* - Grants all permissions
|
| Node | Description |
|
||||||
* bookswithoutborders.admin - Grants all permissions
|
|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
* bookswithoutborders.use - Allows player to use commands to save/load/delete in their personal directory
|
| bookswithoutborders.* | Grants all permissions |
|
||||||
* bookswithoutborders.alterbooks - Allows player to change books' data such as lore/title/author/formatting and
|
| bookswithoutborders.admin | Grants all permissions |
|
||||||
unsigning books
|
| bookswithoutborders.use | bookswithoutborders.use - Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled |
|
||||||
* bookswithoutborders.format - Allows a player to format a book
|
| bookswithoutborders.alterbooks | Allows player to change books' data such as lore/title/author/generation/formatting and un-signing books |
|
||||||
* bookswithoutborders.save - Allows a player to save books to their personal directory
|
| bookswithoutborders.reload | Allows player to reload this plugin |
|
||||||
* bookswithoutborders.load - Allows player to load books from their personal directory
|
| bookswithoutborders.format | Allows a player to format a book |
|
||||||
* bookswithoutborders.delete - Allows player to delete books from their personal directory
|
| bookswithoutborders.save | Allows a player to save books to their personal directory |
|
||||||
* bookswithoutborders.unsign - Allows player to un-sign books
|
| bookswithoutborders.load | Allows player to load books from their personal directory |
|
||||||
* bookswithoutborders.copy - Allows player to copy books
|
| bookswithoutborders.delete | Allows player to delete books from their personal directory |
|
||||||
* bookswithoutborders.loadpublic - Allows player to load from the public directory
|
| bookswithoutborders.unsign | Allows player to un-sign books |
|
||||||
* bookswithoutborders.savepublic - Allows player to save to the public directory
|
| bookswithoutborders.copy | Allows player to copy books |
|
||||||
* bookswithoutborders.encrypt - Allows player to encrypt books
|
| bookswithoutborders.loadpublic | Allows player to load from the public directory |
|
||||||
* bookswithoutborders.groupencrypt - Allows player to use group-based encryption
|
| bookswithoutborders.savepublic | Allows player to save to the public directory |
|
||||||
* bookswithoutborders.decrypt - Allows player to decrypt books
|
| bookswithoutborders.encrypt | Allows player to encrypt books |
|
||||||
* bookswithoutborders.decrypt.agroup - Allows player to decrypt books group-encrypted for group "agroup"
|
| bookswithoutborders.groupencrypt | Allows player to use group-based encryption |
|
||||||
* bookswithoutborders.signs - Allows player to create signs that give/encrypt/decrypt books
|
| bookswithoutborders.decrypt | Allows player to decrypt books |
|
||||||
* bookswithoutborders.give - Allows player to give another player one of their privately saved books
|
| bookswithoutborders.decrypt.agroup | Allows player to decrypt books group-encrypted for group "agroup" |
|
||||||
* bookswithoutborders.givepublic - Allows a player to give another player a book from the public directory
|
| bookswithoutborders.signs | Allows player to create signs that give/encrypt/decrypt books |
|
||||||
* bookswithoutborders.settitle - Allows player to set the title of the currently held book
|
| bookswithoutborders.give | Allows player to give another player one of their privately saved books |
|
||||||
* bookswithoutborders.setauthor - Allows player to set the author of the currently held book
|
| bookswithoutborders.givepublic | Allows a player to give another player a book from the public directory |
|
||||||
* bookswithoutborders.setlore - Allows player to set the lore of the currently held item
|
| bookswithoutborders.settitle | Allows player to set the title of the currently held book |
|
||||||
* bookswithoutborders.bypassauthoronlycopy - Allows player to ignore Author_Only_Copy config setting
|
| bookswithoutborders.setauthor | Allows player to set the author of the currently held book |
|
||||||
* bookswithoutborders.bypassbookprice - Allows player to ignore Price_to_create_book config setting
|
| bookswithoutborders.setlore | Allows player to set the lore of the currently held item |
|
||||||
* bookswithoutborders-setbookprice - Allows player to set the cost of creating a book
|
| bookswithoutborders.bypassauthoronlycopy | Allows player to ignore Author_Only_Copy config setting |
|
||||||
|
| bookswithoutborders.bypassauthoronlyunsign | Allows player to ignore Author_Only_Unsign config setting |
|
||||||
|
| bookswithoutborders.bypassauthoronlysave | Allows player to ignore Author_Only_Save config setting |
|
||||||
|
| bookswithoutborders.bypassbookprice | Allows player to ignore Price_to_create_book config setting |
|
||||||
|
| bookswithoutborders.setbookprice | Allows player to set the cost of creating a book |
|
||||||
|
| bookswithoutborders.setgeneration | Allows player to change the generation of a book (Original, Copy, Copy of Copy) |
|
||||||
|
| bookswithoutborders.clear | Allows player to clear the contents of the held writable book |
|
||||||
|
| bookswithoutborders.peekbookshelf | Allows player to left-click a bookshelf to see the contents of the shelf |
|
||||||
|
|
||||||
### Signs
|
### Signs
|
||||||
|
|
||||||
@@ -105,15 +108,35 @@ 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
|
||||||
|
|
||||||
The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third line must contain the decryption key
|
The **_decrypt_** sign must have **\[Decrypt]** on its second line. The third line must contain the decryption key
|
||||||
|
|
||||||
|
### Configuration options:
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Save_Books_in_Yaml_Format | Whether to use YAML for saved books instead of just storing them as text |
|
||||||
|
| Max_Number_of_Duplicates | The maximum number of duplicates of a saved book allowed |
|
||||||
|
| Author_Separator | The separator used to separate the book title and the book author |
|
||||||
|
| Lore_line_separator | The separator used to denote a new line in the book/item lore |
|
||||||
|
| Books_for_new_players | A list of books given to new players the first time they join the server |
|
||||||
|
| Message_for_new_players | An optional message displayed to new players the first time they join the server |
|
||||||
|
| Price_to_create_book.Item_type | The item type used as currency for copying books. Use "Economy" to use money instead of items |
|
||||||
|
| Price_to_create_book.Required_quantity | The quantity of currency required to pay for each book produced |
|
||||||
|
| Admin_Auto_Decrypt | Whether any admin can decrypt any book regardless of the group it was encrypted for |
|
||||||
|
| Author_Only_Copy | Whether to only allow the author of a book to create copies |
|
||||||
|
| Author_Only_Unsign | Whether to only allow the author of a book to unsign it |
|
||||||
|
| Author_Only_Save | Whether to only allow saving a player's own books with /savebook |
|
||||||
|
| Format_Book_After_Signing | Whether to automatically format every book when it's signed |
|
||||||
|
| Change_Generation_On_Copy | Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the vanilla behavior where a copy of a copy or tattered book cannot be copied further. |
|
||||||
|
| Enable_Book_Peeking | Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents. |
|
66
pom.xml
66
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>net.knarcraft</groupId>
|
<groupId>net.knarcraft</groupId>
|
||||||
<artifactId>BooksWithoutBorders</artifactId>
|
<artifactId>BooksWithoutBorders</artifactId>
|
||||||
<version>1.2.2</version>
|
<version>1.3.9</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,14 +31,14 @@
|
|||||||
<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>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.2.4</version>
|
<version>3.3.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
@@ -47,6 +47,30 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
<relocations>
|
||||||
|
<relocation>
|
||||||
|
<pattern>net.knarcraft.knarlib</pattern>
|
||||||
|
<shadedPattern>net.knarcraft.bookswithoutborders.lib.knarlib</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
<relocation>
|
||||||
|
<pattern>org.jetbrains.annotations</pattern>
|
||||||
|
<shadedPattern>net.knarcraft.bookswithoutborders.lib.annotations</shadedPattern>
|
||||||
|
</relocation>
|
||||||
|
</relocations>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>net.knarcraft:knarlib</artifact>
|
||||||
|
<includes>
|
||||||
|
<include>net/knarcraft/knarlib/**</include>
|
||||||
|
</includes>
|
||||||
|
</filter>
|
||||||
|
<filter>
|
||||||
|
<artifact>org.jetbrains:annotations</artifact>
|
||||||
|
<includes>
|
||||||
|
<include>org/jetbrains/annotations/**</include>
|
||||||
|
</includes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
@@ -61,6 +85,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,25 +98,47 @@
|
|||||||
<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.18.1-R0.1-SNAPSHOT</version>
|
<version>1.21.8-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.MilkBowl</groupId>
|
<groupId>com.github.MilkBowl</groupId>
|
||||||
<artifactId>VaultAPI</artifactId>
|
<artifactId>VaultAPI</artifactId>
|
||||||
<version>1.7</version>
|
<version>1.7.1</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.knarcraft</groupId>
|
||||||
|
<artifactId>knarlib</artifactId>
|
||||||
|
<version>1.2.7</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>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.13.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders;
|
package net.knarcraft.bookswithoutborders;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.command.CommandBooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.command.CommandClear;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandCopy;
|
import net.knarcraft.bookswithoutborders.command.CommandCopy;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
|
import net.knarcraft.bookswithoutborders.command.CommandDecrypt;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandDelete;
|
import net.knarcraft.bookswithoutborders.command.CommandDelete;
|
||||||
@@ -17,30 +18,40 @@ import net.knarcraft.bookswithoutborders.command.CommandSave;
|
|||||||
import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
|
import net.knarcraft.bookswithoutborders.command.CommandSavePublic;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
|
import net.knarcraft.bookswithoutborders.command.CommandSetAuthor;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
|
import net.knarcraft.bookswithoutborders.command.CommandSetBookPrice;
|
||||||
|
import net.knarcraft.bookswithoutborders.command.CommandSetBookshelfData;
|
||||||
|
import net.knarcraft.bookswithoutborders.command.CommandSetGeneration;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetLore;
|
import net.knarcraft.bookswithoutborders.command.CommandSetLore;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
import net.knarcraft.bookswithoutborders.command.CommandSetTitle;
|
||||||
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
import net.knarcraft.bookswithoutborders.command.CommandUnSign;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
|
||||||
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;
|
||||||
import org.bukkit.command.PluginCommand;
|
import org.bukkit.command.PluginCommand;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemFactory;
|
import org.bukkit.inventory.ItemFactory;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||||
@@ -53,16 +64,20 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
|
|||||||
public class BooksWithoutBorders extends JavaPlugin {
|
public class BooksWithoutBorders extends JavaPlugin {
|
||||||
|
|
||||||
private static ItemFactory itemFactory;
|
private static ItemFactory itemFactory;
|
||||||
private static Map<String, List<String>> playerBooksList;
|
private static @NotNull Map<UUID, List<String>> playerBooksList = new HashMap<>();
|
||||||
private static List<String> publicBooksList;
|
private static @NotNull List<String> publicBooksList = new ArrayList<>();
|
||||||
|
private static Map<Character, Integer> publicLetterIndex;
|
||||||
|
private static Map<UUID, Map<Character, Integer>> playerLetterIndex;
|
||||||
private static BooksWithoutBorders booksWithoutBorders;
|
private static BooksWithoutBorders booksWithoutBorders;
|
||||||
private static ConsoleCommandSender consoleSender;
|
private static ConsoleCommandSender consoleSender;
|
||||||
|
private static BookshelfHandler bookshelfHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the console sender for printing to the console
|
* Gets the console sender for printing to the console
|
||||||
*
|
*
|
||||||
* @return <p>The console's console sender</p>
|
* @return <p>The console's console sender</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static ConsoleCommandSender getConsoleSender() {
|
public static ConsoleCommandSender getConsoleSender() {
|
||||||
return consoleSender;
|
return consoleSender;
|
||||||
}
|
}
|
||||||
@@ -74,16 +89,38 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
* @param getPublic <p>Whether to get available public books</p>
|
* @param getPublic <p>Whether to get available public books</p>
|
||||||
* @return <p>A list of available books</p>
|
* @return <p>A list of available books</p>
|
||||||
*/
|
*/
|
||||||
public static List<String> getAvailableBooks(CommandSender sender, boolean getPublic) {
|
@NotNull
|
||||||
|
public static List<String> getAvailableBooks(@NotNull CommandSender sender, boolean getPublic) {
|
||||||
if (getPublic) {
|
if (getPublic) {
|
||||||
return new ArrayList<>(publicBooksList);
|
return new ArrayList<>(publicBooksList);
|
||||||
} else {
|
} else if (sender instanceof Player player) {
|
||||||
String senderName = sender.getName();
|
UUID playerUUID = player.getUniqueId();
|
||||||
if (!playerBooksList.containsKey(senderName)) {
|
if (!playerBooksList.containsKey(playerUUID)) {
|
||||||
List<String> newFiles = FileHelper.listFiles(sender, false);
|
List<String> newFiles = BookFileHelper.listFiles(sender, false);
|
||||||
playerBooksList.put(senderName, newFiles);
|
if (newFiles != null) {
|
||||||
|
playerBooksList.put(playerUUID, newFiles);
|
||||||
|
playerLetterIndex.put(playerUUID, BookFileHelper.populateLetterIndices(newFiles));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return playerBooksList.get(senderName);
|
return new ArrayList<>(playerBooksList.get(playerUUID));
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the letter index map for public books, or a specific player's books
|
||||||
|
*
|
||||||
|
* @param playerIndex <p>The player to get the index for, or null for the public index</p>
|
||||||
|
* @return <p>An index mapping between a character and the first index containing that character</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Map<Character, Integer> getLetterIndex(@Nullable UUID playerIndex) {
|
||||||
|
if (playerIndex == null) {
|
||||||
|
return publicLetterIndex;
|
||||||
|
} else {
|
||||||
|
Map<Character, Integer> letterIndex = playerLetterIndex.get(playerIndex);
|
||||||
|
return Objects.requireNonNullElseGet(letterIndex, HashMap::new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,15 +130,29 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
* @param sender <p>The sender to update books for</p>
|
* @param sender <p>The sender to update books for</p>
|
||||||
* @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(@NotNull CommandSender sender, boolean updatePublic) {
|
||||||
List<String> newFiles = FileHelper.listFiles(sender, updatePublic);
|
List<String> newFiles = BookFileHelper.listFiles(sender, updatePublic);
|
||||||
|
if (newFiles == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (updatePublic) {
|
if (updatePublic) {
|
||||||
publicBooksList = newFiles;
|
publicBooksList = newFiles;
|
||||||
} else {
|
publicLetterIndex = BookFileHelper.populateLetterIndices(newFiles);
|
||||||
playerBooksList.put(sender.getName(), newFiles);
|
} else if (sender instanceof Player player) {
|
||||||
|
playerBooksList.put(player.getUniqueId(), newFiles);
|
||||||
|
playerLetterIndex.put(player.getUniqueId(), BookFileHelper.populateLetterIndices(newFiles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears book data such as per-player lists and per-player character indexes
|
||||||
|
*/
|
||||||
|
public static void clearBookData() {
|
||||||
|
playerBooksList = new HashMap<>();
|
||||||
|
playerLetterIndex = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
FileConfiguration config = this.getConfig();
|
FileConfiguration config = this.getConfig();
|
||||||
@@ -115,8 +166,15 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
booksWithoutBorders = this;
|
booksWithoutBorders = this;
|
||||||
consoleSender = this.getServer().getConsoleSender();
|
consoleSender = this.getServer().getConsoleSender();
|
||||||
playerBooksList = new HashMap<>();
|
playerBooksList = new HashMap<>();
|
||||||
|
playerLetterIndex = new HashMap<>();
|
||||||
BooksWithoutBordersConfig.initialize(this);
|
BooksWithoutBordersConfig.initialize(this);
|
||||||
publicBooksList = FileHelper.listFiles(consoleSender, true);
|
@Nullable List<String> files = BookFileHelper.listFiles(consoleSender, true);
|
||||||
|
if (files != null) {
|
||||||
|
publicBooksList = files;
|
||||||
|
publicLetterIndex = BookFileHelper.populateLetterIndices(files);
|
||||||
|
}
|
||||||
|
bookshelfHandler = new BookshelfHandler();
|
||||||
|
bookshelfHandler.load();
|
||||||
|
|
||||||
PluginManager pluginManager = this.getServer().getPluginManager();
|
PluginManager pluginManager = this.getServer().getPluginManager();
|
||||||
|
|
||||||
@@ -124,6 +182,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);
|
||||||
}
|
}
|
||||||
@@ -139,6 +198,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
*
|
*
|
||||||
* @return <p>An instance of this plugin</p>
|
* @return <p>An instance of this plugin</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static BooksWithoutBorders getInstance() {
|
public static BooksWithoutBorders getInstance() {
|
||||||
return booksWithoutBorders;
|
return booksWithoutBorders;
|
||||||
}
|
}
|
||||||
@@ -167,6 +227,9 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders());
|
registerCommand("booksWithoutBorders", new CommandBooksWithoutBorders());
|
||||||
registerCommand("reload", new CommandReload());
|
registerCommand("reload", new CommandReload());
|
||||||
registerCommand("formatBook", new CommandFormat());
|
registerCommand("formatBook", new CommandFormat());
|
||||||
|
registerCommand("setBookGeneration", new CommandSetGeneration());
|
||||||
|
registerCommand("clearBook", new CommandClear());
|
||||||
|
registerCommand("setBookshelfData", new CommandSetBookshelfData());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +238,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
* @param commandName <p>The name of the command to register</p>
|
* @param commandName <p>The name of the command to register</p>
|
||||||
* @param executor <p>The executor to register for the command</p>
|
* @param executor <p>The executor to register for the command</p>
|
||||||
*/
|
*/
|
||||||
private void registerCommand(String commandName, CommandExecutor executor) {
|
private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor executor) {
|
||||||
PluginCommand pluginCommand = this.getCommand(commandName);
|
PluginCommand pluginCommand = this.getCommand(commandName);
|
||||||
if (pluginCommand != null) {
|
if (pluginCommand != null) {
|
||||||
pluginCommand.setExecutor(executor);
|
pluginCommand.setExecutor(executor);
|
||||||
@@ -211,11 +274,21 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
return testFileSaving();
|
return testFileSaving();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the bookshelf handler
|
||||||
|
*
|
||||||
|
* @return <p>The bookshelf handler</p>
|
||||||
|
*/
|
||||||
|
public static BookshelfHandler getBookshelfHandler() {
|
||||||
|
return bookshelfHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the server's item factory
|
* Gets the server's item factory
|
||||||
*
|
*
|
||||||
* @return <p>The server's item factory</p>
|
* @return <p>The server's item factory</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static ItemFactory getItemFactory() {
|
public static ItemFactory getItemFactory() {
|
||||||
return itemFactory;
|
return itemFactory;
|
||||||
}
|
}
|
||||||
@@ -234,8 +307,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create necessary folders");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,8 +318,8 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
sendErrorMessage(consoleSender, "Saving failed! Aborting...");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create necessary folders");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +332,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
* @param sender <p>The sender to send the message to</p>
|
* @param sender <p>The sender to send the message to</p>
|
||||||
* @param message <p>The message to send</p>
|
* @param message <p>The message to send</p>
|
||||||
*/
|
*/
|
||||||
public static void sendSuccessMessage(CommandSender sender, String message) {
|
public static void sendSuccessMessage(@NotNull CommandSender sender, @NotNull String message) {
|
||||||
sender.sendMessage(getSuccessColor() + message);
|
sender.sendMessage(getSuccessColor() + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +342,7 @@ public class BooksWithoutBorders extends JavaPlugin {
|
|||||||
* @param sender <p>The sender to send the message to</p>
|
* @param sender <p>The sender to send the message to</p>
|
||||||
* @param message <p>The message to send</p>
|
* @param message <p>The message to send</p>
|
||||||
*/
|
*/
|
||||||
public static void sendErrorMessage(CommandSender sender, String message) {
|
public static void sendErrorMessage(@NotNull CommandSender sender, @NotNull String message) {
|
||||||
sender.sendMessage(getErrorColor() + message);
|
sender.sendMessage(getErrorColor() + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig
|
|||||||
public class CommandBooksWithoutBorders implements TabExecutor {
|
public class CommandBooksWithoutBorders implements TabExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(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) {
|
||||||
sender.sendMessage(getCommandColor() + "[] denote optional parameters");
|
sender.sendMessage(getCommandColor() + "[] denote optional parameters");
|
||||||
sender.sendMessage(getCommandColor() + "<> denote required parameters");
|
sender.sendMessage(getCommandColor() + "<> denote required parameters");
|
||||||
sender.sendMessage(getCommandColor() + "{} denote required permission");
|
sender.sendMessage(getCommandColor() + "{} denote required permission");
|
||||||
@@ -42,7 +42,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
*
|
*
|
||||||
* @param sender <p>The console which sent the command</p>
|
* @param sender <p>The console which sent the command</p>
|
||||||
*/
|
*/
|
||||||
private void showConsoleCommands(CommandSender sender) {
|
private void showConsoleCommands(@NotNull CommandSender sender) {
|
||||||
sender.sendMessage(getCommandColor() + "Commands:");
|
sender.sendMessage(getCommandColor() + "Commands:");
|
||||||
showCommandInfo("deletePublicBook", sender);
|
showCommandInfo("deletePublicBook", sender);
|
||||||
showCommandInfo("givePublicBook", sender);
|
showCommandInfo("givePublicBook", sender);
|
||||||
@@ -54,7 +54,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
*
|
*
|
||||||
* @param sender <p>The player which sent the command</p>
|
* @param sender <p>The player which sent the command</p>
|
||||||
*/
|
*/
|
||||||
private void showPlayerCommands(CommandSender sender) {
|
private void showPlayerCommands(@NotNull CommandSender sender) {
|
||||||
//Lists all commands
|
//Lists all commands
|
||||||
Material bookPriceType = BooksWithoutBordersConfig.getBookPriceType();
|
Material bookPriceType = BooksWithoutBordersConfig.getBookPriceType();
|
||||||
double bookPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
|
double bookPriceQuantity = BooksWithoutBordersConfig.getBookPriceQuantity();
|
||||||
@@ -69,6 +69,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
}
|
}
|
||||||
sender.sendMessage(getCommandColor() + "Commands:");
|
sender.sendMessage(getCommandColor() + "Commands:");
|
||||||
|
|
||||||
|
showCommandInfo("booksWithoutBorders", sender);
|
||||||
showCommandInfo("copyBook", sender);
|
showCommandInfo("copyBook", sender);
|
||||||
showCommandInfo("decryptBook", sender);
|
showCommandInfo("decryptBook", sender);
|
||||||
showCommandInfo("deleteBook", sender);
|
showCommandInfo("deleteBook", sender);
|
||||||
@@ -84,10 +85,13 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
showCommandInfo("saveBook", sender);
|
showCommandInfo("saveBook", sender);
|
||||||
showCommandInfo("savePublicBook", sender);
|
showCommandInfo("savePublicBook", sender);
|
||||||
showCommandInfo("setAuthor", sender);
|
showCommandInfo("setAuthor", sender);
|
||||||
|
showCommandInfo("setBookGeneration", sender);
|
||||||
showCommandInfo("setBookPrice", sender);
|
showCommandInfo("setBookPrice", sender);
|
||||||
showCommandInfo("setLore", sender);
|
showCommandInfo("setLore", sender);
|
||||||
showCommandInfo("setTitle", sender);
|
showCommandInfo("setTitle", sender);
|
||||||
showCommandInfo("unsignBook", sender);
|
showCommandInfo("unsignBook", sender);
|
||||||
|
showCommandInfo("clearBook", sender);
|
||||||
|
showCommandInfo("setBookshelfData", sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +100,7 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
* @param command <p>The command to get information about</p>
|
* @param command <p>The command to get information about</p>
|
||||||
* @param sender <p>The sender asking to see command info</p>
|
* @param sender <p>The sender asking to see command info</p>
|
||||||
*/
|
*/
|
||||||
private void showCommandInfo(String command, CommandSender sender) {
|
private void showCommandInfo(@NotNull String command, @NotNull CommandSender sender) {
|
||||||
PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(command);
|
PluginCommand pluginCommand = BooksWithoutBorders.getInstance().getCommand(command);
|
||||||
if (pluginCommand != null) {
|
if (pluginCommand != null) {
|
||||||
String permission = pluginCommand.getPermission();
|
String permission = pluginCommand.getPermission();
|
||||||
@@ -104,6 +108,9 @@ public class CommandBooksWithoutBorders implements TabExecutor {
|
|||||||
String commandInfo = "\n" + getCommandColor() + pluginCommand.getUsage().replace("<command>",
|
String commandInfo = "\n" + getCommandColor() + pluginCommand.getUsage().replace("<command>",
|
||||||
pluginCommand.getName()) + ": " + getSuccessColor() + pluginCommand.getDescription();
|
pluginCommand.getName()) + ": " + getSuccessColor() + pluginCommand.getDescription();
|
||||||
if (sender.hasPermission("bookswithoutborders.admin")) {
|
if (sender.hasPermission("bookswithoutborders.admin")) {
|
||||||
|
if (permission == null) {
|
||||||
|
permission = "None";
|
||||||
|
}
|
||||||
commandInfo += getCommandColor() + " {" + permission + "}";
|
commandInfo += getCommandColor() + " {" + permission + "}";
|
||||||
}
|
}
|
||||||
sender.sendMessage(commandInfo);
|
sender.sendMessage(commandInfo);
|
||||||
@@ -112,7 +119,8 @@ public class CommandBooksWithoutBorders 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[] arguments) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command executor for the clear command
|
||||||
|
*/
|
||||||
|
public class CommandClear implements TabExecutor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] args) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InventoryHelper.notHoldingOneWritableBookCheck(player, "You must be holding a writable book to " +
|
||||||
|
"clear it!", "You cannot clear two books at once!")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clear the player's held book
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, false);
|
||||||
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
if (bookMeta == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Unable to get metadata for the held book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bookMeta.setPages("");
|
||||||
|
bookMeta.setAuthor(null);
|
||||||
|
bookMeta.setGeneration(null);
|
||||||
|
bookMeta.setTitle(null);
|
||||||
|
heldBook.setItemMeta(bookMeta);
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(sender, "Book cleared!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -2,10 +2,12 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
import net.knarcraft.bookswithoutborders.utility.EconomyHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
import net.knarcraft.bookswithoutborders.utility.TabCompletionTypeHelper;
|
||||||
|
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.TabExecutor;
|
import org.bukkit.command.TabExecutor;
|
||||||
@@ -24,7 +26,8 @@ import java.util.Objects;
|
|||||||
public class CommandCopy implements TabExecutor {
|
public class CommandCopy 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[] arguments) {
|
||||||
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;
|
||||||
@@ -35,62 +38,113 @@ public class CommandCopy implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (arguments.length < 1) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "You must specify the number of copies to be made!");
|
BooksWithoutBorders.sendErrorMessage(player, "You must specify the number of copies to be made!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int copies = Integer.parseInt(args[0]);
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
if (copies > 0) {
|
int copies = Integer.parseInt(arguments[0]);
|
||||||
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
if (copies <= 0) {
|
||||||
if (!isAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
throw new NumberFormatException("Number of copies must be larger than 0");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
|
||||||
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
|
||||||
EconomyHelper.cannotPayForBookPrinting(player, copies)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
heldBook.setAmount(heldBook.getAmount() + copies);
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return performCopy(copies, player, heldBook);
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Book not copied!");
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Number specified was invalid!");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given player is the author of a given book
|
* Performs the actual copying
|
||||||
*
|
*
|
||||||
* @param player <p>The player to check</p>
|
* @param copies <p>The number of copies to be made</p>
|
||||||
* @param book <p>The book to check</p>
|
* @param player <p>The player requesting the copies</p>
|
||||||
* @return <p>True if the player is the book's author</p>
|
* @param heldBook <p>The book to be copied</p>
|
||||||
|
* @return <p>True if the copying was successful</p>
|
||||||
*/
|
*/
|
||||||
private boolean isAuthor(Player player, BookMeta book) {
|
private boolean performCopy(int copies, @NotNull Player player, @NotNull ItemStack heldBook) {
|
||||||
String author = book.getAuthor();
|
//Make sure the player owns the book if authorOnlyCopy is enabled
|
||||||
String playerName = InputCleaningHelper.cleanString(player.getName());
|
if (BooksWithoutBordersConfig.getAuthorOnlyCopy() &&
|
||||||
if (author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author))) {
|
!player.hasPermission("bookswithoutborders.bypassAuthorOnlyCopy")) {
|
||||||
return true;
|
if (BookHelper.isNotAuthor(player, (BookMeta) Objects.requireNonNull(heldBook.getItemMeta()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "You must be the author of this book to use this command!");
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
return false;
|
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) {
|
||||||
|
return copyNextGenerationBook(bookMeta, player, copies);
|
||||||
|
} else {
|
||||||
|
//Make sure the player can pay for the copying
|
||||||
|
if (paymentUnSuccessful(player, copies)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
heldBook.setAmount(heldBook.getAmount() + copies);
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(player, "Book copied!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to take payment from a player
|
||||||
|
*
|
||||||
|
* @param player <p>The player to take the payment from</p>
|
||||||
|
* @param copies <p>The number of copies to create for the player</p>
|
||||||
|
* @return <p>True if the payment failed</p>
|
||||||
|
*/
|
||||||
|
private boolean paymentUnSuccessful(@NotNull Player player, int copies) {
|
||||||
|
return BooksWithoutBordersConfig.booksHavePrice() &&
|
||||||
|
!player.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||||
|
EconomyHelper.cannotPayForBookPrinting(player, copies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a book with the next generation relative to the input book
|
||||||
|
*
|
||||||
|
* @param bookMeta <p>The book to copy</p>
|
||||||
|
* @param player <p>The player copying the book</p>
|
||||||
|
* @param copies <p>The number of copies requested</p>
|
||||||
|
* @return <p>True if the book was successfully copied</p>
|
||||||
|
*/
|
||||||
|
private boolean copyNextGenerationBook(@NotNull BookMeta bookMeta, @NotNull Player player, int copies) {
|
||||||
|
//Copy the vanilla behavior of refusing copying any further
|
||||||
|
if (bookMeta.getGeneration() == BookMeta.Generation.COPY_OF_COPY ||
|
||||||
|
bookMeta.getGeneration() == BookMeta.Generation.TATTERED) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You cannot copy this book any further. " +
|
||||||
|
"You must have the original or a direct copy.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Make sure the player can fit the book in their inventory
|
||||||
|
int nextAvailableSlot = player.getInventory().firstEmpty();
|
||||||
|
if (nextAvailableSlot == -1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You need an available slot in your inventory.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Make sure the player can pay for the copying
|
||||||
|
if (paymentUnSuccessful(player, copies)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack itemStack = new ItemStack(Material.WRITTEN_BOOK);
|
||||||
|
itemStack.setItemMeta(bookMeta);
|
||||||
|
//Increase the generation of the book
|
||||||
|
BookHelper.increaseGeneration(itemStack);
|
||||||
|
itemStack.setAmount(copies);
|
||||||
|
|
||||||
|
player.getInventory().addItem(itemStack);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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,
|
||||||
int argumentCount = args.length;
|
@NotNull String[] arguments) {
|
||||||
|
int argumentCount = arguments.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getNumbers(1, 20), args[0]);
|
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getNumbers(1, 20), arguments[0]);
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
import net.knarcraft.bookswithoutborders.utility.EncryptionHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@@ -47,12 +48,6 @@ public class CommandDecrypt implements TabExecutor {
|
|||||||
//Warning: admin decrypt only allows decrypting files created by the same player. Not sure if intended
|
//Warning: admin decrypt only allows decrypting files created by the same player. Not sure if intended
|
||||||
if (args.length == 0 && BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
if (args.length == 0 && BooksWithoutBordersConfig.getAdminDecrypt() && player.hasPermission("bookswithoutborders.admin")) {
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||||
String fileName;
|
|
||||||
if (bookMetadata.hasTitle()) {
|
|
||||||
fileName = bookMetadata.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
|
||||||
} else {
|
|
||||||
fileName = "Untitled," + player.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
File encryptedDirectory = new File(path);
|
File encryptedDirectory = new File(path);
|
||||||
String[] encryptedFiles = encryptedDirectory.list();
|
String[] encryptedFiles = encryptedDirectory.list();
|
||||||
@@ -64,7 +59,7 @@ public class CommandDecrypt implements TabExecutor {
|
|||||||
//Get the "encryption key" from the filename
|
//Get the "encryption key" from the filename
|
||||||
String key = "";
|
String key = "";
|
||||||
for (String encryptedFile : encryptedFiles) {
|
for (String encryptedFile : encryptedFiles) {
|
||||||
if (encryptedFile.contains(fileName)) {
|
if (encryptedFile.contains(BookHelper.getBookFile(bookMetadata, player, true))) {
|
||||||
key = encryptedFile.substring(encryptedFile.indexOf("[") + 1, encryptedFile.indexOf("]"));
|
key = encryptedFile.substring(encryptedFile.indexOf("[") + 1, encryptedFile.indexOf("]"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -104,8 +99,9 @@ public class CommandDecrypt 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,
|
||||||
int argumentCount = args.length;
|
@NotNull String[] arguments) {
|
||||||
|
int argumentCount = arguments.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
List<String> info = new ArrayList<>();
|
List<String> info = new ArrayList<>();
|
||||||
info.add("<password>");
|
info.add("<password>");
|
||||||
|
@@ -1,9 +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.FileHelper;
|
import net.knarcraft.bookswithoutborders.gui.PagedBookIndex;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
import net.knarcraft.bookswithoutborders.utility.TabCompletionHelper;
|
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
|
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.TabExecutor;
|
import org.bukkit.command.TabExecutor;
|
||||||
@@ -14,43 +16,41 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the delete command
|
* Command executor for the delete command
|
||||||
*/
|
*/
|
||||||
public class CommandDelete implements TabExecutor {
|
public class CommandDelete 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[] arguments) {
|
||||||
if (!(sender instanceof Player)) {
|
if (!(sender instanceof 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return deleteBook(sender, args, false);
|
return deleteBook(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a book
|
* Deletes a book
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender trying to delete the book</p>
|
* @param sender <p>The sender trying to delete the book</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param deletePublic <p>Whether to delete a public book</p>
|
* @param deletePublic <p>Whether to delete a public book</p>
|
||||||
* @return <p>True if the book was deleted successfully</p>
|
* @return <p>True if the book was deleted successfully</p>
|
||||||
*/
|
*/
|
||||||
boolean deleteBook(CommandSender sender, String[] args, boolean deletePublic) {
|
protected boolean deleteBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean deletePublic) {
|
||||||
//List deletable files
|
String command = deletePublic ? "deletepublicbook" : "deletebook";
|
||||||
if (args.length == 0) {
|
if (PagedBookIndex.displayPage(arguments, sender, deletePublic, command)) {
|
||||||
FileHelper.printBooks(sender, deletePublic);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the file
|
//Delete the file
|
||||||
if (args.length == 1) {
|
if (arguments.length == 1) {
|
||||||
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, deletePublic);
|
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, deletePublic);
|
||||||
if (!availableBooks.isEmpty()) {
|
if (!availableBooks.isEmpty()) {
|
||||||
performBookDeletion(sender, args[0], deletePublic);
|
performBookDeletion(sender, arguments[0], deletePublic);
|
||||||
//Update the book list
|
//Update the book list
|
||||||
BooksWithoutBorders.updateBooks(sender, deletePublic);
|
BooksWithoutBorders.updateBooks(sender, deletePublic);
|
||||||
return true;
|
return true;
|
||||||
@@ -70,7 +70,7 @@ public class CommandDelete implements TabExecutor {
|
|||||||
* @param fileName <p>The file name of the book</p>
|
* @param fileName <p>The file name of the book</p>
|
||||||
* @param isPublic <p>Whether the book to delete is public or not</p>
|
* @param isPublic <p>Whether the book to delete is public or not</p>
|
||||||
*/
|
*/
|
||||||
public void performBookDeletion(CommandSender sender, String fileName, Boolean isPublic) {
|
public void performBookDeletion(@NotNull CommandSender sender, @NotNull String fileName, @NotNull Boolean isPublic) {
|
||||||
//If the file name is an index of the load list, load the book
|
//If the file name is an index of the load list, load the book
|
||||||
try {
|
try {
|
||||||
int loadListIndex = Integer.parseInt(fileName);
|
int loadListIndex = Integer.parseInt(fileName);
|
||||||
@@ -82,13 +82,9 @@ public class CommandDelete implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Get the file to be deleted
|
//Get the file to be deleted
|
||||||
File file;
|
String bookDirectory = BookHelper.getBookDirectoryPathString(
|
||||||
if (isPublic) {
|
isPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||||
file = FileHelper.getBookFile(getBookFolder() + fileName);
|
File file = BookFileHelper.getBookFile(bookDirectory + fileName);
|
||||||
} else {
|
|
||||||
file = FileHelper.getBookFile(getBookFolder() +
|
|
||||||
InputCleaningHelper.cleanString(sender.getName()) + getSlash() + fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send message if no such file could be found
|
//Send message if no such file could be found
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
@@ -109,8 +105,9 @@ public class CommandDelete 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,
|
||||||
return doTabCompletion(sender, args, false);
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +118,8 @@ public class CommandDelete implements TabExecutor {
|
|||||||
* @param deletePublic <p>Whether to delete a public book</p>
|
* @param deletePublic <p>Whether to delete a public book</p>
|
||||||
* @return <p>A list of available arguments</p>
|
* @return <p>A list of available arguments</p>
|
||||||
*/
|
*/
|
||||||
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean deletePublic) {
|
@NotNull
|
||||||
|
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] args, boolean deletePublic) {
|
||||||
int argumentCount = args.length;
|
int argumentCount = args.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic),
|
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, deletePublic),
|
||||||
|
@@ -13,13 +13,13 @@ import java.util.List;
|
|||||||
public class CommandDeletePublic extends CommandDelete implements TabExecutor {
|
public class CommandDeletePublic extends CommandDelete implements TabExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] arguments) {
|
||||||
return deleteBook(sender, args, true);
|
return deleteBook(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] arguments) {
|
||||||
return doTabCompletion(sender, args, true);
|
return doTabCompletion(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
@@ -13,6 +13,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,25 +24,28 @@ import java.util.List;
|
|||||||
public class CommandEncrypt implements TabExecutor {
|
public class CommandEncrypt 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,
|
||||||
if (performPreChecks(sender, args, 1, "You must specify a key to encrypt a book!") == null) {
|
@NotNull String[] arguments) {
|
||||||
|
if (performPreChecks(sender, arguments, 1, "You must specify a key to encrypt a book!") == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionStyle encryptionStyle = args.length == 2 ? EncryptionStyle.getFromString(args[1]) : EncryptionStyle.SUBSTITUTION;
|
EncryptionStyle encryptionStyle = arguments.length == 2 ? EncryptionStyle.getFromString(arguments[1]) : EncryptionStyle.SUBSTITUTION;
|
||||||
return encryptBook(encryptionStyle, (Player) sender, args[0], "");
|
return encryptBook(encryptionStyle, (Player) sender, arguments[0], "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs necessary pre-checks before going through with the encryption
|
* Performs necessary pre-checks before going through with the encryption
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender trying to encrypt a book</p>
|
* @param sender <p>The sender trying to encrypt a book</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param necessaryArguments <p>How many arguments is the minimum requirement</p>
|
* @param necessaryArguments <p>How many arguments is the minimum requirement</p>
|
||||||
* @param missingArgumentsError <p>The error to show if a required argument is missing</p>
|
* @param missingArgumentsError <p>The error to show if a required argument is missing</p>
|
||||||
* @return <p>The metadata of the book to encrypt, or null if any checks fail</p>
|
* @return <p>The metadata of the book to encrypt, or null if any checks fail</p>
|
||||||
*/
|
*/
|
||||||
BookMeta performPreChecks(CommandSender sender, String[] args, int necessaryArguments, String missingArgumentsError) {
|
@Nullable
|
||||||
|
protected BookMeta performPreChecks(@NotNull CommandSender sender, @NotNull String[] arguments,
|
||||||
|
int necessaryArguments, @NotNull String missingArgumentsError) {
|
||||||
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 null;
|
return null;
|
||||||
@@ -53,7 +57,7 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int argumentCount = args.length;
|
int argumentCount = arguments.length;
|
||||||
if (argumentCount < necessaryArguments) {
|
if (argumentCount < necessaryArguments) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, missingArgumentsError);
|
BooksWithoutBorders.sendErrorMessage(player, missingArgumentsError);
|
||||||
return null;
|
return null;
|
||||||
@@ -85,7 +89,8 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
* @param group <p>The group to encrypt for</p>
|
* @param group <p>The group to encrypt for</p>
|
||||||
* @return <p>True if the book was encrypted successfully</p>
|
* @return <p>True if the book was encrypted successfully</p>
|
||||||
*/
|
*/
|
||||||
boolean encryptBook(EncryptionStyle encryptionStyle, Player player, String key, String group) {
|
protected boolean encryptBook(@NotNull EncryptionStyle encryptionStyle, @NotNull Player player, @NotNull String key,
|
||||||
|
@NotNull String group) {
|
||||||
ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true);
|
ItemSlot heldSlot = InventoryHelper.getHeldSlotBook(player, false, false, true, true);
|
||||||
ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key, encryptionStyle, group);
|
ItemStack encryptedBook = EncryptionHelper.encryptBook(player, heldSlot == ItemSlot.MAIN_HAND, key, encryptionStyle, group);
|
||||||
|
|
||||||
@@ -98,8 +103,10 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
@NotNull
|
||||||
return doTabCompletion(args, false);
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,7 +116,8 @@ public class CommandEncrypt implements TabExecutor {
|
|||||||
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
|
* @param groupEncrypt <p>Whether to auto-complete for group encryption</p>
|
||||||
* @return <p>The strings to auto-complete</p>
|
* @return <p>The strings to auto-complete</p>
|
||||||
*/
|
*/
|
||||||
protected List<String> doTabCompletion(String[] args, boolean groupEncrypt) {
|
@NotNull
|
||||||
|
protected List<String> doTabCompletion(@NotNull String[] args, boolean groupEncrypt) {
|
||||||
int argumentsCount = args.length;
|
int argumentsCount = args.length;
|
||||||
|
|
||||||
List<String> encryptionStyles = new ArrayList<>();
|
List<String> encryptionStyles = new ArrayList<>();
|
||||||
|
@@ -20,7 +20,8 @@ import java.util.List;
|
|||||||
public class CommandFormat implements TabExecutor {
|
public class CommandFormat 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[] arguments) {
|
||||||
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;
|
||||||
@@ -32,7 +33,14 @@ public class CommandFormat implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
heldBook.setItemMeta(BookFormatter.formatPages((BookMeta) heldBook.getItemMeta()));
|
BookMeta meta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
|
||||||
|
if (meta == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Unable to get metadata from the held book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
heldBook.setItemMeta(BookFormatter.formatPages(meta));
|
||||||
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book formatted!");
|
BooksWithoutBorders.sendSuccessMessage(sender, "Book formatted!");
|
||||||
|
|
||||||
@@ -40,7 +48,8 @@ public class CommandFormat 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[] arguments) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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.gui.PagedBookIndex;
|
||||||
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;
|
||||||
@@ -12,6 +13,7 @@ import org.bukkit.command.TabExecutor;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,51 +26,67 @@ public class CommandGive implements TabExecutor {
|
|||||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||||
|
|
||||||
@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[] arguments) {
|
||||||
if (!(sender instanceof Player)) {
|
if (!(sender instanceof 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return giveBook(sender, args, false, "player");
|
return giveBook(sender, arguments, false, "player");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a book to another player
|
* Gives a book to another player
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender trying to give a book</p>
|
* @param sender <p>The sender trying to give a book</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param givePublic <p>Whether to give a public book</p>
|
* @param givePublic <p>Whether to give a public book</p>
|
||||||
* @param folder <p>The folder containing the book to load</p>
|
* @param folder <p>The folder containing the book to load</p>
|
||||||
* @return <p>True if the book was given successfully</p>
|
* @return <p>True if the book was given successfully</p>
|
||||||
*/
|
*/
|
||||||
boolean giveBook(CommandSender sender, String[] args, boolean givePublic, String folder) {
|
boolean giveBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean givePublic,
|
||||||
if (args.length == 1 || args.length > 4) {
|
@NotNull String folder) {
|
||||||
|
String command = givePublic ? "givepublicbook" : "givebook";
|
||||||
|
if (PagedBookIndex.displayPage(arguments, sender, givePublic, command)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.length == 1 || arguments.length > 4) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect number of arguments for this command!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Incorrect number of arguments for this command!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length == 0) {
|
|
||||||
FileHelper.printBooks(sender, givePublic);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Organize and parse input
|
//Organize and parse input
|
||||||
String bookIdentifier = args[0];
|
String bookIdentifier = arguments[0];
|
||||||
String receivingPlayerName = args[1];
|
String receivingPlayerName = arguments[1];
|
||||||
String copies = "1";
|
String copies = "1";
|
||||||
String isSigned = "true";
|
String isSigned = "true";
|
||||||
if (args.length == 4) {
|
if (arguments.length == 4) {
|
||||||
copies = args[2];
|
copies = arguments[2];
|
||||||
isSigned = args[3];
|
isSigned = arguments[3];
|
||||||
} else if (args.length == 3) {
|
} else if (arguments.length == 3) {
|
||||||
if (args[2].equalsIgnoreCase("true") || args[2].equalsIgnoreCase("false")) {
|
if (arguments[2].equalsIgnoreCase("true") || arguments[2].equalsIgnoreCase("false")) {
|
||||||
isSigned = args[2];
|
isSigned = arguments[2];
|
||||||
} else {
|
} else {
|
||||||
copies = args[2];
|
copies = arguments[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Try and find the target player
|
||||||
|
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
||||||
|
if (receivingPlayer == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make sure the receiver is able to fit the book
|
||||||
|
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory" +
|
||||||
|
" to receive books!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//Load books available to the player
|
//Load books available to the player
|
||||||
try {
|
try {
|
||||||
Integer.parseInt(bookIdentifier);
|
Integer.parseInt(bookIdentifier);
|
||||||
@@ -76,29 +94,8 @@ public class CommandGive implements TabExecutor {
|
|||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Player receivingPlayer = booksWithoutBorders.getServer().getPlayer(receivingPlayerName);
|
|
||||||
if (receivingPlayer == null) {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Player not found!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (receivingPlayer.getInventory().firstEmpty() == -1) {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Receiving player must have space in their inventory to receive books!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
|
||||||
try {
|
try {
|
||||||
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
return loadAndGiveBook(bookIdentifier, sender, receivingPlayer, isSigned, folder, copies);
|
||||||
if (newBook != null) {
|
|
||||||
receivingPlayer.getInventory().addItem(newBook);
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Invalid number of book copies specified!");
|
||||||
return false;
|
return false;
|
||||||
@@ -106,8 +103,9 @@ public class CommandGive 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,
|
||||||
return doTabCompletion(sender, args, false);
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,7 +116,8 @@ public class CommandGive implements TabExecutor {
|
|||||||
* @param listPublic <p>Whether to list public files or player files</p>
|
* @param listPublic <p>Whether to list public files or player files</p>
|
||||||
* @return <p>A list of available choices</p>
|
* @return <p>A list of available choices</p>
|
||||||
*/
|
*/
|
||||||
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean listPublic) {
|
@Nullable
|
||||||
|
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] args, boolean listPublic) {
|
||||||
Server server = booksWithoutBorders.getServer();
|
Server server = booksWithoutBorders.getServer();
|
||||||
|
|
||||||
int argumentCount = args.length;
|
int argumentCount = args.length;
|
||||||
@@ -139,12 +138,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<>();
|
||||||
}
|
}
|
||||||
@@ -152,4 +151,32 @@ public class CommandGive implements TabExecutor {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a book and gives it to the correct player
|
||||||
|
*
|
||||||
|
* @param bookIdentifier <p>The file name specified by the user</p>
|
||||||
|
* @param sender <p>The player trying to give the book</p>
|
||||||
|
* @param receivingPlayer <p>The player which is the receiver of the book</p>
|
||||||
|
* @param isSigned <p>The value given for if the given book should be signed or not</p>
|
||||||
|
* @param folder <p>The folder containing the book to load</p>
|
||||||
|
* @param copies <p>The number of copies the player wants to give</p>
|
||||||
|
* @return <p>True if the book was successfully given</p>
|
||||||
|
*/
|
||||||
|
private boolean loadAndGiveBook(@NotNull String bookIdentifier, @NotNull CommandSender sender,
|
||||||
|
@NotNull Player receivingPlayer, @NotNull String isSigned, @NotNull String folder,
|
||||||
|
@NotNull String copies) throws NumberFormatException {
|
||||||
|
String bookToLoad = InputCleaningHelper.cleanString(bookIdentifier);
|
||||||
|
ItemStack newBook = BookLoader.loadBook(sender, bookToLoad, isSigned, folder, Integer.parseInt(copies));
|
||||||
|
if (newBook != null) {
|
||||||
|
//NOTE: As this method bypasses cost, it should also bypass the generation change
|
||||||
|
receivingPlayer.getInventory().addItem(newBook);
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(sender, "Book sent!");
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(receivingPlayer, "Book received!");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Book failed to load!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ 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;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -13,13 +14,16 @@ import java.util.List;
|
|||||||
public class CommandGivePublic extends CommandGive implements TabExecutor {
|
public class CommandGivePublic extends CommandGive implements TabExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
return giveBook(sender, args, true, "public");
|
@NotNull String[] arguments) {
|
||||||
|
return giveBook(sender, arguments, true, "public");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
@Nullable
|
||||||
return doTabCompletion(sender, args, true);
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,9 @@ import java.util.List;
|
|||||||
public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
BookMeta bookMetadata = performPreChecks(sender, args, 2,
|
@NotNull String[] arguments) {
|
||||||
|
BookMeta bookMetadata = performPreChecks(sender, arguments, 2,
|
||||||
"You must specify a group name and key to encrypt a book!");
|
"You must specify a group name and key to encrypt a book!");
|
||||||
|
|
||||||
if (bookMetadata == null) {
|
if (bookMetadata == null) {
|
||||||
@@ -32,13 +33,14 @@ public class CommandGroupEncrypt extends CommandEncrypt implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptionStyle encryptionStyle = args.length == 3 ? EncryptionStyle.getFromString(args[2]) : EncryptionStyle.SUBSTITUTION;
|
EncryptionStyle encryptionStyle = arguments.length == 3 ? EncryptionStyle.getFromString(arguments[2]) : EncryptionStyle.SUBSTITUTION;
|
||||||
return encryptBook(encryptionStyle, (Player) sender, args[1], args[0]);
|
return encryptBook(encryptionStyle, (Player) sender, arguments[1], arguments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
public @NotNull List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||||
return doTabCompletion(args, true);
|
@NotNull String alias, @NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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.gui.PagedBookIndex;
|
||||||
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;
|
||||||
@@ -21,20 +22,22 @@ import java.util.List;
|
|||||||
public class CommandLoad implements TabExecutor {
|
public class CommandLoad 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,
|
||||||
return loadBook(sender, args, "player", false);
|
@NotNull String[] arguments) {
|
||||||
|
return loadBook(sender, arguments, "player", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a stored book
|
* Loads a stored book
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender asking to load the book</p>
|
* @param sender <p>The sender asking to load the book</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param directory <p>The directory to load from (public/player)</p>
|
* @param directory <p>The directory to load from (public/player)</p>
|
||||||
* @param loadPublic <p>Whether to list public files as loadable</p>
|
* @param loadPublic <p>Whether to list public files as loadable</p>
|
||||||
* @return <p>True if the book was loaded successfully</p>
|
* @return <p>True if the book was loaded successfully</p>
|
||||||
*/
|
*/
|
||||||
boolean loadBook(CommandSender sender, String[] args, String directory, boolean loadPublic) {
|
public boolean loadBook(@NotNull CommandSender sender, @NotNull String[] arguments, @NotNull String directory,
|
||||||
|
boolean loadPublic) {
|
||||||
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;
|
||||||
@@ -45,26 +48,25 @@ public class CommandLoad implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int argumentCount = args.length;
|
int argumentCount = arguments.length;
|
||||||
|
|
||||||
//Show books available to the player
|
String command = loadPublic ? "loadpublicbook" : "loadbook";
|
||||||
if (argumentCount == 0) {
|
if (PagedBookIndex.displayPage(arguments, sender, loadPublic, command)) {
|
||||||
FileHelper.printBooks(sender, loadPublic);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Organize and parse input
|
//Organize and parse input
|
||||||
String bookIdentifier = args[0];
|
String bookIdentifier = arguments[0];
|
||||||
String copies = "1";
|
String copies = "1";
|
||||||
String isSigned = "true";
|
String isSigned = "true";
|
||||||
if (args.length == 3) {
|
if (argumentCount == 3) {
|
||||||
copies = args[1];
|
copies = arguments[1];
|
||||||
isSigned = args[2];
|
isSigned = arguments[2];
|
||||||
} else if (args.length == 2) {
|
} else if (argumentCount == 2) {
|
||||||
if (args[1].equalsIgnoreCase("true") || args[1].equalsIgnoreCase("false")) {
|
if (arguments[1].equalsIgnoreCase("true") || arguments[1].equalsIgnoreCase("false")) {
|
||||||
isSigned = args[1];
|
isSigned = arguments[1];
|
||||||
} else {
|
} else {
|
||||||
copies = args[1];
|
copies = arguments[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,32 +96,34 @@ public class CommandLoad 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,
|
||||||
return doTabCompletion(sender, args, false);
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the actual tab completion
|
* Performs the actual tab completion
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender of the command</p>
|
* @param sender <p>The sender of the command</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param loadPublic <p>Whether to list public files or player files</p>
|
* @param loadPublic <p>Whether to list public files or player files</p>
|
||||||
* @return <p>A list of available choices</p>
|
* @return <p>A list of available choices</p>
|
||||||
*/
|
*/
|
||||||
protected List<String> doTabCompletion(CommandSender sender, String[] args, boolean loadPublic) {
|
@NotNull
|
||||||
int argumentCount = args.length;
|
protected List<String> doTabCompletion(@NotNull CommandSender sender, @NotNull String[] arguments, boolean loadPublic) {
|
||||||
|
int argumentCount = arguments.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
//Return list of books
|
//Return list of books
|
||||||
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic),
|
return TabCompletionHelper.filterMatchingContains(BooksWithoutBorders.getAvailableBooks(sender, loadPublic),
|
||||||
args[0]);
|
arguments[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), arguments[1]);
|
||||||
} else if (argumentCount == 3) {
|
} else if (argumentCount == 3) {
|
||||||
//Signed
|
//Signed
|
||||||
try {
|
try {
|
||||||
Integer.parseInt(args[1]);
|
Integer.parseInt(arguments[1]);
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionHelper.getBooleans(), args[2]);
|
return TabCompletionHelper.filterMatchingStartsWith(TabCompletionTypeHelper.getBooleans(), arguments[2]);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
@@ -13,13 +13,15 @@ import java.util.List;
|
|||||||
public class CommandLoadPublic extends CommandLoad implements CommandExecutor {
|
public class CommandLoadPublic extends CommandLoad implements CommandExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
return loadBook(sender, args, "public", true);
|
@NotNull String[] arguments) {
|
||||||
|
return loadBook(sender, arguments, "public", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
return doTabCompletion(sender, args, true);
|
@NotNull String[] arguments) {
|
||||||
|
return doTabCompletion(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -16,18 +16,24 @@ import java.util.List;
|
|||||||
public class CommandReload implements TabExecutor {
|
public class CommandReload 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[] arguments) {
|
||||||
if (BooksWithoutBordersConfig.loadConfig()) {
|
if (BooksWithoutBordersConfig.loadConfig()) {
|
||||||
BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!");
|
BooksWithoutBorders.sendSuccessMessage(sender, "BooksWithoutBorders configuration reloaded!");
|
||||||
} else {
|
} else {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Reload Failed!");
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "See console for details");
|
BooksWithoutBorders.sendErrorMessage(sender, "See console for details");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload books
|
||||||
|
BooksWithoutBorders.updateBooks(sender, true);
|
||||||
|
BooksWithoutBorders.clearBookData();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,10 +2,13 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookFileHelper;
|
||||||
|
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 net.md_5.bungee.api.ChatColor;
|
||||||
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;
|
||||||
@@ -18,13 +21,11 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getCommandColor;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getErrorColor;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getTitleAuthorSeparator;
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the save command
|
* Command executor for the save command
|
||||||
@@ -32,19 +33,19 @@ import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixN
|
|||||||
public class CommandSave implements TabExecutor {
|
public class CommandSave implements TabExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] arguments) {
|
||||||
return saveHeldBook(sender, args, false);
|
return saveHeldBook(sender, arguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the player's held book if it exists
|
* Saves the player's held book if it exists
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender of the command</p>
|
* @param sender <p>The sender of the command</p>
|
||||||
* @param args <p>The arguments given</p>
|
* @param arguments <p>The arguments given</p>
|
||||||
* @param savePublic <p>Whether to save the book in the public directory or the player directory</p>
|
* @param savePublic <p>Whether to save the book in the public directory or the player directory</p>
|
||||||
* @return <p>True if a book was saved successfully</p>
|
* @return <p>True if a book was saved successfully</p>
|
||||||
*/
|
*/
|
||||||
boolean saveHeldBook(CommandSender sender, String[] args, boolean savePublic) {
|
boolean saveHeldBook(@NotNull CommandSender sender, @NotNull String[] arguments, boolean savePublic) {
|
||||||
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;
|
||||||
@@ -53,7 +54,7 @@ public class CommandSave implements TabExecutor {
|
|||||||
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
|
ItemSlot holdingSlot = InventoryHelper.getHeldSlotBook(player, false, false, false, false);
|
||||||
if (holdingSlot != ItemSlot.NONE) {
|
if (holdingSlot != ItemSlot.NONE) {
|
||||||
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
|
ItemStack holdingItem = InventoryHelper.getHeldItem(player, holdingSlot == ItemSlot.MAIN_HAND);
|
||||||
boolean duplicate = args.length == 1 && Boolean.parseBoolean(args[0]);
|
boolean duplicate = arguments.length == 1 && Boolean.parseBoolean(arguments[0]);
|
||||||
saveBook(player, holdingItem, duplicate, savePublic);
|
saveBook(player, holdingItem, duplicate, savePublic);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -70,64 +71,76 @@ public class CommandSave implements TabExecutor {
|
|||||||
* @param overwrite <p>Whether to overwrite any existing books</p>
|
* @param overwrite <p>Whether to overwrite any existing books</p>
|
||||||
* @param saveToPublicFolder <p>Whether to save the book to the public folder instead of the player folder</p>
|
* @param saveToPublicFolder <p>Whether to save the book to the public folder instead of the player folder</p>
|
||||||
*/
|
*/
|
||||||
public void saveBook(Player player, ItemStack heldBook, boolean overwrite, boolean saveToPublicFolder) {
|
public void saveBook(@NotNull Player player, @NotNull ItemStack heldBook, boolean overwrite, boolean saveToPublicFolder) {
|
||||||
BookMeta book = (BookMeta) heldBook.getItemMeta();
|
BookMeta book = (BookMeta) heldBook.getItemMeta();
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for your held book!");
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for your held book!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String savePath;
|
//Only allow saving of own books if enabled
|
||||||
if (saveToPublicFolder) {
|
if (BooksWithoutBordersConfig.getAuthorOnlySave() && !saveToPublicFolder &&
|
||||||
savePath = getBookFolder();
|
(!player.hasPermission("bookswithoutborders.bypassAuthorOnlySave") &&
|
||||||
} else {
|
BookHelper.isNotAuthor(player, book))) {
|
||||||
savePath = getBookFolder() + cleanString(player.getName()) + getSlash();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String savePath = BookHelper.getBookDirectoryPathString(
|
||||||
|
saveToPublicFolder ? BookDirectory.PUBLIC : BookDirectory.PLAYER, player);
|
||||||
|
|
||||||
|
if (savePath == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! Unable to find the save path!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Generate book filename
|
//Generate book filename
|
||||||
String fileName;
|
String fileName;
|
||||||
if (!book.hasTitle()) {
|
try {
|
||||||
fileName = "Untitled," + player.getName();
|
fileName = BookHelper.getBookFile(book, player, saveToPublicFolder);
|
||||||
} else {
|
} catch (IllegalArgumentException exception) {
|
||||||
fileName = book.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + book.getAuthor();
|
BooksWithoutBorders.sendErrorMessage(player, exception.getMessage());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
//Make sure the used folders exist
|
//Make sure the used folders exist
|
||||||
File file = new File(savePath);
|
File file = new File(savePath);
|
||||||
if (!file.exists() && !file.mkdir()) {
|
if (!file.exists() && !file.mkdir()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||||
|
" a server admin!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File[] foundFiles = file.listFiles();
|
File[] foundFiles = file.listFiles();
|
||||||
if (foundFiles == null) {
|
if (foundFiles == null) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult server admin!");
|
BooksWithoutBorders.sendErrorMessage(player, "Saving Failed! If this continues to happen, consult" +
|
||||||
|
" a server admin!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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) {
|
||||||
//TODO: Decide if this makes sense or needs to be changed
|
//TODO: Decide if this makes sense or needs to be changed
|
||||||
//Skip duplicate book
|
//Skip duplicate book
|
||||||
if (!fileName.contains("Untitled") && !overwrite) {
|
if (!fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Book is already saved!");
|
BooksWithoutBorders.sendErrorMessage(player, "Book is already saved!");
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " + getErrorColor() + "to overwrite!");
|
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " +
|
||||||
|
getErrorColor() + "to overwrite!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Skip if duplicate limit is reached
|
//Skip if duplicate limit is reached
|
||||||
if (foundDuplicates > BooksWithoutBordersConfig.getBookDuplicateLimit()) {
|
if (foundDuplicates > BooksWithoutBordersConfig.getBookDuplicateLimit()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName + " duplicates reached!");
|
BooksWithoutBorders.sendErrorMessage(player, "Maximum amount of " + fileName +
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " + getErrorColor() + "to overwrite!");
|
" duplicates reached!");
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Use " + getCommandColor() + "/savebook true " +
|
||||||
|
getErrorColor() + "to overwrite!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Alter duplicated filename
|
//Alter duplicated filename
|
||||||
if (fileName.contains("Untitled") && !overwrite) {
|
if (fileName.contains("Untitled" + getTitleAuthorSeparator()) && !overwrite) {
|
||||||
fileName = "(" + foundDuplicates + ")" + fileName;
|
fileName = "(" + foundDuplicates + ")" + fileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,15 +154,17 @@ public class CommandSave implements TabExecutor {
|
|||||||
|
|
||||||
//Update the relevant book list
|
//Update the relevant book list
|
||||||
BooksWithoutBorders.updateBooks(player, saveToPublicFolder);
|
BooksWithoutBorders.updateBooks(player, saveToPublicFolder);
|
||||||
BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + "\"");
|
BooksWithoutBorders.sendSuccessMessage(player, "Book Saved as \"" + fileName + ChatColor.RESET + "\"");
|
||||||
} catch (IOException e) {
|
} catch (IOException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save book");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
@NotNull
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,8 +11,9 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class CommandSavePublic extends CommandSave implements TabExecutor {
|
public class CommandSavePublic extends CommandSave 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,
|
||||||
return saveHeldBook(sender, args, true);
|
@NotNull String[] arguments) {
|
||||||
|
return saveHeldBook(sender, arguments, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,8 @@ package net.knarcraft.bookswithoutborders.command;
|
|||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
|
import net.knarcraft.knarlib.property.ColorConversion;
|
||||||
|
import net.knarcraft.knarlib.util.ColorHelper;
|
||||||
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;
|
||||||
@@ -10,6 +12,7 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -20,13 +23,14 @@ import java.util.List;
|
|||||||
public class CommandSetAuthor implements TabExecutor {
|
public class CommandSetAuthor 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[] arguments) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (arguments.length < 1) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
|
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -41,8 +45,12 @@ public class CommandSetAuthor implements TabExecutor {
|
|||||||
boolean mainHand = heldBookSlot == ItemSlot.MAIN_HAND;
|
boolean mainHand = heldBookSlot == ItemSlot.MAIN_HAND;
|
||||||
ItemStack heldBook = InventoryHelper.getHeldItem(player, mainHand);
|
ItemStack heldBook = InventoryHelper.getHeldItem(player, mainHand);
|
||||||
BookMeta bookMetaData = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
BookMeta bookMetaData = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||||
|
if (bookMetaData == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata for the held book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
String author = String.join(" ", args);
|
String author = ColorHelper.translateColorCodes(String.join(" ", arguments), ColorConversion.RGB);
|
||||||
bookMetaData.setAuthor(author);
|
bookMetaData.setAuthor(author);
|
||||||
heldBook.setItemMeta(bookMetaData);
|
heldBook.setItemMeta(bookMetaData);
|
||||||
BooksWithoutBorders.sendSuccessMessage(player, "Book author set to " + author + "!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Book author set to " + author + "!");
|
||||||
@@ -50,8 +58,10 @@ public class CommandSetAuthor implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
@Nullable
|
||||||
if (args.length == 1) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
if (arguments.length == 1) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
|
@@ -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;
|
||||||
@@ -25,35 +26,36 @@ public class CommandSetBookPrice implements TabExecutor {
|
|||||||
private List<String> paymentTypes;
|
private List<String> paymentTypes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
//Clear the current price
|
//Clear the current price
|
||||||
if (args.length == 0) {
|
if (arguments.length == 0) {
|
||||||
clearItemPrice(sender);
|
clearItemPrice(sender);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Warn about missing arguments
|
//Warn about missing arguments
|
||||||
if (args.length < 2) {
|
if (arguments.length < 2) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "[Item/Eco] and [quantity] must be specified!");
|
BooksWithoutBorders.sendErrorMessage(sender, "[Item/Eco] and [quantity] must be specified!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Warn about invalid argument
|
//Warn about invalid argument
|
||||||
if (!args[0].equalsIgnoreCase("Item") && !args[0].equalsIgnoreCase("Eco")) {
|
if (!arguments[0].equalsIgnoreCase("Item") && !arguments[0].equalsIgnoreCase("Eco")) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "Price type must be \"Item\" or \"Eco\"!");
|
BooksWithoutBorders.sendErrorMessage(sender, "Price type must be \"Item\" or \"Eco\"!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
double price = Double.parseDouble(args[1]);
|
double price = Double.parseDouble(arguments[1]);
|
||||||
if (price <= 0) {
|
if (price <= 0) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "[quantity] must be greater than 0!");
|
BooksWithoutBorders.sendErrorMessage(sender, "[quantity] must be greater than 0!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args[0].equalsIgnoreCase("Item")) {
|
if (arguments[0].equalsIgnoreCase("Item")) {
|
||||||
return setItemPrice(sender, price);
|
return setItemPrice(sender, price);
|
||||||
} else if (args[0].equalsIgnoreCase("Eco")) {
|
} else if (arguments[0].equalsIgnoreCase("Eco")) {
|
||||||
return setEconomyPrice(sender, price);
|
return setEconomyPrice(sender, price);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
@@ -67,7 +69,7 @@ public class CommandSetBookPrice implements TabExecutor {
|
|||||||
*
|
*
|
||||||
* @param sender <p>The sender of the command</p>
|
* @param sender <p>The sender of the command</p>
|
||||||
*/
|
*/
|
||||||
private void clearItemPrice(CommandSender sender) {
|
private void clearItemPrice(@NotNull CommandSender sender) {
|
||||||
BooksWithoutBordersConfig.setBookPriceType(null);
|
BooksWithoutBordersConfig.setBookPriceType(null);
|
||||||
BooksWithoutBordersConfig.setBookPriceQuantity(0);
|
BooksWithoutBordersConfig.setBookPriceQuantity(0);
|
||||||
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Item type name");
|
booksWithoutBorders.getConfig().set("Options.Price_to_create_book.Item_type", "Item type name");
|
||||||
@@ -84,7 +86,7 @@ public class CommandSetBookPrice implements TabExecutor {
|
|||||||
* @param price <p>The new price</p>
|
* @param price <p>The new price</p>
|
||||||
* @return <p>True if the price was changed successfully</p>
|
* @return <p>True if the price was changed successfully</p>
|
||||||
*/
|
*/
|
||||||
private boolean setItemPrice(CommandSender sender, double price) {
|
private boolean setItemPrice(@NotNull CommandSender sender, double price) {
|
||||||
if (!(sender instanceof Player player)) {
|
if (!(sender instanceof Player player)) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "[Item] price can only be used by a player!");
|
BooksWithoutBorders.sendErrorMessage(sender, "[Item] price can only be used by a player!");
|
||||||
return false;
|
return false;
|
||||||
@@ -116,7 +118,7 @@ public class CommandSetBookPrice implements TabExecutor {
|
|||||||
* @param price <p>The new price</p>
|
* @param price <p>The new price</p>
|
||||||
* @return <p>True if the price was changed successfully</p>
|
* @return <p>True if the price was changed successfully</p>
|
||||||
*/
|
*/
|
||||||
private boolean setEconomyPrice(CommandSender sender, double price) {
|
private boolean setEconomyPrice(@NotNull CommandSender sender, double price) {
|
||||||
if (EconomyHelper.setupEconomy()) {
|
if (EconomyHelper.setupEconomy()) {
|
||||||
BooksWithoutBordersConfig.setBookPriceQuantity(price);
|
BooksWithoutBordersConfig.setBookPriceQuantity(price);
|
||||||
BooksWithoutBordersConfig.setBookPriceType(Material.AIR);
|
BooksWithoutBordersConfig.setBookPriceType(Material.AIR);
|
||||||
@@ -135,16 +137,17 @@ public class CommandSetBookPrice implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
if (paymentTypes == null) {
|
if (paymentTypes == null) {
|
||||||
initializeTabCompleteLists();
|
initializeTabCompleteLists();
|
||||||
}
|
}
|
||||||
|
|
||||||
int argumentCount = args.length;
|
int argumentCount = arguments.length;
|
||||||
if (argumentCount == 1) {
|
if (argumentCount == 1) {
|
||||||
return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, args[0]);
|
return TabCompletionHelper.filterMatchingStartsWith(paymentTypes, arguments[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), arguments[1]);
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,111 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.container.Bookshelf;
|
||||||
|
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
|
||||||
|
import net.knarcraft.knarlib.util.TabCompletionHelper;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The command for setting information for a chiseled bookshelf
|
||||||
|
*/
|
||||||
|
public class CommandSetBookshelfData implements TabExecutor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
if (!(commandSender instanceof Player player)) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(commandSender, "This command must be used by a player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block targetBlock = player.getTargetBlockExact(7);
|
||||||
|
if (targetBlock == null || targetBlock.getType() != Material.CHISELED_BOOKSHELF) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(commandSender, "You are not looking at a bookshelf!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BookshelfHandler shelfHandler = BooksWithoutBorders.getBookshelfHandler();
|
||||||
|
Bookshelf bookshelf = shelfHandler.getFromLocation(targetBlock.getLocation());
|
||||||
|
|
||||||
|
if (arguments.length < 2) {
|
||||||
|
if (arguments.length == 1 && arguments[0].equalsIgnoreCase("delete")) {
|
||||||
|
if (bookshelf != null) {
|
||||||
|
shelfHandler.unregisterBookshelf(bookshelf);
|
||||||
|
shelfHandler.save();
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(commandSender, "Bookshelf successfully deleted");
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(commandSender, "The block you are looking at is not a registered bookshelf");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all arguments as a space-separated string
|
||||||
|
StringBuilder builder = new StringBuilder(arguments[1]);
|
||||||
|
for (int i = 2; i < arguments.length; i++) {
|
||||||
|
builder.append(" ").append(arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (arguments[0].toLowerCase()) {
|
||||||
|
case "name":
|
||||||
|
if (bookshelf == null) {
|
||||||
|
Bookshelf newShelf = new Bookshelf(targetBlock.getLocation(), arguments[1], new ArrayList<>());
|
||||||
|
shelfHandler.registerBookshelf(newShelf);
|
||||||
|
} else {
|
||||||
|
bookshelf.setTitle(builder.toString());
|
||||||
|
}
|
||||||
|
shelfHandler.save();
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(commandSender, "Title successfully saved");
|
||||||
|
return true;
|
||||||
|
case "lore":
|
||||||
|
if (bookshelf == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(commandSender, "You must name the bookshelf before " +
|
||||||
|
"assigning lore!");
|
||||||
|
} else {
|
||||||
|
List<String> loreParts = Arrays.asList(builder.toString().split(BooksWithoutBordersConfig.getLoreSeparator()));
|
||||||
|
bookshelf.setLore(loreParts);
|
||||||
|
shelfHandler.save();
|
||||||
|
BooksWithoutBorders.sendSuccessMessage(commandSender, "Lore successfully saved");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
if (arguments.length == 1) {
|
||||||
|
return TabCompletionHelper.filterMatchingStartsWith(List.of("delete", "name", "lore"), arguments[0]);
|
||||||
|
} else if (arguments.length == 2) {
|
||||||
|
return switch (arguments[0].toLowerCase()) {
|
||||||
|
case "delete" -> new ArrayList<>();
|
||||||
|
case "name" ->
|
||||||
|
TabCompletionHelper.filterMatchingStartsWith(List.of("Epic Title", "Lame Title"), arguments[1]);
|
||||||
|
case "lore" ->
|
||||||
|
TabCompletionHelper.filterMatchingStartsWith(List.of("Interesting lore", "Line1~Line2~Line3"), arguments[1]);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,74 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.TabExecutor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command executor for the set generation command
|
||||||
|
*/
|
||||||
|
public class CommandSetGeneration implements TabExecutor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "This command can only be used by a player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InventoryHelper.notHoldingOneWrittenBookCheck(player, "You must be holding a written book to" +
|
||||||
|
" change its generation!", "You cannot change the generation of two books at once!")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments.length < 1) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "You must specify the new generation for your book!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BookMeta.Generation generation;
|
||||||
|
try {
|
||||||
|
generation = BookMeta.Generation.valueOf(arguments[0]);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Invalid book generation specified!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
|
BookMeta bookMeta = (BookMeta) heldBook.getItemMeta();
|
||||||
|
if (bookMeta != null) {
|
||||||
|
bookMeta.setGeneration(generation);
|
||||||
|
heldBook.setItemMeta(bookMeta);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get book metadata!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
|
if (arguments.length == 1) {
|
||||||
|
List<String> generations = new ArrayList<>();
|
||||||
|
for (BookMeta.Generation generation : BookMeta.Generation.values()) {
|
||||||
|
generations.add(generation.name());
|
||||||
|
}
|
||||||
|
return generations;
|
||||||
|
}
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
||||||
@@ -23,13 +24,14 @@ import java.util.List;
|
|||||||
public class CommandSetLore implements TabExecutor {
|
public class CommandSetLore 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[] arguments) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (arguments.length < 1) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Missing a command argument!");
|
BooksWithoutBorders.sendErrorMessage(player, "Missing a command argument!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -41,12 +43,11 @@ public class CommandSetLore implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Treat all arguments as lore input
|
//Treat all arguments as lore input
|
||||||
String rawLore = String.join(" ", args);
|
String rawLore = String.join(" ", arguments);
|
||||||
|
|
||||||
//Format lore
|
//Format lore
|
||||||
rawLore = ChatColor.translateAlternateColorCodes('&', rawLore);
|
rawLore = ColorHelper.translateColorCodes(rawLore, ColorConversion.RGB);
|
||||||
String[] loreParts = rawLore.split(BooksWithoutBordersConfig.getLoreSeparator());
|
List<String> newLore = Arrays.asList(rawLore.split(BooksWithoutBordersConfig.getLoreSeparator()));
|
||||||
List<String> newLore = new ArrayList<>(Arrays.asList(loreParts));
|
|
||||||
|
|
||||||
//Update lore
|
//Update lore
|
||||||
ItemMeta meta = heldItem.getItemMeta();
|
ItemMeta meta = heldItem.getItemMeta();
|
||||||
@@ -61,7 +62,8 @@ public class CommandSetLore implements TabExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
//TODO: Figure out if there is a better way to display that an argument is required
|
//TODO: Figure out if there is a better way to display that an argument is required
|
||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
options.add("<new lore>");
|
options.add("<new lore>");
|
||||||
|
@@ -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,13 +23,14 @@ 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[] arguments) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length < 1) {
|
if (arguments.length < 1) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
|
BooksWithoutBorders.sendErrorMessage(player, "Too few command arguments!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -39,8 +41,8 @@ public class CommandSetTitle implements TabExecutor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String title = String.join(" ", args);
|
String title = String.join(" ", arguments);
|
||||||
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[] arguments) {
|
||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
options.add("<new title>");
|
options.add("<new title>");
|
||||||
return options;
|
return options;
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
package net.knarcraft.bookswithoutborders.command;
|
package net.knarcraft.bookswithoutborders.command;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
import net.knarcraft.bookswithoutborders.state.ItemSlot;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@@ -10,10 +12,13 @@ import org.bukkit.command.TabExecutor;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.bukkit.inventory.meta.WritableBookMeta;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command executor for the unsign command
|
* Command executor for the unsign command
|
||||||
@@ -21,7 +26,8 @@ import java.util.List;
|
|||||||
public class CommandUnSign implements TabExecutor {
|
public class CommandUnSign 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[] arguments) {
|
||||||
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;
|
||||||
@@ -44,19 +50,49 @@ public class CommandUnSign implements TabExecutor {
|
|||||||
* @param player <p>The player holding the book</p>
|
* @param player <p>The player holding the book</p>
|
||||||
* @param mainHand <p>Whether the player is holding the book in its main hand or its off hand</p>
|
* @param mainHand <p>Whether the player is holding the book in its main hand or its off hand</p>
|
||||||
*/
|
*/
|
||||||
public void unSignHeldBook(Player player, boolean mainHand) {
|
public void unSignHeldBook(@NotNull Player player, boolean mainHand) {
|
||||||
//Get the old book
|
//Get the old book
|
||||||
BookMeta oldBook = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
BookMeta oldMetadata = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||||
|
if (oldMetadata == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, mainHand);
|
||||||
|
|
||||||
//UnSign the book
|
//Only allow the owner to un-sign the book
|
||||||
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK);
|
if (BooksWithoutBordersConfig.getAuthorOnlyUnsign() && !player.hasPermission("bookswithoutborders.bypassAuthorOnlyUnsign")) {
|
||||||
newBook.setItemMeta(oldBook);
|
if (BookHelper.isNotAuthor(player, Objects.requireNonNull(oldMetadata))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WritableBookMeta newMetadata = (BookMeta) BooksWithoutBorders.getItemFactory().getItemMeta(Material.WRITABLE_BOOK);
|
||||||
|
if (newMetadata == null) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to create writable book metadata");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a new unsigned book with the same data
|
||||||
|
ItemStack newBook = new ItemStack(Material.WRITABLE_BOOK, heldBook.getAmount());
|
||||||
|
if (oldMetadata.hasLore()) {
|
||||||
|
List<String> oldLore = oldMetadata.getLore();
|
||||||
|
if (oldLore != null) {
|
||||||
|
List<String> newLore = new ArrayList<>(oldLore);
|
||||||
|
newLore.remove(0);
|
||||||
|
newMetadata.setLore(newLore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newMetadata.setPages(oldMetadata.getPages());
|
||||||
|
newBook.setItemMeta(newMetadata);
|
||||||
|
|
||||||
|
// Give the player the new book
|
||||||
InventoryHelper.replaceHeldItem(player, newBook, mainHand);
|
InventoryHelper.replaceHeldItem(player, newBook, mainHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
@NotNull
|
||||||
|
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias,
|
||||||
|
@NotNull String[] arguments) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,11 +2,13 @@ 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;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ public class BooksWithoutBordersConfig {
|
|||||||
private static final ChatColor errorColor = ChatColor.RED;
|
private static final ChatColor errorColor = ChatColor.RED;
|
||||||
private static final ChatColor successColor = ChatColor.GREEN;
|
private static final ChatColor successColor = ChatColor.GREEN;
|
||||||
private static final ChatColor commandColor = ChatColor.YELLOW;
|
private static final ChatColor commandColor = ChatColor.YELLOW;
|
||||||
private static final String SLASH = System.getProperty("file.separator");
|
private static final String SLASH = FileSystems.getDefault().getSeparator();
|
||||||
private static boolean isInitialized;
|
private static boolean isInitialized;
|
||||||
public static String bookFolder;
|
public static String bookFolder;
|
||||||
|
|
||||||
@@ -34,16 +36,20 @@ public class BooksWithoutBordersConfig {
|
|||||||
private static Material bookPriceType = null;
|
private static Material bookPriceType = null;
|
||||||
private static double bookPriceQuantity;
|
private static double bookPriceQuantity;
|
||||||
private static boolean authorOnlyCopy;
|
private static boolean authorOnlyCopy;
|
||||||
|
private static boolean authorOnlyUnsign;
|
||||||
|
private static boolean authorOnlySave;
|
||||||
private static boolean useYml;
|
private static boolean useYml;
|
||||||
private static boolean adminDecrypt;
|
private static boolean adminDecrypt;
|
||||||
private static boolean formatBooks;
|
private static boolean formatBooks;
|
||||||
|
private static boolean changeGenerationOnCopy;
|
||||||
|
private static boolean enableBookshelfPeeking;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the books without borders settings class
|
* Initializes the books without borders settings class
|
||||||
*
|
*
|
||||||
* @param booksWithoutBorders <p>The books without borders object used for getting required data</p>
|
* @param booksWithoutBorders <p>The books without borders object used for getting required data</p>
|
||||||
*/
|
*/
|
||||||
public static void initialize(BooksWithoutBorders booksWithoutBorders) {
|
public static void initialize(@NotNull BooksWithoutBorders booksWithoutBorders) {
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
throw new IllegalArgumentException("Settings class initialized twice. This should not happen!");
|
throw new IllegalArgumentException("Settings class initialized twice. This should not happen!");
|
||||||
}
|
}
|
||||||
@@ -106,6 +112,33 @@ public class BooksWithoutBordersConfig {
|
|||||||
return authorOnlyCopy;
|
return authorOnlyCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether only the author of a book should be able to unsign it
|
||||||
|
*
|
||||||
|
* @return <p>Whether only the book author can unsign it</p>
|
||||||
|
*/
|
||||||
|
public static boolean getAuthorOnlyUnsign() {
|
||||||
|
return authorOnlyUnsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether a player can only save their own books with /savebook
|
||||||
|
*
|
||||||
|
* @return <p>Whether a player can only save their own books</p>
|
||||||
|
*/
|
||||||
|
public static boolean getAuthorOnlySave() {
|
||||||
|
return authorOnlySave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether 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
|
||||||
*
|
*
|
||||||
@@ -184,6 +217,15 @@ public class BooksWithoutBordersConfig {
|
|||||||
return bookDuplicateLimit;
|
return bookDuplicateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether books should change their generation during copy
|
||||||
|
*
|
||||||
|
* @return <p>True if books should change their generation</p>
|
||||||
|
*/
|
||||||
|
public static boolean changeGenerationOnCopy() {
|
||||||
|
return changeGenerationOnCopy;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the separator used to split book title from book author
|
* Gets the separator used to split book title from book author
|
||||||
*
|
*
|
||||||
@@ -242,6 +284,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) {
|
||||||
@@ -257,10 +300,14 @@ public class BooksWithoutBordersConfig {
|
|||||||
config.set(ConfigOption.PRICE_QUANTITY.getConfigNode(), bookPriceQuantity);
|
config.set(ConfigOption.PRICE_QUANTITY.getConfigNode(), bookPriceQuantity);
|
||||||
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
config.set(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(), adminDecrypt);
|
||||||
config.set(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(), authorOnlyCopy);
|
config.set(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(), authorOnlyCopy);
|
||||||
|
config.set(ConfigOption.AUTHOR_ONLY_UNSIGN.getConfigNode(), authorOnlyUnsign);
|
||||||
|
config.set(ConfigOption.AUTHOR_ONLY_SAVE.getConfigNode(), authorOnlySave);
|
||||||
|
config.set(ConfigOption.CHANGE_GENERATION_ON_COPY.getConfigNode(), changeGenerationOnCopy);
|
||||||
|
|
||||||
//Handles old book and quill settings
|
//Handles old book and quill settings
|
||||||
if (config.contains("Options.Require_book_and_quill_to_create_book")) {
|
if (config.contains("Options.Require_book_and_quill_to_create_book")) {
|
||||||
sendSuccessMessage(consoleSender, "[BooksWithoutBorders] Found old config setting \"Require_book_and_quill_to_create_book\"");
|
sendSuccessMessage(consoleSender, "[BooksWithoutBorders] Found old config setting " +
|
||||||
|
"\"Require_book_and_quill_to_create_book\"");
|
||||||
sendSuccessMessage(consoleSender, "Updating to \"Price_to_create_book\" settings");
|
sendSuccessMessage(consoleSender, "Updating to \"Price_to_create_book\" settings");
|
||||||
|
|
||||||
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
if (config.getBoolean("Options.Require_book_and_quill_to_create_book")) {
|
||||||
@@ -284,32 +331,29 @@ public class BooksWithoutBordersConfig {
|
|||||||
BooksWithoutBorders.getInstance().reloadConfig();
|
BooksWithoutBorders.getInstance().reloadConfig();
|
||||||
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
Configuration config = BooksWithoutBorders.getInstance().getConfig();
|
||||||
try {
|
try {
|
||||||
useYml = config.getBoolean(ConfigOption.USE_YAML.getConfigNode(),
|
useYml = getBoolean(config, ConfigOption.USE_YAML);
|
||||||
(Boolean) ConfigOption.USE_YAML.getDefaultValue());
|
|
||||||
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
bookDuplicateLimit = config.getInt(ConfigOption.MAX_DUPLICATES.getConfigNode(),
|
||||||
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
(Integer) ConfigOption.MAX_DUPLICATES.getDefaultValue());
|
||||||
titleAuthorSeparator = config.getString(ConfigOption.TITLE_AUTHOR_SEPARATOR.getConfigNode(),
|
titleAuthorSeparator = getString(config, ConfigOption.TITLE_AUTHOR_SEPARATOR);
|
||||||
(String) ConfigOption.TITLE_AUTHOR_SEPARATOR.getDefaultValue());
|
loreSeparator = getString(config, ConfigOption.LORE_LINE_SEPARATOR);
|
||||||
loreSeparator = config.getString(ConfigOption.LORE_LINE_SEPARATOR.getConfigNode(),
|
adminDecrypt = getBoolean(config, ConfigOption.ADMIN_AUTO_DECRYPT);
|
||||||
(String) ConfigOption.LORE_LINE_SEPARATOR.getDefaultValue());
|
authorOnlyCopy = getBoolean(config, ConfigOption.AUTHOR_ONLY_COPY);
|
||||||
adminDecrypt = config.getBoolean(ConfigOption.ADMIN_AUTO_DECRYPT.getConfigNode(),
|
authorOnlyUnsign = getBoolean(config, ConfigOption.AUTHOR_ONLY_UNSIGN);
|
||||||
(Boolean) ConfigOption.ADMIN_AUTO_DECRYPT.getDefaultValue());
|
authorOnlySave = getBoolean(config, ConfigOption.AUTHOR_ONLY_SAVE);
|
||||||
authorOnlyCopy = config.getBoolean(ConfigOption.AUTHOR_ONLY_COPY.getConfigNode(),
|
|
||||||
(Boolean) ConfigOption.AUTHOR_ONLY_COPY.getDefaultValue());
|
|
||||||
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
firstBooks = config.getStringList(ConfigOption.BOOKS_FOR_NEW_PLAYERS.getConfigNode());
|
||||||
welcomeMessage = config.getString(ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getConfigNode(),
|
welcomeMessage = getString(config, ConfigOption.MESSAGE_FOR_NEW_PLAYERS);
|
||||||
(String) ConfigOption.MESSAGE_FOR_NEW_PLAYERS.getDefaultValue());
|
formatBooks = getBoolean(config, ConfigOption.FORMAT_AFTER_SIGNING);
|
||||||
formatBooks = config.getBoolean(ConfigOption.FORMAT_AFTER_SIGNING.getConfigNode(),
|
changeGenerationOnCopy = getBoolean(config, ConfigOption.CHANGE_GENERATION_ON_COPY);
|
||||||
(Boolean) ConfigOption.FORMAT_AFTER_SIGNING.getDefaultValue());
|
enableBookshelfPeeking = getBoolean(config, ConfigOption.ENABLE_BOOKSHELF_PEEKING);
|
||||||
|
|
||||||
//Convert string into material
|
//Convert string into material
|
||||||
String paymentMaterial = config.getString(ConfigOption.PRICE_ITEM_TYPE.getConfigNode(),
|
String paymentMaterial = getString(config, ConfigOption.PRICE_ITEM_TYPE);
|
||||||
(String) ConfigOption.PRICE_ITEM_TYPE.getDefaultValue());
|
|
||||||
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
if (paymentMaterial.equalsIgnoreCase("Economy")) {
|
||||||
if (EconomyHelper.setupEconomy()) {
|
if (EconomyHelper.setupEconomy()) {
|
||||||
bookPriceType = Material.AIR;
|
bookPriceType = Material.AIR;
|
||||||
} else {
|
} else {
|
||||||
sendErrorMessage(consoleSender, "BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
sendErrorMessage(consoleSender,
|
||||||
|
"BooksWithoutBorders failed to hook into Vault! Book price not set!");
|
||||||
bookPriceType = null;
|
bookPriceType = null;
|
||||||
}
|
}
|
||||||
} else if (!paymentMaterial.trim().isEmpty()) {
|
} else if (!paymentMaterial.trim().isEmpty()) {
|
||||||
@@ -338,4 +382,26 @@ public class BooksWithoutBordersConfig {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string value of the given config option
|
||||||
|
*
|
||||||
|
* @param config <p>The configuration to read from</p>
|
||||||
|
* @param configOption <p>The configuration option to get the value for</p>
|
||||||
|
* @return <p>The value of the option</p>
|
||||||
|
*/
|
||||||
|
private static String getString(@NotNull Configuration config, @NotNull ConfigOption configOption) {
|
||||||
|
return config.getString(configOption.getConfigNode(), (String) configOption.getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the boolean value of the given config option
|
||||||
|
*
|
||||||
|
* @param config <p>The configuration to read from</p>
|
||||||
|
* @param configOption <p>The configuration option to get the value for</p>
|
||||||
|
* @return <p>The value of the option</p>
|
||||||
|
*/
|
||||||
|
private static boolean getBoolean(@NotNull Configuration config, @NotNull ConfigOption configOption) {
|
||||||
|
return config.getBoolean(configOption.getConfigNode(), (Boolean) configOption.getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders.config;
|
package net.knarcraft.bookswithoutborders.config;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of the different available config options
|
* A representation of the different available config options
|
||||||
*/
|
*/
|
||||||
@@ -55,10 +57,31 @@ public enum ConfigOption {
|
|||||||
*/
|
*/
|
||||||
AUTHOR_ONLY_COPY("Options.Author_Only_Copy", false),
|
AUTHOR_ONLY_COPY("Options.Author_Only_Copy", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether only the book author should be able to unsign a book
|
||||||
|
*/
|
||||||
|
AUTHOR_ONLY_UNSIGN("Options.Author_Only_Unsign", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a player can only save their own books with /savebook
|
||||||
|
*/
|
||||||
|
AUTHOR_ONLY_SAVE("Options.Author_Only_Save", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to turn Original into Copy when copying books
|
||||||
|
*/
|
||||||
|
CHANGE_GENERATION_ON_COPY("Options.Change_Generation_On_Copy", false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to automatically format every signed book
|
* Whether to automatically format every signed book
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
@@ -69,7 +92,7 @@ public enum ConfigOption {
|
|||||||
* @param configNode <p>The config node in the config file this option represents</p>
|
* @param configNode <p>The config node in the config file this option represents</p>
|
||||||
* @param defaultValue <p>The default value for this config option</p>
|
* @param defaultValue <p>The default value for this config option</p>
|
||||||
*/
|
*/
|
||||||
ConfigOption(String configNode, Object defaultValue) {
|
ConfigOption(@NotNull String configNode, @NotNull Object defaultValue) {
|
||||||
this.configNode = configNode;
|
this.configNode = configNode;
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.container;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a bookshelf with extra data used when displaying its contents
|
||||||
|
*/
|
||||||
|
public class Bookshelf {
|
||||||
|
|
||||||
|
private final @NotNull Location location;
|
||||||
|
private @NotNull String title;
|
||||||
|
private @NotNull List<String> lore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new bookshelf
|
||||||
|
*
|
||||||
|
* @param location <p>The location of the bookshelf</p>
|
||||||
|
* @param title <p>The title of the bookshelf</p>
|
||||||
|
* @param lore <p>The lore of the bookshelf</p>
|
||||||
|
*/
|
||||||
|
public Bookshelf(@NotNull Location location, @NotNull String title, @NotNull List<String> lore) {
|
||||||
|
this.location = location;
|
||||||
|
this.title = title;
|
||||||
|
this.lore = lore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the location of this bookshelf
|
||||||
|
*
|
||||||
|
* @return <p>The location of this bookshelf</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Location getLocation() {
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title of this bookshelf
|
||||||
|
*
|
||||||
|
* @return <p>The title of this bookshelf</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public String getTitle() {
|
||||||
|
return this.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the lore of this bookshelf
|
||||||
|
*
|
||||||
|
* @return <p>The lore of this bookshelf</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public List<String> getLore() {
|
||||||
|
return this.lore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the title of this bookshelf
|
||||||
|
*
|
||||||
|
* @param title <p>The new title</p>
|
||||||
|
*/
|
||||||
|
public void setTitle(@NotNull String title) {
|
||||||
|
if (title.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Bookshelves cannot have empty titles!");
|
||||||
|
}
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the lore of this bookshelf
|
||||||
|
*
|
||||||
|
* @param lore <p>The new lore</p>
|
||||||
|
*/
|
||||||
|
public void setLore(@NotNull List<String> lore) {
|
||||||
|
this.lore = lore;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,5 +1,9 @@
|
|||||||
package net.knarcraft.bookswithoutborders.encryption;
|
package net.knarcraft.bookswithoutborders.encryption;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
@@ -15,6 +19,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents and AES encryptor/decryptor
|
* This class represents and AES encryptor/decryptor
|
||||||
@@ -49,7 +54,8 @@ public class AES {
|
|||||||
* @param encrypt <p>Whether to encrypt or decrypt the input</p>
|
* @param encrypt <p>Whether to encrypt or decrypt the input</p>
|
||||||
* @return <p>The encrypted/decrypted input, or null if anything went wrong</p>
|
* @return <p>The encrypted/decrypted input, or null if anything went wrong</p>
|
||||||
*/
|
*/
|
||||||
public String encryptDecryptText(String input, String password, boolean encrypt) {
|
@Nullable
|
||||||
|
public String encryptDecryptText(@NotNull String input, @NotNull String password, boolean encrypt) {
|
||||||
//Make a key from the password
|
//Make a key from the password
|
||||||
SecretKeySpec secretKeySpec = getKeyFromPassword(password);
|
SecretKeySpec secretKeySpec = getKeyFromPassword(password);
|
||||||
//Get cipher instance
|
//Get cipher instance
|
||||||
@@ -66,16 +72,16 @@ public class AES {
|
|||||||
//Initialize cipher
|
//Initialize cipher
|
||||||
try {
|
try {
|
||||||
aes.init(mode, secretKeySpec, ivParameterSpec);
|
aes.init(mode, secretKeySpec, ivParameterSpec);
|
||||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
|
} catch (InvalidKeyException | InvalidAlgorithmParameterException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES input given!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//Perform encryption/decryption and output result
|
//Perform encryption/decryption and output result
|
||||||
try {
|
try {
|
||||||
byte[] output = aes.doFinal(getInputBytes(input, encrypt));
|
byte[] output = aes.doFinal(getInputBytes(input, encrypt));
|
||||||
return createResult(output, encrypt);
|
return createResult(output, encrypt);
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
} catch (IllegalBlockSizeException | BadPaddingException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES block size or padding");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +105,7 @@ public class AES {
|
|||||||
* @param encryption <p>Whether the input should be encrypted or decrypted</p>
|
* @param encryption <p>Whether the input should be encrypted or decrypted</p>
|
||||||
* @return <p>The input in byte format</p>
|
* @return <p>The input in byte format</p>
|
||||||
*/
|
*/
|
||||||
private byte[] getInputBytes(String input, boolean encryption) {
|
private byte[] getInputBytes(@NotNull String input, boolean encryption) {
|
||||||
if (encryption) {
|
if (encryption) {
|
||||||
return input.getBytes();
|
return input.getBytes();
|
||||||
} else {
|
} else {
|
||||||
@@ -114,6 +120,7 @@ public class AES {
|
|||||||
* @param encryption <p>Whether the output came from encryption or decryption</p>
|
* @param encryption <p>Whether the output came from encryption or decryption</p>
|
||||||
* @return <p>The output as a string</p>
|
* @return <p>The output as a string</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
private String createResult(byte[] output, boolean encryption) {
|
private String createResult(byte[] output, boolean encryption) {
|
||||||
if (encryption) {
|
if (encryption) {
|
||||||
return Base64.getEncoder().encodeToString(output);
|
return Base64.getEncoder().encodeToString(output);
|
||||||
@@ -127,12 +134,13 @@ public class AES {
|
|||||||
*
|
*
|
||||||
* @return <p>An AES cipher instance, or null if something went wrong</p>
|
* @return <p>An AES cipher instance, or null if something went wrong</p>
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
private Cipher getAESCipher() {
|
private Cipher getAESCipher() {
|
||||||
Cipher aes;
|
Cipher aes;
|
||||||
try {
|
try {
|
||||||
aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES algorithm or padding");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return aes;
|
return aes;
|
||||||
@@ -144,20 +152,21 @@ public class AES {
|
|||||||
* @param password <p>A user supplied password</p>
|
* @param password <p>A user supplied password</p>
|
||||||
* @return <p>A secret key spec or null if something went wrong</p>
|
* @return <p>A secret key spec or null if something went wrong</p>
|
||||||
*/
|
*/
|
||||||
private SecretKeySpec getKeyFromPassword(String password) {
|
@Nullable
|
||||||
|
private SecretKeySpec getKeyFromPassword(@NotNull String password) {
|
||||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), this.passwordSalt, 1000, 128);
|
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), this.passwordSalt, 1000, 128);
|
||||||
SecretKeyFactory keyFactory;
|
SecretKeyFactory keyFactory;
|
||||||
try {
|
try {
|
||||||
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES algorithm");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
SecretKey tmp;
|
SecretKey tmp;
|
||||||
try {
|
try {
|
||||||
tmp = keyFactory.generateSecret(spec);
|
tmp = keyFactory.generateSecret(spec);
|
||||||
} catch (InvalidKeySpecException e) {
|
} catch (InvalidKeySpecException exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Invalid AES key specification");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new SecretKeySpec(tmp.getEncoded(), "AES");
|
return new SecretKeySpec(tmp.getEncoded(), "AES");
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders.encryption;
|
package net.knarcraft.bookswithoutborders.encryption;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -24,7 +26,7 @@ public class GenenCrypt {
|
|||||||
*
|
*
|
||||||
* @param key <p>The key used to generate the codon table</p>
|
* @param key <p>The key used to generate the codon table</p>
|
||||||
*/
|
*/
|
||||||
public GenenCrypt(String key) {
|
public GenenCrypt(@NotNull String key) {
|
||||||
|
|
||||||
// define the initial, unshuffled codon list of 4 base codons
|
// define the initial, unshuffled codon list of 4 base codons
|
||||||
ArrayList<String> originalCodonList = new ArrayList<>();
|
ArrayList<String> originalCodonList = new ArrayList<>();
|
||||||
@@ -33,7 +35,7 @@ public class GenenCrypt {
|
|||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
for (int k = 0; k < 4; k++) {
|
for (int k = 0; k < 4; k++) {
|
||||||
for (int l = 0; l < 4; l++) {
|
for (int l = 0; l < 4; l++) {
|
||||||
originalCodonList.add("" + bases[i] + bases[j] + bases[k] + bases[l]);
|
originalCodonList.add(bases[i] + bases[j] + bases[k] + bases[l]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +51,7 @@ public class GenenCrypt {
|
|||||||
|
|
||||||
// use the random number generator and the originalCodonList to make a shuffled list
|
// use the random number generator and the originalCodonList to make a shuffled list
|
||||||
ArrayList<String> shuffledCodonList = new ArrayList<>();
|
ArrayList<String> shuffledCodonList = new ArrayList<>();
|
||||||
while (originalCodonList.size() > 0) {
|
while (!originalCodonList.isEmpty()) {
|
||||||
int index = ranGen.nextInt(originalCodonList.size());
|
int index = ranGen.nextInt(originalCodonList.size());
|
||||||
shuffledCodonList.add(originalCodonList.get(index));
|
shuffledCodonList.add(originalCodonList.get(index));
|
||||||
originalCodonList.remove(index);
|
originalCodonList.remove(index);
|
||||||
@@ -97,9 +99,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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,7 +116,8 @@ public class GenenCrypt {
|
|||||||
* @param input <p>The input to encrypt</p>
|
* @param input <p>The input to encrypt</p>
|
||||||
* @return <p>The encrypted input</p>
|
* @return <p>The encrypted input</p>
|
||||||
*/
|
*/
|
||||||
public String encrypt(String input) {
|
@NotNull
|
||||||
|
public String encrypt(@NotNull String input) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
for (int i = 0; i < input.length(); i++) {
|
for (int i = 0; i < input.length(); i++) {
|
||||||
// insert junk bases
|
// insert junk bases
|
||||||
@@ -144,7 +150,8 @@ public class GenenCrypt {
|
|||||||
* @param input <p>The input to decrypt</p>
|
* @param input <p>The input to decrypt</p>
|
||||||
* @return <p>The decrypted input</p>
|
* @return <p>The decrypted input</p>
|
||||||
*/
|
*/
|
||||||
public String decrypt(String input) {
|
@NotNull
|
||||||
|
public String decrypt(@NotNull String input) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
int keyCount = 0;
|
int keyCount = 0;
|
||||||
int junk = key.charAt(0);
|
int junk = key.charAt(0);
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders.encryption;
|
package net.knarcraft.bookswithoutborders.encryption;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
@@ -17,9 +19,10 @@ public class SubstitutionCipher {
|
|||||||
// using a string for the key, it is converted
|
// using a string for the key, it is converted
|
||||||
// a series of offsets that each character in the
|
// a series of offsets that each character in the
|
||||||
// original message is offset by
|
// original message is offset by
|
||||||
public String encrypt(String in, String key) {
|
@NotNull
|
||||||
|
public String encrypt(@NotNull String in, @NotNull String key) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
if (key != null && key.length() > 0) {
|
if (!key.isEmpty()) {
|
||||||
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
||||||
// converts each number in the key to an integer and adds to an array
|
// converts each number in the key to an integer and adds to an array
|
||||||
int[] offsetArray = new int[tokenizer.countTokens()];
|
int[] offsetArray = new int[tokenizer.countTokens()];
|
||||||
@@ -52,9 +55,11 @@ 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.
|
||||||
public String decrypt(String in, String key) {
|
@SuppressWarnings("unused")
|
||||||
|
@NotNull
|
||||||
|
public String decrypt(@NotNull String in, @NotNull String key) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
if (key != null && key.length() > 0) {
|
if (!key.isEmpty()) {
|
||||||
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
StringTokenizer tokenizer = new StringTokenizer(key, ", "); // tokenizes the key
|
||||||
// converts each number in the key to an integer and adds to an array
|
// converts each number in the key to an integer and adds to an array
|
||||||
int[] offsetArray = new int[tokenizer.countTokens()];
|
int[] offsetArray = new int[tokenizer.countTokens()];
|
||||||
@@ -79,7 +84,9 @@ 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.
|
||||||
public String oneTimePad(String in, String key) {
|
@SuppressWarnings("unused")
|
||||||
|
@NotNull
|
||||||
|
public String oneTimePad(@NotNull String in, @NotNull 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++) {
|
||||||
output.append((char) (in.charAt(i) ^ key.charAt(i % key.length())));
|
output.append((char) (in.charAt(i) ^ key.charAt(i % key.length())));
|
||||||
|
@@ -0,0 +1,87 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.gui;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AuthorBookIndex extends BookIndex {
|
||||||
|
|
||||||
|
private final static int booksPerPage = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the available books
|
||||||
|
*
|
||||||
|
* @param sender <p>The sender to display the books to</p>
|
||||||
|
* @param listPublic <p>Whether to display public books</p>
|
||||||
|
* @param command <p>The base command causing this to be called</p>
|
||||||
|
* @param page <p>The page of the book list to display</p>
|
||||||
|
*/
|
||||||
|
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page,
|
||||||
|
@NotNull String authorName) {
|
||||||
|
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
||||||
|
availableBooks.removeIf((bookPath) -> !BookFormatter.stripColor(bookPath.substring(0, bookPath.length() - 4).split(",")[1]).equalsIgnoreCase(authorName));
|
||||||
|
|
||||||
|
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
|
||||||
|
if (page > totalPages) {
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "No such page");
|
||||||
|
} else {
|
||||||
|
showAuthorBooks(sender, command, page, totalPages, availableBooks, authorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a menu listing available books from an author
|
||||||
|
*
|
||||||
|
* @param sender <p>The sender wanting to see the book menu</p>
|
||||||
|
* @param command <p>The main command used to trigger display of the book menu</p>
|
||||||
|
* @param page <p>The currently selected page</p>
|
||||||
|
* @param totalPages <p>The total amount of pages</p>
|
||||||
|
* @param availableBooks <p>All books available to the sender</p>
|
||||||
|
* @param authorName <p>The name of the author currently shown</p>
|
||||||
|
*/
|
||||||
|
private static void showAuthorBooks(@NotNull CommandSender sender, @NotNull String command, int page,
|
||||||
|
int totalPages, @NotNull List<String> availableBooks, @NotNull String authorName) {
|
||||||
|
ComponentBuilder componentBuilder = new ComponentBuilder();
|
||||||
|
|
||||||
|
// Display the list of books, with the next and previous buttons
|
||||||
|
displayPreviousButton(componentBuilder, command + " author" + authorName, page);
|
||||||
|
componentBuilder.append("\n");
|
||||||
|
displayBookList(componentBuilder, command, page, availableBooks);
|
||||||
|
displayNextButton(componentBuilder, command + " author" + authorName, page, totalPages);
|
||||||
|
|
||||||
|
// Display total pages and the manual change page command suggestion
|
||||||
|
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
displayTotalPages(componentBuilder, page, totalPages);
|
||||||
|
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
sender.spigot().sendMessage(componentBuilder.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the list of books on the current page
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
* @param availableBooks <p>All available books</p>
|
||||||
|
*/
|
||||||
|
protected static void displayBookList(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page,
|
||||||
|
@NotNull List<String> availableBooks) {
|
||||||
|
int startIndex = (page - 1) * booksPerPage;
|
||||||
|
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
|
||||||
|
componentBuilder.append(getNiceName(availableBooks.get(bookIndex))).color(ChatColor.WHITE).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
|
||||||
|
availableBooks.get(bookIndex))).event(
|
||||||
|
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path")));
|
||||||
|
componentBuilder.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,163 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.gui;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class BookIndex {
|
||||||
|
|
||||||
|
protected final static int booksPerPage = 10;
|
||||||
|
protected final static @NotNull ChatColor interactColor = ChatColor.of("#FFD700");
|
||||||
|
protected final static @NotNull ChatColor inactiveColor = ChatColor.of("#999999");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the correct GUI, if specified in the given arguments
|
||||||
|
*
|
||||||
|
* @param arguments <p>The arguments given by a command sender</p>
|
||||||
|
* @param sender <p>The sender executing the command</p>
|
||||||
|
* @param selectPublic <p>Whether to display public books, or only those available to the command sender</p>
|
||||||
|
* @param command <p>The command used for changing pages and making the final selection</p>
|
||||||
|
* @return <p>True if the GUI was displayed</p>
|
||||||
|
*/
|
||||||
|
public static boolean displayPage(@NotNull String[] arguments, @NotNull CommandSender sender, boolean selectPublic,
|
||||||
|
@NotNull String command) {
|
||||||
|
if (arguments.length == 0) {
|
||||||
|
PagedBookIndex.printBooks(sender, selectPublic, command, 1);
|
||||||
|
return true;
|
||||||
|
} else if (arguments.length == 1) {
|
||||||
|
int page = InputCleaningHelper.parsePageNumber(arguments[0]);
|
||||||
|
if (page > 0) {
|
||||||
|
PagedBookIndex.printBooks(sender, selectPublic, command, page);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (arguments.length == 2) {
|
||||||
|
String author = InputCleaningHelper.parseAuthorSpecifier(arguments[0]);
|
||||||
|
if (author != null) {
|
||||||
|
int page = InputCleaningHelper.parsePageNumber(arguments[1]);
|
||||||
|
if (page > 0) {
|
||||||
|
AuthorBookIndex.printBooks(sender, selectPublic, command, page, author);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the suggestion for manually going to any page
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
*/
|
||||||
|
protected static void displayPageCommand(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page) {
|
||||||
|
componentBuilder.append("[Page Command]", ComponentBuilder.FormatRetention.NONE).color(interactColor).event(new HoverEvent(
|
||||||
|
HoverEvent.Action.SHOW_TEXT, new Text("/" + command + " page" + page))).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " page" + page));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the current page and total amount of pages
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
* @param totalPages <p>The total amount of pages</p>
|
||||||
|
*/
|
||||||
|
protected static void displayTotalPages(@NotNull ComponentBuilder componentBuilder, int page, int totalPages) {
|
||||||
|
componentBuilder.append("Page " + page + " of " + totalPages,
|
||||||
|
ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the alphabet-based page index
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param firstInstances <p>The map of where the first index of a letter is found</p>
|
||||||
|
*/
|
||||||
|
protected static void displayAlphabetIndex(@NotNull ComponentBuilder componentBuilder,
|
||||||
|
@NotNull String command, @NotNull Map<Character, Integer> firstInstances) {
|
||||||
|
componentBuilder.append("[index] <", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
for (int characterIndex = 0; characterIndex <= 25; characterIndex++) {
|
||||||
|
char character = (char) ('a' + characterIndex);
|
||||||
|
if (firstInstances.containsKey(character)) {
|
||||||
|
int pageIndex = (firstInstances.get(character) / booksPerPage) + 1;
|
||||||
|
componentBuilder.append(character + "").color(interactColor).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.RUN_COMMAND,
|
||||||
|
"/" + command + " page" + pageIndex)).event(new HoverEvent(
|
||||||
|
HoverEvent.Action.SHOW_TEXT, new Text("Books starting with " + character)));
|
||||||
|
} else {
|
||||||
|
componentBuilder.append(character + "", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentBuilder.append(">", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the previous page button
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
*/
|
||||||
|
protected static void displayPreviousButton(@NotNull ComponentBuilder componentBuilder,
|
||||||
|
@NotNull String command, int page) {
|
||||||
|
if (page > 1) {
|
||||||
|
String fullCommand = "/" + command + " page" + (page - 1);
|
||||||
|
HoverEvent prevPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page - 1)));
|
||||||
|
ClickEvent prevPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
||||||
|
componentBuilder.append("Previous [<]", ComponentBuilder.FormatRetention.NONE).color(interactColor)
|
||||||
|
.event(prevPagePreview).event(prevPageClick);
|
||||||
|
} else {
|
||||||
|
componentBuilder.append("Previous [<]", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the next page button
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
* @param totalPages <p>The total amount of pages</p>
|
||||||
|
*/
|
||||||
|
protected static void displayNextButton(@NotNull ComponentBuilder componentBuilder,
|
||||||
|
@NotNull String command, int page, int totalPages) {
|
||||||
|
if (page < totalPages) {
|
||||||
|
String fullCommand = "/" + command + " page" + (page + 1);
|
||||||
|
HoverEvent nextPagePreview = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("To page " + (page + 1)));
|
||||||
|
ClickEvent nextPageClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, fullCommand);
|
||||||
|
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(interactColor)
|
||||||
|
.event(nextPagePreview).event(nextPageClick);
|
||||||
|
} else {
|
||||||
|
componentBuilder.append("Next [>]", ComponentBuilder.FormatRetention.NONE).color(inactiveColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a nice name from a book's path
|
||||||
|
*
|
||||||
|
* @param bookPath <p>The path of a book</p>
|
||||||
|
* @return <p>The prettified book name</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
protected static String getNiceName(@NotNull String bookPath) {
|
||||||
|
String separator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
bookPath = ChatColor.translateAlternateColorCodes('&', bookPath.substring(0, bookPath.length() - 4));
|
||||||
|
if (bookPath.contains(separator)) {
|
||||||
|
String[] parts = bookPath.split(separator);
|
||||||
|
return parts[0].replace("_", " ") + ChatColor.RESET + " by " + parts[1] + ChatColor.RESET;
|
||||||
|
} else {
|
||||||
|
return bookPath + ChatColor.RESET + " by Unknown" + ChatColor.RESET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,118 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.gui;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookFormatter;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for displaying a paged index of all available books
|
||||||
|
*/
|
||||||
|
public class PagedBookIndex extends BookIndex {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the available books
|
||||||
|
*
|
||||||
|
* @param sender <p>The sender to display the books to</p>
|
||||||
|
* @param listPublic <p>Whether to display public books</p>
|
||||||
|
* @param command <p>The base command causing this to be called</p>
|
||||||
|
* @param page <p>The page of the book list to display</p>
|
||||||
|
*/
|
||||||
|
public static void printBooks(@NotNull CommandSender sender, boolean listPublic, @NotNull String command, int page) {
|
||||||
|
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
||||||
|
|
||||||
|
Map<Character, Integer> firstInstances;
|
||||||
|
if (listPublic) {
|
||||||
|
firstInstances = BooksWithoutBorders.getLetterIndex(null);
|
||||||
|
} else if (sender instanceof Player player) {
|
||||||
|
firstInstances = BooksWithoutBorders.getLetterIndex(player.getUniqueId());
|
||||||
|
} else {
|
||||||
|
firstInstances = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalPages = (int) Math.ceil((double) availableBooks.size() / booksPerPage);
|
||||||
|
if (page > totalPages) {
|
||||||
|
sender.sendMessage(ChatColor.GRAY + "No such page");
|
||||||
|
} else {
|
||||||
|
showBookMenu(sender, command, page, totalPages, availableBooks, firstInstances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a menu listing available books
|
||||||
|
*
|
||||||
|
* @param sender <p>The sender wanting to see the book menu</p>
|
||||||
|
* @param command <p>The main command used to trigger display of the book menu</p>
|
||||||
|
* @param page <p>The currently selected page</p>
|
||||||
|
* @param totalPages <p>The total amount of pages</p>
|
||||||
|
* @param availableBooks <p>All books available to the sender</p>
|
||||||
|
* @param firstInstances <p>The map between a character, and the index of the first instance of that character in the book list</p>
|
||||||
|
*/
|
||||||
|
private static void showBookMenu(@NotNull CommandSender sender, @NotNull String command, int page,
|
||||||
|
int totalPages, @NotNull List<String> availableBooks,
|
||||||
|
@NotNull Map<Character, Integer> firstInstances) {
|
||||||
|
ComponentBuilder componentBuilder = new ComponentBuilder();
|
||||||
|
|
||||||
|
// Display the list of books, with the next and previous buttons
|
||||||
|
displayPreviousButton(componentBuilder, command, page);
|
||||||
|
componentBuilder.append("\n");
|
||||||
|
displayBookList(componentBuilder, command, page, availableBooks);
|
||||||
|
displayNextButton(componentBuilder, command, page, totalPages);
|
||||||
|
|
||||||
|
// Display total pages and the manual change page command suggestion
|
||||||
|
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
displayTotalPages(componentBuilder, page, totalPages);
|
||||||
|
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
displayPageCommand(componentBuilder, command, page);
|
||||||
|
componentBuilder.append("\n");
|
||||||
|
|
||||||
|
// Display the alphabet index as the header
|
||||||
|
displayAlphabetIndex(componentBuilder, command, firstInstances);
|
||||||
|
sender.spigot().sendMessage(componentBuilder.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the list of books on the current page
|
||||||
|
*
|
||||||
|
* @param componentBuilder <p>The component builder to append to</p>
|
||||||
|
* @param command <p>The command used for switching pages</p>
|
||||||
|
* @param page <p>The current page</p>
|
||||||
|
* @param availableBooks <p>All available books</p>
|
||||||
|
*/
|
||||||
|
protected static void displayBookList(@NotNull ComponentBuilder componentBuilder, @NotNull String command, int page,
|
||||||
|
@NotNull List<String> availableBooks) {
|
||||||
|
int startIndex = (page - 1) * booksPerPage;
|
||||||
|
for (int bookIndex = startIndex; bookIndex < Math.min(startIndex + booksPerPage, availableBooks.size()); bookIndex++) {
|
||||||
|
componentBuilder.append("[" + (bookIndex + 1) + "]").color(interactColor).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
|
||||||
|
(bookIndex + 1))).event(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
|
new Text("Select book by index")));
|
||||||
|
|
||||||
|
componentBuilder.append(" ", ComponentBuilder.FormatRetention.NONE);
|
||||||
|
|
||||||
|
String[] parts = getNiceName(availableBooks.get(bookIndex)).split(" by ");
|
||||||
|
componentBuilder.append(parts[0]).color(ChatColor.WHITE).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " +
|
||||||
|
availableBooks.get(bookIndex))).event(
|
||||||
|
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Select book by path")));
|
||||||
|
componentBuilder.append(" by ", ComponentBuilder.FormatRetention.NONE).color(ChatColor.WHITE);
|
||||||
|
componentBuilder.append(parts[1]).color(ChatColor.WHITE).event(
|
||||||
|
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/" + command + " author" +
|
||||||
|
BookFormatter.stripColor(parts[1]) + " page1")).event(
|
||||||
|
new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Books by " +
|
||||||
|
BookFormatter.stripColor(parts[1]))));
|
||||||
|
componentBuilder.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,145 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.handler;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.container.Bookshelf;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handler keeping track of all bookshelves
|
||||||
|
*/
|
||||||
|
public class BookshelfHandler {
|
||||||
|
|
||||||
|
private static final File bookshelfFile = new File(BooksWithoutBorders.getInstance().getDataFolder(),
|
||||||
|
"bookshelves.yml");
|
||||||
|
private Set<Bookshelf> bookshelves;
|
||||||
|
private Map<Location, Bookshelf> locationLookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a bookshelf from the given location
|
||||||
|
*
|
||||||
|
* @param location <p>The location of the bookshelf</p>
|
||||||
|
* @return <p>The bookshelf at the location, or null if no such bookshelf exists</p>
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Bookshelf getFromLocation(@NotNull Location location) {
|
||||||
|
return locationLookup.get(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given bookshelf to this handler
|
||||||
|
*
|
||||||
|
* @param bookshelf <p>The bookshelf to register</p>
|
||||||
|
*/
|
||||||
|
public void registerBookshelf(@NotNull Bookshelf bookshelf) {
|
||||||
|
this.bookshelves.add(bookshelf);
|
||||||
|
this.locationLookup.put(bookshelf.getLocation(), bookshelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the given bookshelf from this handler
|
||||||
|
*
|
||||||
|
* @param bookshelf <p>The bookshelf to unregister</p>
|
||||||
|
*/
|
||||||
|
public void unregisterBookshelf(@NotNull Bookshelf bookshelf) {
|
||||||
|
this.locationLookup.remove(bookshelf.getLocation());
|
||||||
|
this.bookshelves.remove(bookshelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all stored bookshelves
|
||||||
|
*/
|
||||||
|
public void load() {
|
||||||
|
this.bookshelves = new HashSet<>();
|
||||||
|
this.locationLookup = new HashMap<>();
|
||||||
|
|
||||||
|
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(bookshelfFile);
|
||||||
|
ConfigurationSection bookshelfSection = configuration.getConfigurationSection("bookshelves");
|
||||||
|
if (bookshelfSection == null) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.INFO,
|
||||||
|
"BooksWithoutBorders found no bookshelves to load");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : bookshelfSection.getKeys(false)) {
|
||||||
|
String[] locationInfo = key.split(",");
|
||||||
|
World world = Bukkit.getWorld(UUID.fromString(locationInfo[0]));
|
||||||
|
double x = Integer.parseInt(locationInfo[1]);
|
||||||
|
double y = Integer.parseInt(locationInfo[2]);
|
||||||
|
double z = Integer.parseInt(locationInfo[3]);
|
||||||
|
Location bookshelfLocation = new Location(world, x, y, z);
|
||||||
|
|
||||||
|
String titleKey = key + ".title";
|
||||||
|
String loreKey = key + ".lore";
|
||||||
|
|
||||||
|
String title = bookshelfSection.getString(titleKey, null);
|
||||||
|
List<String> loreStrings = new ArrayList<>();
|
||||||
|
List<?> lore = bookshelfSection.getList(loreKey);
|
||||||
|
if (lore == null) {
|
||||||
|
throw new IllegalArgumentException("Lore is missing from bookshelf data!");
|
||||||
|
}
|
||||||
|
lore.forEach((item) -> {
|
||||||
|
if (item instanceof String) {
|
||||||
|
loreStrings.add((String) item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
registerBookshelf(new Bookshelf(bookshelfLocation, title, loreStrings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves all current bookshelves
|
||||||
|
*/
|
||||||
|
public void save() {
|
||||||
|
try {
|
||||||
|
YamlConfiguration configuration = new YamlConfiguration();
|
||||||
|
ConfigurationSection bookshelfSection = configuration.createSection("bookshelves");
|
||||||
|
for (Bookshelf bookshelf : bookshelves) {
|
||||||
|
saveBookshelf(bookshelfSection, bookshelf);
|
||||||
|
}
|
||||||
|
configuration.save(bookshelfFile);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save bookshelves!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a bookshelf to the given configuration section
|
||||||
|
*
|
||||||
|
* @param section <p>The configuration section to save to</p>
|
||||||
|
* @param bookshelf <p>The bookshelf to save</p>
|
||||||
|
*/
|
||||||
|
private void saveBookshelf(@NotNull ConfigurationSection section, @NotNull Bookshelf bookshelf) {
|
||||||
|
Location location = bookshelf.getLocation();
|
||||||
|
if (location.getWorld() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = location.getWorld().getUID() + "," + location.getBlockX() + "," + location.getBlockY() +
|
||||||
|
"," + location.getBlockZ();
|
||||||
|
String titleKey = key + ".title";
|
||||||
|
String loreKey = key + ".lore";
|
||||||
|
section.set(titleKey, bookshelf.getTitle());
|
||||||
|
section.set(loreKey, bookshelf.getLore());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import net.knarcraft.bookswithoutborders.utility.BookFormatter;
|
|||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerEditBookEvent;
|
import org.bukkit.event.player.PlayerEditBookEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for listening to book events
|
* A listener for listening to book events
|
||||||
@@ -14,7 +15,7 @@ import org.bukkit.event.player.PlayerEditBookEvent;
|
|||||||
public class BookEventListener implements Listener {
|
public class BookEventListener implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBookSign(PlayerEditBookEvent event) {
|
public void onBookSign(@NotNull PlayerEditBookEvent event) {
|
||||||
if (event.isCancelled() || !event.isSigning() || !BooksWithoutBordersConfig.formatBooks()) {
|
if (event.isCancelled() || !event.isSigning() || !BooksWithoutBordersConfig.formatBooks()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,212 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.listener;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.container.Bookshelf;
|
||||||
|
import net.knarcraft.bookswithoutborders.handler.BookshelfHandler;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.IntegerToRomanConverter;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
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.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
|
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener for bookshelf clicking
|
||||||
|
*/
|
||||||
|
public class BookshelfListener implements Listener {
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
|
public void onBookshelfBreak(@NotNull BlockBreakEvent event) {
|
||||||
|
Block block = event.getBlock();
|
||||||
|
|
||||||
|
if (block.getType() != Material.CHISELED_BOOKSHELF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BookshelfHandler bookshelfHandler = BooksWithoutBorders.getBookshelfHandler();
|
||||||
|
Bookshelf bookshelf = bookshelfHandler.getFromLocation(block.getLocation());
|
||||||
|
if (bookshelf != null) {
|
||||||
|
bookshelfHandler.unregisterBookshelf(bookshelf);
|
||||||
|
bookshelfHandler.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBookshelfClick(@NotNull PlayerInteractEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
// If left-clicking a chiseled bookshelf and sneaking, display contents
|
||||||
|
if (!event.hasBlock() || event.getClickedBlock() == null ||
|
||||||
|
!(event.getClickedBlock().getState() instanceof ChiseledBookshelf chiseledBookshelf) ||
|
||||||
|
event.getAction() != Action.LEFT_CLICK_BLOCK || !player.isSneaking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if bookshelf peeking is enabled, and the player can peek
|
||||||
|
if (!BooksWithoutBordersConfig.getEnableBookshelfPeeking() ||
|
||||||
|
!event.getPlayer().hasPermission("bookswithoutborders.peekbookshelf")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setUseInteractedBlock(Event.Result.DENY);
|
||||||
|
event.setUseItemInHand(Event.Result.DENY);
|
||||||
|
|
||||||
|
ChiseledBookshelfInventory bookshelfInventory = chiseledBookshelf.getInventory();
|
||||||
|
player.sendMessage(getBookshelfDescription(bookshelfInventory, event.getClickedBlock().getLocation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description for a bookshelf's contents
|
||||||
|
*
|
||||||
|
* @param bookshelfInventory <p>The inventory of the bookshelf to describe</p>
|
||||||
|
* @param location <p>The location of the clicked bookshelf</p>
|
||||||
|
* @return <p>A textual description of the bookshelf's contents</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getBookshelfDescription(@NotNull ChiseledBookshelfInventory bookshelfInventory, @NotNull Location location) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
Bookshelf bookshelf = BooksWithoutBorders.getBookshelfHandler().getFromLocation(location);
|
||||||
|
if (bookshelf != null) {
|
||||||
|
builder.append(ChatColor.of("#FF5700")).append("Books in ").append(bookshelf.getTitle())
|
||||||
|
.append(":").append(ChatColor.RESET);
|
||||||
|
for (String lore : bookshelf.getLore()) {
|
||||||
|
builder.append("\n ").append(ChatColor.LIGHT_PURPLE).append(lore);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.append(ChatColor.of("#FF5700")).append("Books in shelf:").append(ChatColor.RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < bookshelfInventory.getSize(); i++) {
|
||||||
|
int index = (i % 3) + 1;
|
||||||
|
if (i % 3 == 0) {
|
||||||
|
builder.append("\n ").append(ChatColor.of("#FF5700")).append(
|
||||||
|
i < 3 ? "Top Row:" : "Bottom Row:").append(ChatColor.RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("\n ").append(ChatColor.of("#ffd700")).append(index).append(". ").append(ChatColor.RESET);
|
||||||
|
|
||||||
|
ItemStack itemStack = bookshelfInventory.getItem(i);
|
||||||
|
if (itemStack == null) {
|
||||||
|
builder.append(ChatColor.GRAY).append("<empty>");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ItemMeta meta = itemStack.getItemMeta();
|
||||||
|
|
||||||
|
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(ChatColor.of("#A5682A")).append("[P]").append(ChatColor.RESET).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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getPlainBookDescription(@NotNull 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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getBookDescription(@NotNull 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 ChatColor.of("#686868") + "[Q]" + ChatColor.RESET + title + ChatColor.RESET + " 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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getEnchantedBookDescription(@NotNull EnchantmentStorageMeta enchantmentStorageMeta) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(ChatColor.of("#A64CFF")).append("[E]").append(ChatColor.RESET);
|
||||||
|
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));
|
||||||
|
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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getEnchantmentName(@NotNull Enchantment enchantment) {
|
||||||
|
// Note: While depreciated, changing this is incompatible with Paper
|
||||||
|
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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String uppercaseFirst(@NotNull 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -2,6 +2,8 @@ package net.knarcraft.bookswithoutborders.listener;
|
|||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
|
import net.knarcraft.bookswithoutborders.utility.BookHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
import net.knarcraft.bookswithoutborders.utility.BookLoader;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
import net.knarcraft.bookswithoutborders.utility.InputCleaningHelper;
|
||||||
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
import net.knarcraft.bookswithoutborders.utility.InventoryHelper;
|
||||||
@@ -15,22 +17,21 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.logging.Level;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener for listening to player-related events such as joining or holding a book
|
* A listener for listening to player-related events such as joining or holding a book
|
||||||
*/
|
*/
|
||||||
public class PlayerEventListener implements Listener {
|
public class PlayerEventListener implements Listener {
|
||||||
|
|
||||||
private final String slash = getSlash();
|
|
||||||
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
private final BooksWithoutBorders booksWithoutBorders = BooksWithoutBorders.getInstance();
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onHold(PlayerItemHeldEvent event) {
|
public void onHold(@NotNull PlayerItemHeldEvent event) {
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,9 +56,19 @@ public class PlayerEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
|
//If a book directory exists with this player's name, move it to this player's UUID
|
||||||
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
|
File file = new File(bookFolder + InputCleaningHelper.cleanString(player.getName()));
|
||||||
|
if (file.exists()) {
|
||||||
|
if (!file.renameTo(new File(bookFolder + player.getUniqueId()))) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.WARNING, "Unable to migrate player book " +
|
||||||
|
"directory for player " + player.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Handle new players
|
//Handle new players
|
||||||
if (!player.hasPlayedBefore()) {
|
if (!player.hasPlayedBefore()) {
|
||||||
boolean sendMessage = true;
|
boolean sendMessage = true;
|
||||||
@@ -73,11 +84,15 @@ public class PlayerEventListener implements Listener {
|
|||||||
ItemStack offHandItem = InventoryHelper.getHeldItem(player, false);
|
ItemStack offHandItem = InventoryHelper.getHeldItem(player, false);
|
||||||
if (mainHandItem.getType() == Material.WRITTEN_BOOK) {
|
if (mainHandItem.getType() == Material.WRITTEN_BOOK) {
|
||||||
ItemMeta itemMetadata = mainHandItem.getItemMeta();
|
ItemMeta itemMetadata = mainHandItem.getItemMeta();
|
||||||
updateBookInHand(player, itemMetadata, true);
|
if (itemMetadata != null) {
|
||||||
|
updateBookInHand(player, itemMetadata, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (offHandItem.getType() == Material.WRITTEN_BOOK) {
|
if (offHandItem.getType() == Material.WRITTEN_BOOK) {
|
||||||
ItemMeta itemMetadata = offHandItem.getItemMeta();
|
ItemMeta itemMetadata = offHandItem.getItemMeta();
|
||||||
updateBookInHand(player, itemMetadata, false);
|
if (itemMetadata != null) {
|
||||||
|
updateBookInHand(player, itemMetadata, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +104,7 @@ public class PlayerEventListener implements Listener {
|
|||||||
* @param sendMessage <p>Whether to send a message to the joining player</p>
|
* @param sendMessage <p>Whether to send a message to the joining player</p>
|
||||||
* @return <p>True if a message has yet to be sent</p>
|
* @return <p>True if a message has yet to be sent</p>
|
||||||
*/
|
*/
|
||||||
private boolean giveBookToNewPlayer(String bookName, Player player, boolean sendMessage) {
|
private boolean giveBookToNewPlayer(@NotNull String bookName, @NotNull Player player, boolean sendMessage) {
|
||||||
if (!bookName.trim().isEmpty()) {
|
if (!bookName.trim().isEmpty()) {
|
||||||
|
|
||||||
//Give the book to the player if it exists
|
//Give the book to the player if it exists
|
||||||
@@ -116,7 +131,7 @@ public class PlayerEventListener implements Listener {
|
|||||||
* @param itemMetadata <p>Information about the held book</p>
|
* @param itemMetadata <p>Information about the held book</p>
|
||||||
* @param mainHand <p>Whether to update the book in the player's main hand</p>
|
* @param mainHand <p>Whether to update the book in the player's main hand</p>
|
||||||
*/
|
*/
|
||||||
private void updateBookInHand(Player player, ItemMeta itemMetadata, boolean mainHand) {
|
private void updateBookInHand(@NotNull Player player, @NotNull ItemMeta itemMetadata, boolean mainHand) {
|
||||||
PlayerInventory playerInventory = player.getInventory();
|
PlayerInventory playerInventory = player.getInventory();
|
||||||
ItemStack updatedBook = updateBook(player, (BookMeta) itemMetadata);
|
ItemStack updatedBook = updateBook(player, (BookMeta) itemMetadata);
|
||||||
if (updatedBook != null) {
|
if (updatedBook != null) {
|
||||||
@@ -135,7 +150,8 @@ public class PlayerEventListener implements Listener {
|
|||||||
* @param oldBook <p>Metadata about the held book</p>
|
* @param oldBook <p>Metadata about the held book</p>
|
||||||
* @return <p>An updated book</p>
|
* @return <p>An updated book</p>
|
||||||
*/
|
*/
|
||||||
public ItemStack updateBook(Player player, BookMeta oldBook) {
|
@Nullable
|
||||||
|
public ItemStack updateBook(@NotNull Player player, @NotNull BookMeta oldBook) {
|
||||||
//handles hacked title-less books
|
//handles hacked title-less books
|
||||||
if (oldBook.getTitle() == null || oldBook.getTitle().length() < 3) {
|
if (oldBook.getTitle() == null || oldBook.getTitle().length() < 3) {
|
||||||
return null;
|
return null;
|
||||||
@@ -151,13 +167,14 @@ public class PlayerEventListener implements Listener {
|
|||||||
fileName = oldBook.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + oldBook.getAuthor();
|
fileName = oldBook.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + oldBook.getAuthor();
|
||||||
}
|
}
|
||||||
|
|
||||||
String cleanPlayerName = InputCleaningHelper.cleanString(player.getName());
|
String playerFolderPath = BookHelper.getBookDirectoryPathString(BookDirectory.PLAYER, player);
|
||||||
|
String publicFolderPath = BookHelper.getBookDirectoryPathString(BookDirectory.PUBLIC, player);
|
||||||
|
|
||||||
String[] possiblePaths = new String[]{
|
String[] possiblePaths = new String[]{
|
||||||
getBookFolder() + fileName + ".yml",
|
publicFolderPath + fileName + ".yml",
|
||||||
getBookFolder() + fileName + ".txt",
|
publicFolderPath + fileName + ".txt",
|
||||||
getBookFolder() + cleanPlayerName + slash + fileName + ".yml",
|
playerFolderPath + fileName + ".yml",
|
||||||
getBookFolder() + cleanPlayerName + slash + fileName + ".txt"
|
playerFolderPath + fileName + ".txt"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String path : possiblePaths) {
|
for (String path : possiblePaths) {
|
||||||
|
@@ -3,15 +3,19 @@ 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.BookFormatter;
|
||||||
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;
|
||||||
@@ -21,12 +25,14 @@ import org.bukkit.inventory.EquipmentSlot;
|
|||||||
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.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
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
|
||||||
@@ -36,7 +42,7 @@ public class SignEventListener implements Listener {
|
|||||||
private final String slash = getSlash();
|
private final String slash = getSlash();
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onSignChange(SignChangeEvent event) {
|
public void onSignChange(@NotNull SignChangeEvent event) {
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -54,7 +60,7 @@ public class SignEventListener implements Listener {
|
|||||||
|
|
||||||
//Check if the sign is of a valid type
|
//Check if the sign is of a valid type
|
||||||
if (!((lines[1].equalsIgnoreCase("[Encrypt]") || lines[1].equalsIgnoreCase("[Decrypt]") ||
|
if (!((lines[1].equalsIgnoreCase("[Encrypt]") || lines[1].equalsIgnoreCase("[Decrypt]") ||
|
||||||
lines[1].equalsIgnoreCase("[Give]")) && lines[2].trim().length() > 0)) {
|
lines[1].equalsIgnoreCase("[Give]")) && !lines[2].trim().isEmpty())) {
|
||||||
//Mark the second line as invalid
|
//Mark the second line as invalid
|
||||||
event.setLine(1, ChatColor.DARK_RED + lines[1]);
|
event.setLine(1, ChatColor.DARK_RED + lines[1]);
|
||||||
return;
|
return;
|
||||||
@@ -76,7 +82,7 @@ public class SignEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onClick(PlayerInteractEvent event) {
|
public void onClick(@NotNull PlayerInteractEvent event) {
|
||||||
if (event.getClickedBlock() == null) {
|
if (event.getClickedBlock() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -89,24 +95,32 @@ public class SignEventListener implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ItemStack heldItem = playerInventory.getItem(hand);
|
ItemStack heldItem = playerInventory.getItem(hand);
|
||||||
|
if (heldItem == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Material heldItemType = heldItem.getType();
|
Material heldItemType = heldItem.getType();
|
||||||
|
|
||||||
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
|
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (Tag.SIGNS.isTagged(clickedBlockType) ||
|
||||||
Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
|
Tag.WALL_SIGNS.isTagged(clickedBlockType))) {
|
||||||
|
event.setUseItemInHand(Event.Result.DENY);
|
||||||
//The player right-clicked a sign
|
//The player right-clicked a sign
|
||||||
Sign sign = (Sign) event.getClickedBlock().getState();
|
Sign sign = (Sign) event.getClickedBlock().getState();
|
||||||
if (signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
if (!signLineEquals(sign, 0, "[BwB]", ChatColor.DARK_GREEN)) {
|
||||||
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)) {
|
||||||
@@ -127,13 +141,14 @@ 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>
|
||||||
* @param hand <p>The EquipmentSlot of the used hand</p>
|
* @param hand <p>The EquipmentSlot of the used hand</p>
|
||||||
*/
|
*/
|
||||||
private void decryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
|
private void decryptHeldBookUsingSign(@NotNull Sign sign, @NotNull Material heldItemType, @NotNull Player player,
|
||||||
|
@NotNull EquipmentSlot hand) {
|
||||||
//Decrypt the held book and replace it
|
//Decrypt the held book and replace it
|
||||||
if (heldItemType == Material.WRITTEN_BOOK) {
|
if (heldItemType == Material.WRITTEN_BOOK) {
|
||||||
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 = BookFormatter.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);
|
||||||
@@ -150,10 +165,11 @@ public class SignEventListener implements Listener {
|
|||||||
* @param sign <p>The sign to check</p>
|
* @param sign <p>The sign to check</p>
|
||||||
* @return <p>The color of the sign</p>
|
* @return <p>The color of the sign</p>
|
||||||
*/
|
*/
|
||||||
private ChatColor getSignLine2Color(Sign sign) {
|
@Nullable
|
||||||
String line = sign.getLine(2);
|
private ChatColor getSignLine2Color(@NotNull Sign sign) {
|
||||||
if (!ChatColor.stripColor(line).equals(line)) {
|
String line = sign.getSide(Side.FRONT).getLine(2);
|
||||||
return ChatColor.getByChar(sign.getLine(2).substring(1, 2));
|
if (!BookFormatter.stripColor(line).equals(line)) {
|
||||||
|
return ChatColor.getByChar(sign.getSide(Side.FRONT).getLine(2).substring(1, 2).charAt(0));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -168,8 +184,9 @@ public class SignEventListener implements Listener {
|
|||||||
* @param color <p>The color to match</p>
|
* @param color <p>The color to match</p>
|
||||||
* @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(@NotNull Sign sign, int lineNumber, @NotNull String compareTo,
|
||||||
String line = sign.getLine(lineNumber);
|
@NotNull ChatColor color) {
|
||||||
|
String line = sign.getSide(Side.FRONT).getLine(lineNumber);
|
||||||
return line.equalsIgnoreCase(color + compareTo);
|
return line.equalsIgnoreCase(color + compareTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,16 +197,18 @@ public class SignEventListener implements Listener {
|
|||||||
* @param lines <p>The lines on the sign</p>
|
* @param lines <p>The lines on the sign</p>
|
||||||
* @param player <p>The player which edited the sign</p>
|
* @param player <p>The player which edited the sign</p>
|
||||||
*/
|
*/
|
||||||
private void generateGiveSign(SignChangeEvent event, String[] lines, Player player) {
|
private void generateGiveSign(@NotNull SignChangeEvent event, @NotNull String[] lines,
|
||||||
|
@NotNull Player player) {
|
||||||
if (lines[2].length() > 13 || lines[3].length() > 13) {
|
if (lines[2].length() > 13 || lines[3].length() > 13) {
|
||||||
player.sendMessage(ChatColor.RED + "[Give] signs' 3rd and 4th lines must be 13 characters or less!");
|
BooksWithoutBorders.sendErrorMessage(player,
|
||||||
|
"[Give] signs' 3rd and 4th lines must be 13 characters or less!");
|
||||||
markGiveSignValidity(event, false);
|
markGiveSignValidity(event, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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 {
|
||||||
@@ -208,7 +227,7 @@ public class SignEventListener implements Listener {
|
|||||||
* @param event <p>The event causing the creation of the give sign</p>
|
* @param event <p>The event causing the creation of the give sign</p>
|
||||||
* @param isValid <p>Whether the created sign is valid</p>
|
* @param isValid <p>Whether the created sign is valid</p>
|
||||||
*/
|
*/
|
||||||
private void markGiveSignValidity(SignChangeEvent event, boolean isValid) {
|
private void markGiveSignValidity(@NotNull SignChangeEvent event, boolean isValid) {
|
||||||
String[] lines = event.getLines();
|
String[] lines = event.getLines();
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
event.setLine(2, ChatColor.DARK_GREEN + lines[2]);
|
event.setLine(2, ChatColor.DARK_GREEN + lines[2]);
|
||||||
@@ -227,7 +246,8 @@ public class SignEventListener implements Listener {
|
|||||||
* @param heldItem <p>The type of the held book</p>
|
* @param heldItem <p>The type of the held book</p>
|
||||||
* @param hand <p>The hand the player is using to hold the book</p>
|
* @param hand <p>The hand the player is using to hold the book</p>
|
||||||
*/
|
*/
|
||||||
private void decryptBook(BookMeta oldBook, Player player, ItemStack heldItem, EquipmentSlot hand) {
|
private void decryptBook(@NotNull BookMeta oldBook, @NotNull Player player, @NotNull ItemStack heldItem,
|
||||||
|
@NotNull EquipmentSlot hand) {
|
||||||
ItemStack newBook;
|
ItemStack newBook;
|
||||||
|
|
||||||
//Check if the book is encrypted by Books Without Borders
|
//Check if the book is encrypted by Books Without Borders
|
||||||
@@ -262,7 +282,7 @@ public class SignEventListener implements Listener {
|
|||||||
|
|
||||||
player.getInventory().setItem(hand, newBook);
|
player.getInventory().setItem(hand, newBook);
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
player.sendMessage(ChatColor.GREEN + "Book auto-decrypted!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Book auto-decrypted!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,14 +293,15 @@ 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>
|
||||||
* @param hand <p>The EquipmentSlot of the used hand</p>
|
* @param hand <p>The EquipmentSlot of the used hand</p>
|
||||||
*/
|
*/
|
||||||
private void encryptHeldBookUsingSign(Sign sign, Material heldItemType, Player player, EquipmentSlot hand) {
|
private void encryptHeldBookUsingSign(@NotNull Sign sign, @NotNull Material heldItemType, @NotNull Player player,
|
||||||
|
@NotNull 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();
|
||||||
eBook = EncryptionHelper.encryptBook(player, mainHand, ChatColor.stripColor(lines[2]),
|
eBook = EncryptionHelper.encryptBook(player, mainHand, BookFormatter.stripColor(lines[2]),
|
||||||
EncryptionStyle.getFromString(ChatColor.stripColor(lines[3])));
|
EncryptionStyle.getFromString(BookFormatter.stripColor(lines[3])));
|
||||||
if (eBook != null) {
|
if (eBook != null) {
|
||||||
player.getInventory().setItem(hand, eBook);
|
player.getInventory().setItem(hand, eBook);
|
||||||
}
|
}
|
||||||
@@ -293,8 +314,8 @@ public class SignEventListener implements Listener {
|
|||||||
* @param sign <p>The sign the user clicked</p>
|
* @param sign <p>The sign the user clicked</p>
|
||||||
* @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(@NotNull Sign sign, @NotNull Player player) {
|
||||||
String fileName = ChatColor.stripColor(sign.getLine(2));
|
String fileName = BookFormatter.stripColor(sign.getSide(Side.FRONT).getLine(2));
|
||||||
boolean isLoadListNumber = false;
|
boolean isLoadListNumber = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -304,18 +325,18 @@ 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 += BookFormatter.stripColor(thirdLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public");
|
ItemStack newBook = BookLoader.loadBook(player, fileName, "true", "public");
|
||||||
|
|
||||||
if (newBook != null) {
|
if (newBook != null) {
|
||||||
player.getInventory().addItem(newBook);
|
player.getInventory().addItem(newBook);
|
||||||
player.sendMessage(ChatColor.GREEN + "Received book!");
|
BooksWithoutBorders.sendSuccessMessage(player, "Received book!");
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(ChatColor.RED + "Book failed to load!");
|
BooksWithoutBorders.sendErrorMessage(player, "Book failed to load!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package net.knarcraft.bookswithoutborders.state;
|
package net.knarcraft.bookswithoutborders.state;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This enum represents the different directories books can be saved in
|
* This enum represents the different directories books can be saved in
|
||||||
*/
|
*/
|
||||||
@@ -26,7 +29,8 @@ public enum BookDirectory {
|
|||||||
* @param directory <p>The directory to transform</p>
|
* @param directory <p>The directory to transform</p>
|
||||||
* @return <p>A book directory, or null if the given directory is empty</p>
|
* @return <p>A book directory, or null if the given directory is empty</p>
|
||||||
*/
|
*/
|
||||||
public static BookDirectory getFromString(String directory) {
|
@Nullable
|
||||||
|
public static BookDirectory getFromString(@NotNull String directory) {
|
||||||
if (directory.equalsIgnoreCase("public")) {
|
if (directory.equalsIgnoreCase("public")) {
|
||||||
return BookDirectory.PUBLIC;
|
return BookDirectory.PUBLIC;
|
||||||
} else if (directory.equalsIgnoreCase("player")) {
|
} else if (directory.equalsIgnoreCase("player")) {
|
||||||
|
@@ -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,
|
||||||
|
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package net.knarcraft.bookswithoutborders.state;
|
package net.knarcraft.bookswithoutborders.state;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This enum represents the different available encryption styles
|
* This enum represents the different available encryption styles
|
||||||
*/
|
*/
|
||||||
@@ -10,7 +12,7 @@ public enum EncryptionStyle {
|
|||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
EncryptionStyle(String name) {
|
EncryptionStyle(@NotNull String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +22,8 @@ public enum EncryptionStyle {
|
|||||||
* @param name <p>The name of the encryption style</p>
|
* @param name <p>The name of the encryption style</p>
|
||||||
* @return <p>An encryption style or null if no match is found</p>
|
* @return <p>An encryption style or null if no match is found</p>
|
||||||
*/
|
*/
|
||||||
public static EncryptionStyle getFromString(String name) {
|
@NotNull
|
||||||
|
public static EncryptionStyle getFromString(@NotNull String name) {
|
||||||
for (EncryptionStyle style : EncryptionStyle.values()) {
|
for (EncryptionStyle style : EncryptionStyle.values()) {
|
||||||
if (style.name.equalsIgnoreCase(name)) {
|
if (style.name.equalsIgnoreCase(name)) {
|
||||||
return style;
|
return style;
|
||||||
|
@@ -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,
|
||||||
|
|
||||||
|
@@ -1,29 +1,29 @@
|
|||||||
package net.knarcraft.bookswithoutborders.utility;
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
import org.bukkit.ChatColor;
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
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.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getBookFolder;
|
||||||
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for dealing with files
|
* Helper class for dealing with files
|
||||||
*/
|
*/
|
||||||
public final class FileHelper {
|
public final class BookFileHelper {
|
||||||
|
|
||||||
private FileHelper() {
|
private BookFileHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +32,7 @@ public final class FileHelper {
|
|||||||
* @param possibleIndex <p>The string which might be a book index</p>
|
* @param possibleIndex <p>The string which might be a book index</p>
|
||||||
* @return <p>True if the number is a book index</p>
|
* @return <p>True if the number is a book index</p>
|
||||||
*/
|
*/
|
||||||
public static boolean isBookListIndex(String possibleIndex) {
|
public static boolean isBookListIndex(@NotNull String possibleIndex) {
|
||||||
File bookDirectory = new File(getBookFolder().replaceAll("[\\\\/]$", ""));
|
File bookDirectory = new File(getBookFolder().replaceAll("[\\\\/]$", ""));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -53,7 +53,7 @@ public final class FileHelper {
|
|||||||
* @param bookFile <p>The path to a book</p>
|
* @param bookFile <p>The path to a book</p>
|
||||||
* @return <p>True if the file exists and points to a book file</p>
|
* @return <p>True if the file exists and points to a book file</p>
|
||||||
*/
|
*/
|
||||||
public static boolean bookFileExists(String bookFile) {
|
public static boolean bookFileExists(@NotNull String bookFile) {
|
||||||
return ((new File(bookFile).isFile() && (bookFile.endsWith(".txt") ||
|
return ((new File(bookFile).isFile() && (bookFile.endsWith(".txt") ||
|
||||||
bookFile.endsWith(".yml"))) || new File(bookFile + ".txt").isFile() ||
|
bookFile.endsWith(".yml"))) || new File(bookFile + ".txt").isFile() ||
|
||||||
new File(bookFile + ".yml").isFile()) && !bookFile.contains("../") && !bookFile.contains("..\\");
|
new File(bookFile + ".yml").isFile()) && !bookFile.contains("../") && !bookFile.contains("..\\");
|
||||||
@@ -67,7 +67,8 @@ public final class FileHelper {
|
|||||||
* @param bookPath <p>The path of the book to get</p>
|
* @param bookPath <p>The path of the book to get</p>
|
||||||
* @return <p>The file the path points to, or null otherwise</p>
|
* @return <p>The file the path points to, or null otherwise</p>
|
||||||
*/
|
*/
|
||||||
public static File getBookFile(String bookPath) {
|
@Nullable
|
||||||
|
public static File getBookFile(@NotNull String bookPath) {
|
||||||
if (!bookFileExists(bookPath)) {
|
if (!bookFileExists(bookPath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -97,42 +98,33 @@ public final class FileHelper {
|
|||||||
* @param listPublic <p>Whether to list public or personal files</p>
|
* @param listPublic <p>Whether to list public or personal files</p>
|
||||||
* @return <p>A list of available files</p>
|
* @return <p>A list of available files</p>
|
||||||
*/
|
*/
|
||||||
public static List<String> listFiles(CommandSender sender, Boolean listPublic) {
|
@Nullable
|
||||||
File file;
|
public static List<String> listFiles(@NotNull CommandSender sender, @NotNull Boolean listPublic) {
|
||||||
if (listPublic) {
|
File file = BookHelper.getBookDirectoryPath(listPublic ? BookDirectory.PUBLIC : BookDirectory.PLAYER, sender);
|
||||||
file = new File(getBookFolder());
|
if (file == null) {
|
||||||
} else {
|
return new ArrayList<>();
|
||||||
file = new File(getBookFolder() + cleanString(sender.getName()) + getSlash());
|
|
||||||
}
|
}
|
||||||
return FileHelper.listFiles(sender, file);
|
return BookFileHelper.listFiles(sender, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the available books
|
* Gets a map between characters, and the first instance of a book's title starting with that character
|
||||||
*
|
*
|
||||||
* @param sender <p>The sender to display the books to</p>
|
* @param books <p>The books to look through</p>
|
||||||
* @param listPublic <p>Whether to display public books</p>
|
* @return <p>The map of the first index containing each character</p>
|
||||||
*/
|
*/
|
||||||
public static void printBooks(CommandSender sender, boolean listPublic) {
|
@NotNull
|
||||||
List<String> availableBooks = BooksWithoutBorders.getAvailableBooks(sender, listPublic);
|
public static Map<Character, Integer> populateLetterIndices(@NotNull List<String> books) {
|
||||||
FileHelper.printFiles(sender, availableBooks);
|
Map<Character, Integer> firstEncounter = new HashMap<>();
|
||||||
}
|
Character current = null;
|
||||||
|
for (int i = 0; i < books.size(); i++) {
|
||||||
/**
|
char first = BookFormatter.stripColor(books.get(i)).toLowerCase().charAt(0);
|
||||||
* Prints a list of files
|
if (current == null || current != first) {
|
||||||
*
|
current = first;
|
||||||
* @param sender <p>The command sender to show the list to</p>
|
firstEncounter.put(first, i);
|
||||||
* @param fileList <p>The files to list</p>
|
}
|
||||||
*/
|
|
||||||
public static void printFiles(CommandSender sender, List<String> fileList) {
|
|
||||||
BooksWithoutBorders.sendSuccessMessage(sender, "Available Books:");
|
|
||||||
if (fileList == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int listSize = fileList.size();
|
|
||||||
for (int fileIndex = 0; fileIndex < listSize; fileIndex++) {
|
|
||||||
sender.sendMessage(ChatColor.GRAY + "[" + (fileIndex + 1) + "] " + fileList.get(fileIndex));
|
|
||||||
}
|
}
|
||||||
|
return firstEncounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,7 +134,8 @@ public final class FileHelper {
|
|||||||
* @param searchDirectory <p>The directory to search for files</p>
|
* @param searchDirectory <p>The directory to search for files</p>
|
||||||
* @return <p>A list of available files</p>
|
* @return <p>A list of available files</p>
|
||||||
*/
|
*/
|
||||||
private static List<String> listFiles(CommandSender sender, File searchDirectory) {
|
@Nullable
|
||||||
|
private static List<String> listFiles(@NotNull CommandSender sender, @NotNull File searchDirectory) {
|
||||||
List<String> fileList = new ArrayList<>();
|
List<String> fileList = new ArrayList<>();
|
||||||
File[] existingFiles = searchDirectory.listFiles();
|
File[] existingFiles = searchDirectory.listFiles();
|
||||||
|
|
||||||
@@ -152,11 +145,28 @@ public final class FileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (File foundFile : existingFiles) {
|
for (File foundFile : existingFiles) {
|
||||||
if (foundFile.isFile()) {
|
// Filter out invalid files
|
||||||
fileList.add(foundFile.getName());
|
if (!foundFile.isFile() || foundFile.getName().contains(" ") || foundFile.getName().contains("§")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String fileName = foundFile.getName();
|
||||||
|
String separator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
if (fileName.contains(separator)) {
|
||||||
|
//Convert the UUID into a username if necessary
|
||||||
|
String[] data = fileName.split(separator);
|
||||||
|
String extension = data[1].substring(data[1].length() - 4);
|
||||||
|
String userName = data[1].substring(0, data[1].length() - 4);
|
||||||
|
data[1] = BookHelper.authorFromUUID(userName) + extension;
|
||||||
|
fileList.add(String.join(separator, data));
|
||||||
|
} else {
|
||||||
|
fileList.add(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the book list
|
||||||
|
Comparator<String> bookComparator = Comparator.naturalOrder();
|
||||||
|
fileList.sort((a, b) -> bookComparator.compare(BookFormatter.stripColor(a).toLowerCase(),
|
||||||
|
BookFormatter.stripColor(b).toLowerCase()));
|
||||||
return fileList;
|
return fileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +177,7 @@ public final class FileHelper {
|
|||||||
* @param fileName <p>The name of the file which might already exist</p>
|
* @param fileName <p>The name of the file which might already exist</p>
|
||||||
* @return <p>The number of found duplicates</p>
|
* @return <p>The number of found duplicates</p>
|
||||||
*/
|
*/
|
||||||
public static int findDuplicates(File[] foundFiles, String fileName) {
|
public static int findDuplicates(@NotNull File[] foundFiles, @NotNull String fileName) {
|
||||||
int foundDuplicates = 0;
|
int foundDuplicates = 0;
|
||||||
for (File foundFile : foundFiles) {
|
for (File foundFile : foundFiles) {
|
||||||
if (foundFile.getName().matches("(\\([0-9]+\\))?" + Pattern.quote(fileName) + "(\\.yml|\\.txt)?")) {
|
if (foundFile.getName().matches("(\\([0-9]+\\))?" + Pattern.quote(fileName) + "(\\.yml|\\.txt)?")) {
|
||||||
@@ -177,15 +187,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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -1,13 +1,13 @@
|
|||||||
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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
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
|
||||||
@@ -25,7 +25,7 @@ public final class BookFormatter {
|
|||||||
*
|
*
|
||||||
* @param rawPages <p>A list of pages</p>
|
* @param rawPages <p>A list of pages</p>
|
||||||
*/
|
*/
|
||||||
public static void formatLastPage(List<String> rawPages) {
|
public static void formatLastPage(@NotNull List<String> rawPages) {
|
||||||
int maxPageText = 256;
|
int maxPageText = 256;
|
||||||
int fitsNewline = maxPageText - 2;
|
int fitsNewline = maxPageText - 2;
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ public final class BookFormatter {
|
|||||||
* @param maxPageText <p>The max number of characters which fit on a page</p>
|
* @param maxPageText <p>The max number of characters which fit on a page</p>
|
||||||
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
|
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
|
||||||
*/
|
*/
|
||||||
public static void formatLastPageSplitOverflow(List<String> rawPages, int maxPageText, int fitsNewline) {
|
public static void formatLastPageSplitOverflow(@NotNull List<String> rawPages, int maxPageText, int fitsNewline) {
|
||||||
while (rawPages.get(rawPages.size() - 1).length() > maxPageText) {
|
while (rawPages.get(rawPages.size() - 1).length() > maxPageText) {
|
||||||
int splitPosition;
|
int splitPosition;
|
||||||
String fittingText = rawPages.get(rawPages.size() - 1).substring(0, maxPageText);
|
String fittingText = rawPages.get(rawPages.size() - 1).substring(0, maxPageText);
|
||||||
@@ -78,7 +78,7 @@ public final class BookFormatter {
|
|||||||
* @param rawPages <p>The raw pages to format</p>
|
* @param rawPages <p>The raw pages to format</p>
|
||||||
* @param maxPageText <p>The max number of characters which fit on a page</p>
|
* @param maxPageText <p>The max number of characters which fit on a page</p>
|
||||||
*/
|
*/
|
||||||
public static void formatLastPageCombinePages(List<String> rawPages, int maxPageText) {
|
public static void formatLastPageCombinePages(@NotNull List<String> rawPages, int maxPageText) {
|
||||||
int lastPageIndex = rawPages.size() - 1;
|
int lastPageIndex = rawPages.size() - 1;
|
||||||
int nextToLastIndex = rawPages.size() - 2;
|
int nextToLastIndex = rawPages.size() - 2;
|
||||||
if (rawPages.get(nextToLastIndex).length() + rawPages.get(lastPageIndex).length() <= maxPageText) {
|
if (rawPages.get(nextToLastIndex).length() + rawPages.get(lastPageIndex).length() <= maxPageText) {
|
||||||
@@ -93,7 +93,7 @@ public final class BookFormatter {
|
|||||||
* @param rawPages <p>The raw pages to format</p>
|
* @param rawPages <p>The raw pages to format</p>
|
||||||
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
|
* @param fitsNewline <p>The max number of characters on a page which still fits a newline character</p>
|
||||||
*/
|
*/
|
||||||
public static void formatLastPageAddNewline(List<String> rawPages, int fitsNewline) {
|
public static void formatLastPageAddNewline(@NotNull List<String> rawPages, int fitsNewline) {
|
||||||
int pageIndex = rawPages.size() - 1;
|
int pageIndex = rawPages.size() - 1;
|
||||||
if (rawPages.get(pageIndex).length() <= fitsNewline && !rawPages.get(pageIndex).isEmpty()) {
|
if (rawPages.get(pageIndex).length() <= fitsNewline && !rawPages.get(pageIndex).isEmpty()) {
|
||||||
rawPages.set(pageIndex, (rawPages.get(pageIndex)) + "\n");
|
rawPages.set(pageIndex, (rawPages.get(pageIndex)) + "\n");
|
||||||
@@ -106,29 +106,25 @@ public final class BookFormatter {
|
|||||||
* @param bookMeta <p>The book meta to change</p>
|
* @param bookMeta <p>The book meta to change</p>
|
||||||
* @return <p>The changed book meta</p>
|
* @return <p>The changed book meta</p>
|
||||||
*/
|
*/
|
||||||
public static BookMeta formatPages(BookMeta bookMeta) {
|
@NotNull
|
||||||
|
public static BookMeta formatPages(@NotNull 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
|
* Strips the color from the given input
|
||||||
*
|
*
|
||||||
* @param message <p>The string to search for color codes</p>
|
* @param input <p>The input to strip</p>
|
||||||
* @return <p>The message with color codes translated</p>
|
* @return <p>The color stripped input</p>
|
||||||
*/
|
*/
|
||||||
private static String translateAllColorCodes(String message) {
|
@NotNull
|
||||||
message = ChatColor.translateAlternateColorCodes('&', message);
|
public static String stripColor(@NotNull String input) {
|
||||||
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
|
return ColorHelper.stripColorCodes(input, ColorConversion.RGB);
|
||||||
Matcher matcher = pattern.matcher(message);
|
|
||||||
while (matcher.find()) {
|
|
||||||
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,189 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
|
import net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig;
|
||||||
|
import net.knarcraft.bookswithoutborders.state.BookDirectory;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.config.BooksWithoutBordersConfig.getSlash;
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.cleanString;
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for getting abstract book information
|
||||||
|
*/
|
||||||
|
public final class BookHelper {
|
||||||
|
|
||||||
|
private BookHelper() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the author of a book from UUID if necessary
|
||||||
|
*
|
||||||
|
* @param author <p>The author string</p>
|
||||||
|
* @return <p>The author string, converted if it was a UUID</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static String authorFromUUID(@NotNull String author) {
|
||||||
|
try {
|
||||||
|
UUID authorID = UUID.fromString(author);
|
||||||
|
Player player = Bukkit.getPlayer(authorID);
|
||||||
|
if (player != null) {
|
||||||
|
author = player.getName();
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file path of the selected book directory
|
||||||
|
*
|
||||||
|
* @param bookDirectory <p>The book directory to get (ENCRYPTED is not supported here)</p>
|
||||||
|
* @param sender <p>The command sender trying to get the directory</p>
|
||||||
|
* @return <p>The path of the directory, or null if not possible to get</p>
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static File getBookDirectoryPath(@NotNull BookDirectory bookDirectory, @NotNull CommandSender sender) {
|
||||||
|
String bookFolderString = getBookDirectoryPathString(bookDirectory, sender);
|
||||||
|
if (bookFolderString == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new File(bookFolderString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string path of the selected book directory
|
||||||
|
*
|
||||||
|
* @param bookDirectory <p>The book directory to get (ENCRYPTED is not supported here)</p>
|
||||||
|
* @param sender <p>The command sender trying to get the directory</p>
|
||||||
|
* @return <p>The path of the directory, or null if not possible to get</p>
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getBookDirectoryPathString(@NotNull BookDirectory bookDirectory, @NotNull CommandSender sender) {
|
||||||
|
String folder = null;
|
||||||
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
|
if (bookDirectory == BookDirectory.PUBLIC) {
|
||||||
|
folder = bookFolder;
|
||||||
|
} else if (bookDirectory == BookDirectory.PLAYER && sender instanceof Player player) {
|
||||||
|
folder = bookFolder + player.getUniqueId() + getSlash();
|
||||||
|
}
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the generation of the given book, if necessary
|
||||||
|
*
|
||||||
|
* @param bookItem <p>The book item to increase the generation of</p>
|
||||||
|
*/
|
||||||
|
public static void increaseGeneration(@NotNull ItemStack bookItem) {
|
||||||
|
BookMeta bookMeta = (BookMeta) bookItem.getItemMeta();
|
||||||
|
if (BooksWithoutBordersConfig.changeGenerationOnCopy() && bookMeta != null) {
|
||||||
|
bookMeta.setGeneration(BookHelper.getNextGeneration(bookMeta.getGeneration()));
|
||||||
|
bookItem.setItemMeta(bookMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next generation of the given book
|
||||||
|
*
|
||||||
|
* <p>If an original book is given, this will yield a copy of the original. If a copy of original is given, this
|
||||||
|
* will yield a copy of a copy. In all other cases, the generation will stay the same</p>
|
||||||
|
*
|
||||||
|
* @param currentGeneration <p>The current generation of the book</p>
|
||||||
|
* @return <p>The next generation of the book</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static BookMeta.Generation getNextGeneration(@Nullable BookMeta.Generation currentGeneration) {
|
||||||
|
if (currentGeneration == null) {
|
||||||
|
return BookMeta.Generation.COPY_OF_ORIGINAL;
|
||||||
|
}
|
||||||
|
return switch (currentGeneration) {
|
||||||
|
case ORIGINAL -> BookMeta.Generation.COPY_OF_ORIGINAL;
|
||||||
|
case COPY_OF_ORIGINAL -> BookMeta.Generation.COPY_OF_COPY;
|
||||||
|
default -> currentGeneration;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file name of the given book
|
||||||
|
*
|
||||||
|
* @param book <p>The book to get the file of</p>
|
||||||
|
* @param player <p>The player trying to do something with the book</p>
|
||||||
|
* @return <p>The book file</p>
|
||||||
|
* @throws IllegalArgumentException <p>If the book title or author contains the title author separator</p>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static String getBookFile(@NotNull BookMeta book, @NotNull Player player, boolean isPublic) throws IllegalArgumentException {
|
||||||
|
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
String bookName;
|
||||||
|
if (book.hasTitle()) {
|
||||||
|
bookName = book.getTitle();
|
||||||
|
if (bookName == null) {
|
||||||
|
bookName = "Untitled";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bookName = "Untitled";
|
||||||
|
}
|
||||||
|
|
||||||
|
String authorName;
|
||||||
|
if ((!book.hasAuthor() || isAuthor(player.getName(), book.getAuthor())) && !isPublic) {
|
||||||
|
//Store as unique id to account for name changes
|
||||||
|
authorName = player.getUniqueId().toString();
|
||||||
|
} else if (!book.hasAuthor()) {
|
||||||
|
authorName = player.getName();
|
||||||
|
} else {
|
||||||
|
authorName = book.getAuthor();
|
||||||
|
if (authorName == null) {
|
||||||
|
authorName = "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bookName.contains(titleAuthorSeparator) || authorName.contains(titleAuthorSeparator)) {
|
||||||
|
throw new IllegalArgumentException("The author or title contains the title author separator. Saving this " +
|
||||||
|
"book would lead to unexpected problems.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixName(cleanString(bookName + titleAuthorSeparator + authorName), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given player is the author of a given book
|
||||||
|
*
|
||||||
|
* @param player <p>The player to check</p>
|
||||||
|
* @param book <p>The book to check</p>
|
||||||
|
* @return <p>True if the player is not the book's author</p>
|
||||||
|
*/
|
||||||
|
public static boolean isNotAuthor(@NotNull Player player, @NotNull BookMeta book) {
|
||||||
|
if (isAuthor(player.getName(), book.getAuthor())) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player,
|
||||||
|
"You must be the author of this book to use this command!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the given player name is equal to the given book author
|
||||||
|
*
|
||||||
|
* @param playerName <p>The player name to check</p>
|
||||||
|
* @param author <p>The author to check</p>
|
||||||
|
* @return <p>True if the player is the author</p>
|
||||||
|
*/
|
||||||
|
private static boolean isAuthor(@NotNull String playerName, @Nullable String author) {
|
||||||
|
playerName = InputCleaningHelper.cleanString(playerName);
|
||||||
|
return author != null && playerName.equalsIgnoreCase(InputCleaningHelper.cleanString(author));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -3,11 +3,14 @@ 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;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,7 +33,9 @@ public final class BookLoader {
|
|||||||
* @param directory <p>The directory to save the book in</p>
|
* @param directory <p>The directory to save the book in</p>
|
||||||
* @return <p>The loaded book</p>
|
* @return <p>The loaded book</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory) {
|
@Nullable
|
||||||
|
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
|
||||||
|
@NotNull String directory) {
|
||||||
return loadBook(sender, fileName, isSigned, directory, 1);
|
return loadBook(sender, fileName, isSigned, directory, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +49,14 @@ public final class BookLoader {
|
|||||||
* @param numCopies <p>The number of copies to load</p>
|
* @param numCopies <p>The number of copies to load</p>
|
||||||
* @return <p>The loaded book</p>
|
* @return <p>The loaded book</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack loadBook(CommandSender sender, String fileName, String isSigned, String directory, int numCopies) {
|
@Nullable
|
||||||
|
public static ItemStack loadBook(@NotNull CommandSender sender, @NotNull String fileName, @NotNull String isSigned,
|
||||||
|
@NotNull String directory, int numCopies) {
|
||||||
BookDirectory bookDirectory = BookDirectory.getFromString(directory);
|
BookDirectory bookDirectory = BookDirectory.getFromString(directory);
|
||||||
|
if (bookDirectory == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Unrecognized book directory!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//Find the filename if a book index is given
|
//Find the filename if a book index is given
|
||||||
try {
|
try {
|
||||||
@@ -60,11 +71,27 @@ 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
|
||||||
if (BooksWithoutBordersConfig.booksHavePrice() && !sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
if (BooksWithoutBordersConfig.booksHavePrice() &&
|
||||||
|
!sender.hasPermission("bookswithoutborders.bypassBookPrice") &&
|
||||||
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
(bookDirectory == BookDirectory.PUBLIC || bookDirectory == BookDirectory.PLAYER) &&
|
||||||
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
EconomyHelper.cannotPayForBookPrinting((Player) sender, numCopies)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -79,8 +106,13 @@ public final class BookLoader {
|
|||||||
book = new ItemStack(Material.WRITABLE_BOOK);
|
book = new ItemStack(Material.WRITABLE_BOOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bookMetadata == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(sender, "Unable to create blank book metadata!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//Load the book from the given file
|
//Load the book from the given file
|
||||||
BookToFromTextHelper.bookFromFile(file, bookMetadata);
|
bookMetadata = BookToFromTextHelper.bookFromFile(file, bookMetadata);
|
||||||
if (bookMetadata == null) {
|
if (bookMetadata == null) {
|
||||||
BooksWithoutBorders.sendErrorMessage(sender, "File was blank!!");
|
BooksWithoutBorders.sendErrorMessage(sender, "File was blank!!");
|
||||||
return null;
|
return null;
|
||||||
@@ -98,6 +130,8 @@ public final class BookLoader {
|
|||||||
|
|
||||||
//Set the metadata and amount to the new book
|
//Set the metadata and amount to the new book
|
||||||
book.setItemMeta(bookMetadata);
|
book.setItemMeta(bookMetadata);
|
||||||
|
//Increase book generation if enabled
|
||||||
|
BookHelper.increaseGeneration(book);
|
||||||
book.setAmount(numCopies);
|
book.setAmount(numCopies);
|
||||||
|
|
||||||
return book;
|
return book;
|
||||||
@@ -112,19 +146,18 @@ public final class BookLoader {
|
|||||||
* @param directory <p>The relative directory given</p>
|
* @param directory <p>The relative directory given</p>
|
||||||
* @return <p>A file or null if it does not exist</p>
|
* @return <p>A file or null if it does not exist</p>
|
||||||
*/
|
*/
|
||||||
private static File getFullPath(CommandSender sender, String fileName, BookDirectory bookDirectory, String directory) {
|
@Nullable
|
||||||
File file = null;
|
private static File getFullPath(@NotNull CommandSender sender, @NotNull String fileName,
|
||||||
|
@NotNull BookDirectory bookDirectory, @NotNull String directory) {
|
||||||
|
File file;
|
||||||
String slash = BooksWithoutBordersConfig.getSlash();
|
String slash = BooksWithoutBordersConfig.getSlash();
|
||||||
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
String bookFolder = BooksWithoutBordersConfig.getBookFolder();
|
||||||
if (bookDirectory == BookDirectory.PUBLIC) {
|
if (bookDirectory == BookDirectory.ENCRYPTED) {
|
||||||
file = FileHelper.getBookFile(bookFolder + fileName);
|
file = BookFileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + fileName);
|
||||||
} else if (bookDirectory == BookDirectory.PLAYER) {
|
} else {
|
||||||
file = FileHelper.getBookFile(bookFolder + InputCleaningHelper.cleanString(sender.getName()) + slash + fileName);
|
file = BookFileHelper.getBookFile(BookHelper.getBookDirectoryPathString(bookDirectory, sender) + fileName);
|
||||||
} else if (bookDirectory == BookDirectory.ENCRYPTED) {
|
|
||||||
file = FileHelper.getBookFile(bookFolder + "Encrypted" + slash + directory + slash + 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;
|
||||||
|
@@ -1,19 +1,26 @@
|
|||||||
package net.knarcraft.bookswithoutborders.utility;
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import net.knarcraft.bookswithoutborders.BooksWithoutBorders;
|
||||||
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 org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
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;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static net.knarcraft.bookswithoutborders.utility.BookHelper.authorFromUUID;
|
||||||
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
import static net.knarcraft.bookswithoutborders.utility.InputCleaningHelper.fixName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +39,7 @@ public final class BookToFromTextHelper {
|
|||||||
* @param bookMetadata <p>Metadata about the book to save</p>
|
* @param bookMetadata <p>Metadata about the book to save</p>
|
||||||
* @throws IOException <p>If unable to save the book</p>
|
* @throws IOException <p>If unable to save the book</p>
|
||||||
*/
|
*/
|
||||||
public static void bookToYml(String path, String fileName, BookMeta bookMetadata) throws IOException {
|
public static void bookToYml(@NotNull String path, @NotNull String fileName, @NotNull BookMeta bookMetadata) throws IOException {
|
||||||
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(new File(path, "blank"));
|
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(new File(path, "blank"));
|
||||||
|
|
||||||
if (bookMetadata.hasTitle()) {
|
if (bookMetadata.hasTitle()) {
|
||||||
@@ -41,6 +48,11 @@ public final class BookToFromTextHelper {
|
|||||||
if (bookMetadata.hasAuthor()) {
|
if (bookMetadata.hasAuthor()) {
|
||||||
bookYml.set("Author", bookMetadata.getAuthor());
|
bookYml.set("Author", bookMetadata.getAuthor());
|
||||||
}
|
}
|
||||||
|
BookMeta.Generation generation = bookMetadata.getGeneration();
|
||||||
|
if (generation == null) {
|
||||||
|
generation = BookMeta.Generation.ORIGINAL;
|
||||||
|
}
|
||||||
|
bookYml.set("Generation", generation.name());
|
||||||
if (bookMetadata.hasPages()) {
|
if (bookMetadata.hasPages()) {
|
||||||
bookYml.set("Pages", bookMetadata.getPages());
|
bookYml.set("Pages", bookMetadata.getPages());
|
||||||
}
|
}
|
||||||
@@ -48,7 +60,7 @@ public final class BookToFromTextHelper {
|
|||||||
bookYml.set("Lore", bookMetadata.getLore());
|
bookYml.set("Lore", bookMetadata.getLore());
|
||||||
}
|
}
|
||||||
|
|
||||||
bookYml.save(path + fileName + ".yml");
|
bookYml.save(path + InputCleaningHelper.cleanString(fileName) + ".yml");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +70,8 @@ public final class BookToFromTextHelper {
|
|||||||
* @param bookMetadata <p>The book metadata to use for saving the book</p>
|
* @param bookMetadata <p>The book metadata to use for saving the book</p>
|
||||||
* @return <p>The book metadata of the loaded book</p>
|
* @return <p>The book metadata of the loaded book</p>
|
||||||
*/
|
*/
|
||||||
public static BookMeta bookFromFile(File file, BookMeta bookMetadata) {
|
@Nullable
|
||||||
|
public static BookMeta bookFromFile(@NotNull File file, @NotNull BookMeta bookMetadata) {
|
||||||
if (file.getName().endsWith(".txt")) {
|
if (file.getName().endsWith(".txt")) {
|
||||||
return bookFromTXT(file.getName(), file, bookMetadata);
|
return bookFromTXT(file.getName(), file, bookMetadata);
|
||||||
} else if (file.getName().endsWith(".yml")) {
|
} else if (file.getName().endsWith(".yml")) {
|
||||||
@@ -76,13 +89,19 @@ public final class BookToFromTextHelper {
|
|||||||
* @param bookMetadata <p>Metadata about the book to save</p>
|
* @param bookMetadata <p>Metadata about the book to save</p>
|
||||||
* @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(@NotNull String folderPath, @NotNull String fileName, @NotNull BookMeta bookMetadata) throws IOException {
|
||||||
FileWriter fileWriter = new FileWriter(folderPath + fileName + ".txt");
|
FileWriter fileWriter = new FileWriter(folderPath + InputCleaningHelper.cleanString(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();
|
||||||
|
|
||||||
|
BookMeta.Generation generation = bookMetadata.getGeneration();
|
||||||
|
if (generation == null) {
|
||||||
|
generation = BookMeta.Generation.ORIGINAL;
|
||||||
|
}
|
||||||
|
String generationString = ":" + generation.name();
|
||||||
|
|
||||||
//Save each page of the book as a text line
|
//Save each page of the book as a text line
|
||||||
printWriter.println("[Book]");
|
printWriter.println("[Book]" + generationString);
|
||||||
for (String page : pages) {
|
for (String page : pages) {
|
||||||
printWriter.println(page);
|
printWriter.println(page);
|
||||||
}
|
}
|
||||||
@@ -96,12 +115,14 @@ public final class BookToFromTextHelper {
|
|||||||
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
|
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
|
||||||
* @return <p>Metadata for the loaded book</p>
|
* @return <p>Metadata for the loaded book</p>
|
||||||
*/
|
*/
|
||||||
private static BookMeta bookFromYml(File file, BookMeta bookMetadata) {
|
@Nullable
|
||||||
|
private static BookMeta bookFromYml(@NotNull File file, @NotNull BookMeta bookMetadata) {
|
||||||
try {
|
try {
|
||||||
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
|
FileConfiguration bookYml = YamlConfiguration.loadConfiguration(file);
|
||||||
|
|
||||||
|
bookMetadata.setGeneration(BookMeta.Generation.valueOf(bookYml.getString("Generation", "ORIGINAL")));
|
||||||
bookMetadata.setTitle(bookYml.getString("Title", "Untitled"));
|
bookMetadata.setTitle(bookYml.getString("Title", "Untitled"));
|
||||||
bookMetadata.setAuthor(bookYml.getString("Author", "Unknown"));
|
bookMetadata.setAuthor(authorFromUUID(bookYml.getString("Author", "Unknown")));
|
||||||
bookMetadata.setPages(bookYml.getStringList("Pages"));
|
bookMetadata.setPages(bookYml.getStringList("Pages"));
|
||||||
bookMetadata.setLore(bookYml.getStringList("Lore"));
|
bookMetadata.setLore(bookYml.getStringList("Lore"));
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@@ -118,18 +139,22 @@ public final class BookToFromTextHelper {
|
|||||||
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
|
* @param bookMetadata <p>Metadata which will be altered with the book's contents</p>
|
||||||
* @return <p>Metadata for the loaded book</p>
|
* @return <p>Metadata for the loaded book</p>
|
||||||
*/
|
*/
|
||||||
private static BookMeta bookFromTXT(String fileName, File file, BookMeta bookMetadata) {
|
@Nullable
|
||||||
|
private static BookMeta bookFromTXT(@NotNull String fileName, @NotNull File file, @NotNull BookMeta bookMetadata) {
|
||||||
String author;
|
String author;
|
||||||
String title;
|
String title;
|
||||||
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
String titleAuthorSeparator = BooksWithoutBordersConfig.getTitleAuthorSeparator();
|
||||||
|
|
||||||
|
//Remove .txt extension
|
||||||
|
fileName = fileName.substring(0, fileName.length() - 4);
|
||||||
//Get title and author from the file name
|
//Get title and author from the file name
|
||||||
if (fileName.contains(titleAuthorSeparator)) {
|
if (fileName.contains(titleAuthorSeparator)) {
|
||||||
author = fileName.substring(fileName.indexOf(titleAuthorSeparator) + 1, fileName.length() - 4);
|
String[] titleAuthor = fileName.split(titleAuthorSeparator);
|
||||||
title = fileName.substring(0, fileName.indexOf(titleAuthorSeparator));
|
title = titleAuthor[0];
|
||||||
|
author = titleAuthor[1];
|
||||||
} else {
|
} else {
|
||||||
author = "Unknown";
|
author = "Unknown";
|
||||||
title = fileName.substring(0, fileName.length() - 4);
|
title = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Replace underscores with spaces
|
//Replace underscores with spaces
|
||||||
@@ -139,16 +164,26 @@ public final class BookToFromTextHelper {
|
|||||||
List<String> rawPages;
|
List<String> rawPages;
|
||||||
try {
|
try {
|
||||||
rawPages = readTextFile(file);
|
rawPages = readTextFile(file);
|
||||||
} catch (IOException e) {
|
if (rawPages == null) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Text file's first line was null");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (IOException exception) {
|
||||||
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to read text file");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Parse the generation from the book data
|
||||||
|
if (!rawPages.isEmpty() && rawPages.get(0).startsWith("Generation:")) {
|
||||||
|
bookMetadata.setGeneration(BookMeta.Generation.valueOf(rawPages.get(0).split(":")[1]));
|
||||||
|
rawPages.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
//Remove any empty pages
|
//Remove any empty pages
|
||||||
List<String> pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages));
|
List<String> pages = new ArrayList<>(InputCleaningHelper.cleanList(rawPages));
|
||||||
|
|
||||||
//Update the metadata of the book with its new values
|
//Update the metadata of the book with its new values
|
||||||
bookMetadata.setAuthor(author);
|
bookMetadata.setAuthor(authorFromUUID(author));
|
||||||
bookMetadata.setTitle(title);
|
bookMetadata.setTitle(title);
|
||||||
bookMetadata.setPages(pages);
|
bookMetadata.setPages(pages);
|
||||||
|
|
||||||
@@ -162,9 +197,10 @@ public final class BookToFromTextHelper {
|
|||||||
* @return <p>A string list where each string is the text on one page</p>
|
* @return <p>A string list where each string is the text on one page</p>
|
||||||
* @throws IOException <p>If unable to read from the file</p>
|
* @throws IOException <p>If unable to read from the file</p>
|
||||||
*/
|
*/
|
||||||
private static List<String> readTextFile(File file) throws IOException {
|
@Nullable
|
||||||
|
private static List<String> readTextFile(@NotNull 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();
|
||||||
@@ -172,8 +208,11 @@ public final class BookToFromTextHelper {
|
|||||||
bufferedReader.close();
|
bufferedReader.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (firstLine.equalsIgnoreCase("[Book]")) {
|
if (firstLine.toLowerCase().startsWith("[book]")) {
|
||||||
//Read every line directly as a page, as this is a saved book
|
//Read every line directly as a page, as this is a saved book
|
||||||
|
if (firstLine.contains(":")) {
|
||||||
|
rawPages.add("Generation:" + firstLine.split(":")[1]);
|
||||||
|
}
|
||||||
String readLine;
|
String readLine;
|
||||||
do {
|
do {
|
||||||
readLine = bufferedReader.readLine();
|
readLine = bufferedReader.readLine();
|
||||||
|
@@ -8,10 +8,14 @@ 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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +33,7 @@ public final class EconomyHelper {
|
|||||||
*
|
*
|
||||||
* @return <p>An economy instance, or null if it's not initialized</p>
|
* @return <p>An economy instance, or null if it's not initialized</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static Economy getEconomy() {
|
public static Economy getEconomy() {
|
||||||
return economy;
|
return economy;
|
||||||
}
|
}
|
||||||
@@ -64,7 +69,7 @@ public final class EconomyHelper {
|
|||||||
* @param numCopies <p>The number of copies the player is trying to print</p>
|
* @param numCopies <p>The number of copies the player is trying to print</p>
|
||||||
* @return <p>True if the player cannot pay for the printing of the books</p>
|
* @return <p>True if the player cannot pay for the printing of the books</p>
|
||||||
*/
|
*/
|
||||||
public static boolean cannotPayForBookPrinting(Player player, int numCopies) {
|
public static boolean cannotPayForBookPrinting(@NotNull Player player, int numCopies) {
|
||||||
//BookPriceQuantity: How many items are required to pay for each book
|
//BookPriceQuantity: How many items are required to pay for each book
|
||||||
//BookPriceType: Which item is used to pay for the books. AIR = use economy
|
//BookPriceType: Which item is used to pay for the books. AIR = use economy
|
||||||
Material bookCurrency = BooksWithoutBordersConfig.getBookPriceType();
|
Material bookCurrency = BooksWithoutBordersConfig.getBookPriceType();
|
||||||
@@ -74,17 +79,90 @@ 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(@NotNull 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(@NotNull 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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static List<ItemStack> getPlayersEmptyBooks(@NotNull 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
|
||||||
*
|
*
|
||||||
@@ -93,11 +171,13 @@ public final class EconomyHelper {
|
|||||||
* @param numCopies <p>The number of books the player is printing</p>
|
* @param numCopies <p>The number of books the player is printing</p>
|
||||||
* @return <p>True if the player had the money and it has been withdrawn</p>
|
* @return <p>True if the player had the money and it has been withdrawn</p>
|
||||||
*/
|
*/
|
||||||
private static boolean payForBookPrintingEconomy(Player player, double cost, int numCopies) {
|
private static boolean payForBookPrintingEconomy(@NotNull 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!");
|
||||||
@@ -111,7 +191,7 @@ public final class EconomyHelper {
|
|||||||
* @param player <p>The player which needs to pay</p>
|
* @param player <p>The player which needs to pay</p>
|
||||||
* @param itemCost <p>The number of items to pay</p>
|
* @param itemCost <p>The number of items to pay</p>
|
||||||
*/
|
*/
|
||||||
private static void payForBookPrintingItem(Player player, int itemCost) {
|
private static void payForBookPrintingItem(@NotNull Player player, int itemCost) {
|
||||||
PlayerInventory playerInventory = player.getInventory();
|
PlayerInventory playerInventory = player.getInventory();
|
||||||
|
|
||||||
int clearedAmount = 0;
|
int clearedAmount = 0;
|
||||||
|
@@ -5,17 +5,20 @@ 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;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
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;
|
||||||
@@ -36,7 +39,8 @@ public final class EncryptionHelper {
|
|||||||
* @param key <p>The key to transform</p>
|
* @param key <p>The key to transform</p>
|
||||||
* @return <p>The numbers representing the key's characters</p>
|
* @return <p>The numbers representing the key's characters</p>
|
||||||
*/
|
*/
|
||||||
public static String getNumberKeyFromStringKey(String key) {
|
@NotNull
|
||||||
|
public static String getNumberKeyFromStringKey(@NotNull String key) {
|
||||||
StringBuilder integerKey = new StringBuilder();
|
StringBuilder integerKey = new StringBuilder();
|
||||||
for (int x = 0; x < key.length(); x++) {
|
for (int x = 0; x < key.length(); x++) {
|
||||||
integerKey.append(Character.getNumericValue(Character.codePointAt(key, x)));
|
integerKey.append(Character.getNumericValue(Character.codePointAt(key, x)));
|
||||||
@@ -53,7 +57,9 @@ public final class EncryptionHelper {
|
|||||||
* @param player <p>The player trying to encrypt a book</p>
|
* @param player <p>The player trying to encrypt a book</p>
|
||||||
* @return <p>The pages of the book in encrypted form</p>
|
* @return <p>The pages of the book in encrypted form</p>
|
||||||
*/
|
*/
|
||||||
public static List<String> encryptBookPages(BookMeta book, EncryptionStyle style, String integerKey, Player player) {
|
@Nullable
|
||||||
|
public static List<String> encryptBookPages(@NotNull BookMeta book, @NotNull EncryptionStyle style,
|
||||||
|
@NotNull String integerKey, @NotNull Player player) {
|
||||||
List<String> encryptedPages = new ArrayList<>();
|
List<String> encryptedPages = new ArrayList<>();
|
||||||
//Scramble the book's contents
|
//Scramble the book's contents
|
||||||
if (style == EncryptionStyle.DNA) {
|
if (style == EncryptionStyle.DNA) {
|
||||||
@@ -85,7 +91,9 @@ public final class EncryptionHelper {
|
|||||||
* @param style <p>The encryption style to use</p>
|
* @param style <p>The encryption style to use</p>
|
||||||
* @return <p>An encrypted version of the book</p>
|
* @return <p>An encrypted version of the book</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack encryptBook(Player player, boolean mainHand, String key, EncryptionStyle style) {
|
@Nullable
|
||||||
|
public static ItemStack encryptBook(@NotNull Player player, boolean mainHand, @NotNull String key,
|
||||||
|
@NotNull EncryptionStyle style) {
|
||||||
return encryptBook(player, mainHand, key, style, "");
|
return encryptBook(player, mainHand, key, style, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +107,17 @@ public final class EncryptionHelper {
|
|||||||
* @param groupName <p>The name of the group to encrypt for, or "" otherwise</p>
|
* @param groupName <p>The name of the group to encrypt for, or "" otherwise</p>
|
||||||
* @return <p>An encrypted version of the book</p>
|
* @return <p>An encrypted version of the book</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack encryptBook(Player player, boolean mainHand, String key, EncryptionStyle style, String groupName) {
|
@Nullable
|
||||||
|
public static ItemStack encryptBook(Player player, boolean mainHand, @NotNull String key,
|
||||||
|
@NotNull EncryptionStyle style, @NotNull String groupName) {
|
||||||
//converts user supplied key into integer form
|
//converts user supplied key into integer form
|
||||||
String integerKey = EncryptionHelper.getNumberKeyFromStringKey(key);
|
String integerKey = EncryptionHelper.getNumberKeyFromStringKey(key);
|
||||||
|
|
||||||
BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
BookMeta book = InventoryHelper.getHeldBookMetadata(player, mainHand);
|
||||||
|
if (book == null) {
|
||||||
|
BooksWithoutBorders.sendErrorMessage(player, "Unable to get metadata from the held book!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!book.hasPages()) {
|
if (!book.hasPages()) {
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Book is empty!");
|
BooksWithoutBorders.sendErrorMessage(player, "Book is empty!");
|
||||||
@@ -142,7 +156,9 @@ public final class EncryptionHelper {
|
|||||||
* @param newMetadata <p>The new metadata of the book</p>
|
* @param newMetadata <p>The new metadata of the book</p>
|
||||||
* @return <p>An encrypted version of the book</p>
|
* @return <p>An encrypted version of the book</p>
|
||||||
*/
|
*/
|
||||||
private static ItemStack createEncryptedBook(BookMeta book, List<String> newPages, Player player, BookMeta newMetadata) {
|
@NotNull
|
||||||
|
private static ItemStack createEncryptedBook(@NotNull BookMeta book, @NotNull List<String> newPages,
|
||||||
|
@NotNull Player player, @NotNull BookMeta newMetadata) {
|
||||||
//Create the encrypted book
|
//Create the encrypted book
|
||||||
ItemStack encryptedBook = new ItemStack(Material.WRITTEN_BOOK);
|
ItemStack encryptedBook = new ItemStack(Material.WRITTEN_BOOK);
|
||||||
book.setPages(newPages);
|
book.setPages(newPages);
|
||||||
@@ -165,7 +181,9 @@ public final class EncryptionHelper {
|
|||||||
* @param integerKey <p>The key used to encrypt the book</p>
|
* @param integerKey <p>The key used to encrypt the book</p>
|
||||||
* @return <p>The new metadata for the book, or null if it could not be saved</p>
|
* @return <p>The new metadata for the book, or null if it could not be saved</p>
|
||||||
*/
|
*/
|
||||||
private static BookMeta saveBookPlaintext(String groupName, Player player, BookMeta book, String integerKey) {
|
@Nullable
|
||||||
|
private static BookMeta saveBookPlaintext(@NotNull String groupName, @NotNull Player player,
|
||||||
|
@NotNull BookMeta book, @NotNull String integerKey) {
|
||||||
BookMeta newMetadata = book;
|
BookMeta newMetadata = book;
|
||||||
boolean wasSaved;
|
boolean wasSaved;
|
||||||
if (groupName.trim().isEmpty()) {
|
if (groupName.trim().isEmpty()) {
|
||||||
@@ -189,7 +207,8 @@ public final class EncryptionHelper {
|
|||||||
* @param deleteEncryptedFile <p>Whether to delete the plaintext file after decryption is finished</p>
|
* @param deleteEncryptedFile <p>Whether to delete the plaintext file after decryption is finished</p>
|
||||||
* @return <p>The loaded book, or null if no book could be loaded</p>
|
* @return <p>The loaded book, or null if no book could be loaded</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack loadEncryptedBook(Player player, String key, boolean deleteEncryptedFile) {
|
@Nullable
|
||||||
|
public static ItemStack loadEncryptedBook(@NotNull Player player, @NotNull String key, boolean deleteEncryptedFile) {
|
||||||
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
ItemStack heldBook = InventoryHelper.getHeldBook(player, true);
|
||||||
BookMeta bookMetadata = (BookMeta) heldBook.getItemMeta();
|
BookMeta bookMetadata = (BookMeta) heldBook.getItemMeta();
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||||
@@ -198,11 +217,8 @@ public final class EncryptionHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() : bookMetadata.getTitle() +
|
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetadata, player, true);
|
||||||
BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
fileName = fixName(cleanString(fileName), false);
|
||||||
fileName = "[" + key + "]" + fileName;
|
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
File file = new File(path + fileName + ".yml");
|
File file = new File(path + fileName + ".yml");
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
@@ -248,7 +264,9 @@ public final class EncryptionHelper {
|
|||||||
* @param groupName <p>The group which should be able to decrypt the book</p>
|
* @param groupName <p>The group which should be able to decrypt the book</p>
|
||||||
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
||||||
*/
|
*/
|
||||||
private static BookMeta saveEncryptedBookForGroup(Player player, BookMeta bookMetadata, String groupName) {
|
@Nullable
|
||||||
|
private static BookMeta saveEncryptedBookForGroup(@NotNull Player player, @NotNull BookMeta bookMetadata,
|
||||||
|
@NotNull String groupName) {
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash() + cleanString(groupName) + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash() + cleanString(groupName) + getSlash();
|
||||||
File dirTest = new File(path);
|
File dirTest = new File(path);
|
||||||
//Creates group dir
|
//Creates group dir
|
||||||
@@ -258,17 +276,13 @@ public final class EncryptionHelper {
|
|||||||
BooksWithoutBorders.sendErrorMessage(BooksWithoutBorders.getConsoleSender(), "Unable to create encryption group folder!");
|
BooksWithoutBorders.sendErrorMessage(BooksWithoutBorders.getConsoleSender(), "Unable to create encryption group folder!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception exception) {
|
||||||
e.printStackTrace();
|
BooksWithoutBorders.getInstance().getLogger().log(Level.SEVERE, "Unable to save group encrypted book");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Creates file
|
//Generate file name
|
||||||
String fileName = (!bookMetadata.hasTitle()) ? "Untitled," + player.getName() :
|
String fileName = BookHelper.getBookFile(bookMetadata, player, true);
|
||||||
bookMetadata.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetadata.getAuthor();
|
|
||||||
|
|
||||||
fileName = cleanString(fileName);
|
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
List<String> newLore = new ArrayList<>();
|
List<String> newLore = new ArrayList<>();
|
||||||
newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]");
|
newLore.add(ChatColor.GRAY + "[" + groupName + " encrypted]");
|
||||||
@@ -281,12 +295,12 @@ public final class EncryptionHelper {
|
|||||||
bookMetadata.setLore(newLore);
|
bookMetadata.setLore(newLore);
|
||||||
|
|
||||||
//Save file
|
//Save file
|
||||||
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") : new File(path + fileName + ".txt");
|
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") :
|
||||||
|
new File(path + fileName + ".txt");
|
||||||
if (!file.isFile()) {
|
if (!file.isFile()) {
|
||||||
try {
|
try {
|
||||||
BookToFromTextHelper.bookToYml(path, fileName, bookMetadata);
|
BookToFromTextHelper.bookToYml(path, fileName, bookMetadata);
|
||||||
} catch (IOException e) {
|
} catch (IOException exception) {
|
||||||
e.printStackTrace();
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Group encrypted failed!");
|
BooksWithoutBorders.sendErrorMessage(player, "Group encrypted failed!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -303,25 +317,23 @@ public final class EncryptionHelper {
|
|||||||
* @param key <p>The key to use for encryption</p>
|
* @param key <p>The key to use for encryption</p>
|
||||||
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
* @return <p>The new encrypted metadata for the book, or null if encryption failed</p>
|
||||||
*/
|
*/
|
||||||
private static Boolean saveEncryptedBook(Player player, BookMeta bookMetaData, String key) {
|
@NotNull
|
||||||
|
private static Boolean saveEncryptedBook(@NotNull Player player, @NotNull BookMeta bookMetaData, @NotNull String key) {
|
||||||
String path = getBookFolder() + "Encrypted" + getSlash();
|
String path = getBookFolder() + "Encrypted" + getSlash();
|
||||||
String fileName = (!bookMetaData.hasTitle()) ? "Untitled," + player.getName() :
|
|
||||||
bookMetaData.getTitle() + BooksWithoutBordersConfig.getTitleAuthorSeparator() + bookMetaData.getAuthor();
|
|
||||||
|
|
||||||
fileName = "[" + key + "]" + fileName;
|
String fileName = "[" + key + "]" + BookHelper.getBookFile(bookMetaData, player, true);
|
||||||
fileName = cleanString(fileName);
|
fileName = fixName(cleanString(fileName), false);
|
||||||
fileName = fixName(fileName, false);
|
|
||||||
|
|
||||||
//cancels saving if file is already encrypted
|
//cancels saving if file is already encrypted
|
||||||
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") : new File(path + fileName + ".txt");
|
File file = (BooksWithoutBordersConfig.getUseYml()) ? new File(path + fileName + ".yml") :
|
||||||
|
new File(path + fileName + ".txt");
|
||||||
if (file.isFile()) {
|
if (file.isFile()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BookToFromTextHelper.bookToYml(path, fileName, bookMetaData);
|
BookToFromTextHelper.bookToYml(path, fileName, bookMetaData);
|
||||||
} catch (IOException e) {
|
} catch (IOException exception) {
|
||||||
e.printStackTrace();
|
|
||||||
BooksWithoutBorders.sendErrorMessage(player, "Encryption failed!");
|
BooksWithoutBorders.sendErrorMessage(player, "Encryption failed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
package net.knarcraft.bookswithoutborders.utility;
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for cleaning input and names
|
* Helper class for cleaning input and names
|
||||||
@@ -17,7 +22,8 @@ public final class InputCleaningHelper {
|
|||||||
* @param list <p>The list to clean</p>
|
* @param list <p>The list to clean</p>
|
||||||
* @return <p>A clean list containing all relevant values</p>
|
* @return <p>A clean list containing all relevant values</p>
|
||||||
*/
|
*/
|
||||||
public static List<String> cleanList(List<String> list) {
|
@NotNull
|
||||||
|
public static List<String> cleanList(@NotNull List<String> list) {
|
||||||
List<String> resultList = new ArrayList<>(list);
|
List<String> resultList = new ArrayList<>(list);
|
||||||
resultList.removeIf((item) -> item == null || item.trim().isEmpty());
|
resultList.removeIf((item) -> item == null || item.trim().isEmpty());
|
||||||
return resultList;
|
return resultList;
|
||||||
@@ -29,7 +35,8 @@ public final class InputCleaningHelper {
|
|||||||
* @param fileName <p>The file name to clean</p>
|
* @param fileName <p>The file name to clean</p>
|
||||||
* @return <p>The cleaned file name</p>
|
* @return <p>The cleaned file name</p>
|
||||||
*/
|
*/
|
||||||
public static String cleanString(String fileName) {
|
@NotNull
|
||||||
|
public static String cleanString(@NotNull String fileName) {
|
||||||
fileName = fileName.replace("/", "");
|
fileName = fileName.replace("/", "");
|
||||||
fileName = fileName.replace("\\", "");
|
fileName = fileName.replace("\\", "");
|
||||||
fileName = fileName.replace("*", "");
|
fileName = fileName.replace("*", "");
|
||||||
@@ -39,6 +46,7 @@ public final class InputCleaningHelper {
|
|||||||
fileName = fileName.replace(">", "");
|
fileName = fileName.replace(">", "");
|
||||||
fileName = fileName.replace("?", "");
|
fileName = fileName.replace("?", "");
|
||||||
fileName = fileName.replace("\"", "");
|
fileName = fileName.replace("\"", "");
|
||||||
|
fileName = fileName.replace("§", "&");
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +57,8 @@ public final class InputCleaningHelper {
|
|||||||
* @param isLoading <p>Whether loading from a file as opposed to saving to a file</p>
|
* @param isLoading <p>Whether loading from a file as opposed to saving to a file</p>
|
||||||
* @return <p>The fixed name</p>
|
* @return <p>The fixed name</p>
|
||||||
*/
|
*/
|
||||||
public static String fixName(String fileName, Boolean isLoading) {
|
@NotNull
|
||||||
|
public static String fixName(@NotNull String fileName, @NotNull Boolean isLoading) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
fileName = fileName.replace("_", " ");
|
fileName = fileName.replace("_", " ");
|
||||||
} else {
|
} else {
|
||||||
@@ -58,4 +67,41 @@ public final class InputCleaningHelper {
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a page number for a string like "page1"
|
||||||
|
*
|
||||||
|
* @param input <p>The input to parse</p>
|
||||||
|
* @return <p>The page number, or 0 if not valid</p>
|
||||||
|
*/
|
||||||
|
public static int parsePageNumber(@NotNull String input) {
|
||||||
|
try {
|
||||||
|
Pattern pattern = Pattern.compile("page([0-9]+)");
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
return Integer.parseInt(matcher.group(1));
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an author specifier given in a command
|
||||||
|
*
|
||||||
|
* @param input <p>The input to parse</p>
|
||||||
|
* @return <p>The author name, or null if not an author specifier</p>
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String parseAuthorSpecifier(@NotNull String input) {
|
||||||
|
Pattern pattern = Pattern.compile("author([0-9a-zA-Z_]+)");
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,94 @@
|
|||||||
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
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>
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static String repeat(char character, int times) {
|
||||||
|
return String.valueOf(character).repeat(Math.max(0, times));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -7,6 +7,8 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
import org.bukkit.inventory.meta.BookMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inventory helper mainly helps with getting and setting books
|
* The inventory helper mainly helps with getting and setting books
|
||||||
@@ -23,7 +25,8 @@ public final class InventoryHelper {
|
|||||||
* @param signedBook <p>Whether to check for signed or unsigned books</p>
|
* @param signedBook <p>Whether to check for signed or unsigned books</p>
|
||||||
* @return <p>The book the player is holding</p>
|
* @return <p>The book the player is holding</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack getHeldBook(Player player, boolean signedBook) {
|
@NotNull
|
||||||
|
public static ItemStack getHeldBook(@NotNull Player player, boolean signedBook) {
|
||||||
ItemSlot heldSlot = getHeldSlotBook(player, false, false, true, signedBook);
|
ItemSlot heldSlot = getHeldSlotBook(player, false, false, true, signedBook);
|
||||||
if (heldSlot == ItemSlot.MAIN_HAND) {
|
if (heldSlot == ItemSlot.MAIN_HAND) {
|
||||||
return getHeldItem(player, true);
|
return getHeldItem(player, true);
|
||||||
@@ -40,7 +43,7 @@ public final class InventoryHelper {
|
|||||||
* @param player <p>The player holding the book</p>
|
* @param player <p>The player holding the book</p>
|
||||||
* @param newBook <p>The new book the player should hold</p>
|
* @param newBook <p>The new book the player should hold</p>
|
||||||
*/
|
*/
|
||||||
public static void setHeldWrittenBook(Player player, ItemStack newBook) {
|
public static void setHeldWrittenBook(@NotNull Player player, @NotNull ItemStack newBook) {
|
||||||
ItemSlot itemSlot = getHeldSlotBook(player, false, false, true, true);
|
ItemSlot itemSlot = getHeldSlotBook(player, false, false, true, true);
|
||||||
if (itemSlot == ItemSlot.MAIN_HAND) {
|
if (itemSlot == ItemSlot.MAIN_HAND) {
|
||||||
replaceHeldItem(player, newBook, true);
|
replaceHeldItem(player, newBook, true);
|
||||||
@@ -51,6 +54,32 @@ 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(@NotNull Player player, @NotNull String noBookMessage,
|
||||||
|
@NotNull 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
|
||||||
*
|
*
|
||||||
@@ -59,7 +88,8 @@ public final class InventoryHelper {
|
|||||||
* @param twoBooksMessage <p>The message to display if the player is holding one book in each hand</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>
|
* @return <p>False if the player is holding exactly one book</p>
|
||||||
*/
|
*/
|
||||||
public static boolean notHoldingOneWrittenBookCheck(Player player, String noBookMessage, String twoBooksMessage) {
|
public static boolean notHoldingOneWrittenBookCheck(@NotNull Player player, @NotNull String noBookMessage,
|
||||||
|
@NotNull String twoBooksMessage) {
|
||||||
BookHoldingState holdingState = getBookHoldingState(player);
|
BookHoldingState holdingState = getBookHoldingState(player);
|
||||||
|
|
||||||
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
if (holdingState == BookHoldingState.NONE || holdingState == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
||||||
@@ -86,7 +116,8 @@ public final class InventoryHelper {
|
|||||||
* @param writtenBook <p>Whether to search for written or unwritten books, if it's relevant</p>
|
* @param writtenBook <p>Whether to search for written or unwritten books, if it's relevant</p>
|
||||||
* @return <p>The slot of the player's held book</p>
|
* @return <p>The slot of the player's held book</p>
|
||||||
*/
|
*/
|
||||||
public static ItemSlot getHeldSlotBook(Player player, boolean handMatters, boolean mainHand,
|
@NotNull
|
||||||
|
public static ItemSlot getHeldSlotBook(@NotNull Player player, boolean handMatters, boolean mainHand,
|
||||||
boolean typeMatters, boolean writtenBook) {
|
boolean typeMatters, boolean writtenBook) {
|
||||||
BookHoldingState state = getBookHoldingState(player);
|
BookHoldingState state = getBookHoldingState(player);
|
||||||
ItemStack mainHandItem = getHeldItem(player, true);
|
ItemStack mainHandItem = getHeldItem(player, true);
|
||||||
@@ -96,7 +127,7 @@ public final class InventoryHelper {
|
|||||||
if (state == BookHoldingState.SIGNED_BOTH_HANDS ||
|
if (state == BookHoldingState.SIGNED_BOTH_HANDS ||
|
||||||
state == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
state == BookHoldingState.UNSIGNED_BOTH_HANDS ||
|
||||||
state == BookHoldingState.NONE) {
|
state == BookHoldingState.NONE) {
|
||||||
return null;
|
return ItemSlot.NONE;
|
||||||
}
|
}
|
||||||
if (handMatters && typeMatters) {
|
if (handMatters && typeMatters) {
|
||||||
if (mainHand && mainHandItem.getType() == requiredMaterial) {
|
if (mainHand && mainHandItem.getType() == requiredMaterial) {
|
||||||
@@ -143,7 +174,8 @@ public final class InventoryHelper {
|
|||||||
* @param player <p>The player to check</p>
|
* @param player <p>The player to check</p>
|
||||||
* @return <p>The state of the player's book holding</p>
|
* @return <p>The state of the player's book holding</p>
|
||||||
*/
|
*/
|
||||||
private static BookHoldingState getBookHoldingState(Player player) {
|
@NotNull
|
||||||
|
private static BookHoldingState getBookHoldingState(@NotNull Player player) {
|
||||||
ItemStack mainHandItem = getHeldItem(player, true);
|
ItemStack mainHandItem = getHeldItem(player, true);
|
||||||
ItemStack offHandItem = getHeldItem(player, false);
|
ItemStack offHandItem = getHeldItem(player, false);
|
||||||
|
|
||||||
@@ -180,7 +212,8 @@ public final class InventoryHelper {
|
|||||||
* @param mainHand <p>Whether to get information about a book in the player's main hand or off hand</p>
|
* @param mainHand <p>Whether to get information about a book in the player's main hand or off hand</p>
|
||||||
* @return <p>Information about the held book</p>
|
* @return <p>Information about the held book</p>
|
||||||
*/
|
*/
|
||||||
public static BookMeta getHeldBookMetadata(Player player, boolean mainHand) {
|
@Nullable
|
||||||
|
public static BookMeta getHeldBookMetadata(@NotNull Player player, boolean mainHand) {
|
||||||
return (BookMeta) getHeldItem(player, mainHand).getItemMeta();
|
return (BookMeta) getHeldItem(player, mainHand).getItemMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +224,8 @@ public final class InventoryHelper {
|
|||||||
* @param mainHand <p>Whether to get the item in the player's main hand or off hand</p>
|
* @param mainHand <p>Whether to get the item in the player's main hand or off hand</p>
|
||||||
* @return <p>The item the player is holding in the given hand</p>
|
* @return <p>The item the player is holding in the given hand</p>
|
||||||
*/
|
*/
|
||||||
public static ItemStack getHeldItem(Player player, boolean mainHand) {
|
@NotNull
|
||||||
|
public static ItemStack getHeldItem(@NotNull Player player, boolean mainHand) {
|
||||||
if (mainHand) {
|
if (mainHand) {
|
||||||
return player.getInventory().getItemInMainHand();
|
return player.getInventory().getItemInMainHand();
|
||||||
} else {
|
} else {
|
||||||
@@ -206,7 +240,7 @@ public final class InventoryHelper {
|
|||||||
* @param newBook <p>The new book the player should hold</p>
|
* @param newBook <p>The new book the player should hold</p>
|
||||||
* @param mainHand <p>Whether to replace the item in the player's main hand or off hand</p>
|
* @param mainHand <p>Whether to replace the item in the player's main hand or off hand</p>
|
||||||
*/
|
*/
|
||||||
public static void replaceHeldItem(Player player, ItemStack newBook, boolean mainHand) {
|
public static void replaceHeldItem(@NotNull Player player, @NotNull ItemStack newBook, boolean mainHand) {
|
||||||
if (mainHand) {
|
if (mainHand) {
|
||||||
player.getInventory().setItemInMainHand(newBook);
|
player.getInventory().setItemInMainHand(newBook);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,48 +1,16 @@
|
|||||||
package net.knarcraft.bookswithoutborders.utility;
|
package net.knarcraft.bookswithoutborders.utility;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,6 +18,7 @@ public final class TabCompletionHelper {
|
|||||||
*
|
*
|
||||||
* @return <p>A list of booleans</p>
|
* @return <p>A list of booleans</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static List<String> getBooleans() {
|
public static List<String> getBooleans() {
|
||||||
List<String> booleans = new ArrayList<>();
|
List<String> booleans = new ArrayList<>();
|
||||||
booleans.add("true");
|
booleans.add("true");
|
||||||
@@ -64,6 +33,7 @@ public final class TabCompletionHelper {
|
|||||||
* @param end <p>The end number</p>
|
* @param end <p>The end number</p>
|
||||||
* @return <p>A list of numbers</p>
|
* @return <p>A list of numbers</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static List<String> getNumbers(int start, int end) {
|
public static List<String> getNumbers(int start, int end) {
|
||||||
List<String> numbers = new ArrayList<>();
|
List<String> numbers = new ArrayList<>();
|
||||||
for (int i = start; i <= end; i++) {
|
for (int i = start; i <= end; i++) {
|
||||||
@@ -79,6 +49,7 @@ public final class TabCompletionHelper {
|
|||||||
* @param end <p>The end number</p>
|
* @param end <p>The end number</p>
|
||||||
* @return <p>A list of booleans and numbers</p>
|
* @return <p>A list of booleans and numbers</p>
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
public static List<String> getBooleansAndNumbers(int start, int end) {
|
public static List<String> getBooleansAndNumbers(int start, int end) {
|
||||||
List<String> booleansAndNumbers = new ArrayList<>();
|
List<String> booleansAndNumbers = new ArrayList<>();
|
||||||
List<String> booleans = getBooleans();
|
List<String> booleans = getBooleans();
|
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -5,7 +5,7 @@ Options:
|
|||||||
Max_Number_of_Duplicates: 5
|
Max_Number_of_Duplicates: 5
|
||||||
# The separator used to separate the book title and the book author
|
# The separator used to separate the book title and the book author
|
||||||
Title-Author_Separator: ","
|
Title-Author_Separator: ","
|
||||||
# The separator used to denote a new line in the book lore
|
# The separator used to denote a new line in the book/item lore
|
||||||
Lore_line_separator: "~"
|
Lore_line_separator: "~"
|
||||||
# A list of books given to new players the first time they join the server
|
# A list of books given to new players the first time they join the server
|
||||||
Books_for_new_players: [ ]
|
Books_for_new_players: [ ]
|
||||||
@@ -21,6 +21,14 @@ Options:
|
|||||||
Admin_Auto_Decrypt: false
|
Admin_Auto_Decrypt: false
|
||||||
# Whether to only allow the author of a book to create copies
|
# Whether to only allow the author of a book to create copies
|
||||||
Author_Only_Copy: false
|
Author_Only_Copy: false
|
||||||
|
# Whether to only allow the author of a book to unsign it
|
||||||
|
Author_Only_Unsign: false
|
||||||
|
# Whether to only allow saving a player's own books with /savebook
|
||||||
|
Author_Only_Save: false
|
||||||
# Whether to automatically format every book when it's signed
|
# Whether to automatically format every book when it's signed
|
||||||
Format_Book_After_Signing: false
|
Format_Book_After_Signing: false
|
||||||
|
# Whether to display "COPY" or "COPY_OF_COPY" instead of "ORIGINAL" when a book is copied. This also uses the
|
||||||
|
# vanilla behavior where a copy of a copy cannot be copied further.
|
||||||
|
Change_Generation_On_Copy: false
|
||||||
|
# Whether to enable hitting a chiseled bookshelf while sneaking to see the shelf's contents.
|
||||||
|
Enable_Book_Peeking: false
|
@@ -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>
|
||||||
@@ -53,6 +57,10 @@ commands:
|
|||||||
description: Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
description: Encrypts the book the player is holding. "key" is required and can be any phrase or number excluding spaces. "style" is not required. Possible values are "DNA" or ""
|
||||||
usage: /<command> <key> [encryption style]
|
usage: /<command> <key> [encryption style]
|
||||||
permission: bookswithoutborders.encrypt
|
permission: bookswithoutborders.encrypt
|
||||||
|
setbookgeneration:
|
||||||
|
description: Sets the generation of the held book
|
||||||
|
usage: /<command> <generation>
|
||||||
|
permission: bookswithoutborders.setgeneration
|
||||||
setbookprice:
|
setbookprice:
|
||||||
description: Sets the per-book-price to create a book via commands. If "Item", the item in the player's hand in the amount of [quantity] will be the price. If "Eco", a Vault based economy will be used for price. If neither <Item/Eco> or <quantity> are specified, the current price to create books will be removed.
|
description: Sets the per-book-price to create a book via commands. If "Item", the item in the player's hand in the amount of [quantity] will be the price. If "Eco", a Vault based economy will be used for price. If neither <Item/Eco> or <quantity> are specified, the current price to create books will be removed.
|
||||||
usage: /<command> <item/eco> <quantity>
|
usage: /<command> <item/eco> <quantity>
|
||||||
@@ -88,7 +96,11 @@ commands:
|
|||||||
reload:
|
reload:
|
||||||
description: Reloads BwB's configuration file
|
description: Reloads BwB's configuration file
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
permission: bookswithoutborders.admin
|
permission: bookswithoutborders.reload
|
||||||
|
setBookshelfData:
|
||||||
|
description: Sets custom data for a chiseled bookshelf used when peeking at the bookshelf
|
||||||
|
usage: /<command> <delete/name/lore> <text> [more text]
|
||||||
|
permission: bookswithoutborders.editbookshelf
|
||||||
permissions:
|
permissions:
|
||||||
bookswithoutborders.*:
|
bookswithoutborders.*:
|
||||||
description: Grants all permissions
|
description: Grants all permissions
|
||||||
@@ -111,22 +123,30 @@ permissions:
|
|||||||
bookswithoutborders.give: true
|
bookswithoutborders.give: true
|
||||||
bookswithoutborders.givepublic: true
|
bookswithoutborders.givepublic: true
|
||||||
bookswithoutborders.bypassauthoronlycopy: true
|
bookswithoutborders.bypassauthoronlycopy: true
|
||||||
|
bookswithoutborders.bypassauthoronlyunsign: true
|
||||||
|
bookswithoutborders.bypassauthoronlysave: true
|
||||||
bookswithoutborders.bypassbookprice: true
|
bookswithoutborders.bypassbookprice: true
|
||||||
bookswithoutborders.setbookprice: true
|
bookswithoutborders.setbookprice: true
|
||||||
|
bookswithoutborders.reload: true
|
||||||
|
bookswithoutborders.setgeneration: true
|
||||||
|
bookswithoutborders.editbookshelf: true
|
||||||
bookswithoutborders.use:
|
bookswithoutborders.use:
|
||||||
description: Allows player to use commands to save/load/delete in their personal directory
|
description: Allows player to use commands to save/load/delete in their personal directory, and peeking at bookshelves if enabled
|
||||||
children:
|
children:
|
||||||
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
|
||||||
bookswithoutborders.setlore: true
|
bookswithoutborders.setlore: true
|
||||||
bookswithoutborders.format: true
|
bookswithoutborders.format: true
|
||||||
|
bookswithoutborders.setgeneration: true
|
||||||
bookswithoutborders.format:
|
bookswithoutborders.format:
|
||||||
description: Allows a player to format a book
|
description: Allows a player to format a book
|
||||||
bookswithoutborders.save:
|
bookswithoutborders.save:
|
||||||
@@ -136,9 +156,11 @@ permissions:
|
|||||||
bookswithoutborders.delete:
|
bookswithoutborders.delete:
|
||||||
description: Allows player to delete books from their personal directory
|
description: Allows player to delete books from their personal directory
|
||||||
bookswithoutborders.unsign:
|
bookswithoutborders.unsign:
|
||||||
description: Allows player to use unsign command
|
description: Allows player to use the unsign command
|
||||||
bookswithoutborders.copy:
|
bookswithoutborders.copy:
|
||||||
description: Allows player to use copy command
|
description: Allows player to use the copy command
|
||||||
|
bookswithoutborders.clear:
|
||||||
|
description: Allows player to use the clear command
|
||||||
bookswithoutborders.loadpublic:
|
bookswithoutborders.loadpublic:
|
||||||
description: Allows player to load from the public directory
|
description: Allows player to load from the public directory
|
||||||
bookswithoutborders.savepublic:
|
bookswithoutborders.savepublic:
|
||||||
@@ -163,7 +185,19 @@ permissions:
|
|||||||
description: Allows player to set the lore of the currently held item
|
description: Allows player to set the lore of the currently held item
|
||||||
bookswithoutborders.bypassauthoronlycopy:
|
bookswithoutborders.bypassauthoronlycopy:
|
||||||
description: Allows player to ignore Author_Only_Copy config setting
|
description: Allows player to ignore Author_Only_Copy config setting
|
||||||
|
bookswithoutborders.bypassauthoronlyunsign:
|
||||||
|
description: Allows player to ignore Author_Only_Unsign config setting
|
||||||
|
bookswithoutborders.bypassauthoronlysave:
|
||||||
|
description: Allows player to ignore Author_Only_Save config setting
|
||||||
bookswithoutborders.bypassbookprice:
|
bookswithoutborders.bypassbookprice:
|
||||||
description: Allows player to ignore Price_to_create_book config setting
|
description: Allows player to ignore Price_to_create_book config setting
|
||||||
bookswithoutborders.setbookprice:
|
bookswithoutborders.setbookprice:
|
||||||
description: Allows player to set the cost of creating a book
|
description: Allows player to set the cost of creating a book
|
||||||
|
bookswithoutborders.reload:
|
||||||
|
description: Allows player to reload this plugin
|
||||||
|
bookswithoutborders.setgeneration:
|
||||||
|
description: Allows player to change the generation of a book (Original, Copy, Copy of Copy)
|
||||||
|
bookswithoutborders.peekbookshelf:
|
||||||
|
description: Allows player to left-click a bookshelf to see the contents of the shelf
|
||||||
|
bookswithoutborders.editbookshelf:
|
||||||
|
description: Allows player to set name/lore for bookshelves, used for peeking
|
@@ -1,9 +1,10 @@
|
|||||||
package net.knarcraft.bookswithoutborders.encryption;
|
package net.knarcraft.bookswithoutborders.encryption;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
|
|
||||||
public class AESTest {
|
public class AESTest {
|
||||||
|
|
||||||
@@ -15,7 +16,8 @@ public class AESTest {
|
|||||||
AES aes = new AES(AES.generateIV(), AES.generateIV());
|
AES aes = new AES(AES.generateIV(), AES.generateIV());
|
||||||
|
|
||||||
String encrypted = aes.encryptDecryptText(plainText, password, true);
|
String encrypted = aes.encryptDecryptText(plainText, password, true);
|
||||||
Assert.assertNotSame(encrypted, plainText);
|
assertNotSame(encrypted, plainText);
|
||||||
|
assertNotNull(encrypted);
|
||||||
String decrypted = aes.encryptDecryptText(encrypted, password, false);
|
String decrypted = aes.encryptDecryptText(encrypted, password, false);
|
||||||
assertEquals(plainText, decrypted);
|
assertEquals(plainText, decrypted);
|
||||||
}
|
}
|
||||||
|
@@ -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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user