197 Commits

Author SHA1 Message Date
0edb800cd3 Adds broken code for yaml storage
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2025-12-17 14:28:14 +01:00
c5a964337a Cleans permissions, and reduces redundancy in permission checking code
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 14:42:26 +02:00
48b4151038 Updates update check URL
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 03:04:20 +02:00
e30f41071c Restructures events, and adds some missing protections
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 03:01:03 +02:00
d080644364 Moves SimpleVectorOperation to its own class
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 01:45:03 +02:00
061430dfe4 Merge branch 'refs/heads/master' into dev
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 01:38:17 +02:00
0f6c29a203 Bumps version to snapshot for jenkins
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 01:38:05 +02:00
3ad13137bd Updates readme
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2025-09-14 01:35:56 +02:00
e9559a2601 Updates links 2025-09-14 01:29:47 +02:00
766b63d2f3 Merge branch 'refs/heads/master' into dev
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2025-09-14 01:22:49 +02:00
1eeefa4593 Adds distribution management to pom
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2025-09-14 01:21:56 +02:00
8c4d492e07 Adds back the Jenkins file
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2025-09-14 01:08:26 +02:00
01dca5aac8 Fixes formatting 2025-09-14 01:06:58 +02:00
3751ef070c Merge branch 'refs/heads/master' into dev
# Conflicts:
#	src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java
#	src/main/java/net/knarcraft/stargate/portal/Portal.java
2025-09-14 01:06:02 +02:00
407b22fba0 Ignores cancelled block events 2025-09-14 00:55:01 +02:00
bcde89dec4 Updates dependencies, and removes some redundant code 2025-09-14 00:15:19 +02:00
fe8200813f Updates KnarLib, uses improved formatting and removes redundant code 2025-09-13 23:57:05 +02:00
d80907b5fb Adds better default permissions
- Adds three new permission groups for easier setup
- Sets better default permissions
- Updates and prettifies some documentation
- Renames the silent portal option to quiet
2025-07-30 18:58:42 +02:00
e971bd2cf5 Fixes Stargate verification 2025-07-27 20:26:25 +02:00
a861591fec Cleanup and maintenance
Updates Spigot version
Removes MockBukkit and related tests, as it makes updating a hassle
Bumps API version to 1.20
Removes legacy sign updating
2025-07-26 20:21:35 +02:00
932dae0182 Fix #389
Tested on Purpur Version: 1.21.5-2430-603c755 (MC: 1.21.5)
2025-05-10 18:14:18 +02:00
Pheotis
779a8265f5 Preparing for a release. 2024-11-10 18:26:41 -05:00
864eaac940 Removes unnecessary hashmap copying 2024-11-08 18:45:10 +01:00
7dd01995ed Fixes dependency relocations to remove all warnings
Removes unused adventure-platform-bukkit dependency
Explicitly includes bstats-base
Adds and fixes filters and relocations
2024-11-08 16:50:14 +01:00
966fed331f Optimizes portal entrance lookup 2024-11-08 16:09:28 +01:00
534ada874d Implement equalsand hashCode methods for material specifiers 2024-10-09 18:00:45 +02:00
bd12ec138b Revert "Fix validate portals check" 4377a9418f 2024-10-09 17:59:44 +02:00
4377a9418f Fix validate portals check 2024-09-22 21:25:04 +02:00
Pheotis
72258f659d Preparing for a release. 2024-06-01 14:21:18 -04:00
0a4295e8a9 Merge branch 'legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into legacy 2024-06-01 20:13:33 +02:00
f77ead15c4 Uses optional legacy method when getting sign lines 2024-06-01 20:13:20 +02:00
Pheotis
eb34d998f9 Getting ready for a release. 2024-05-28 20:48:18 -04:00
2c98103db0 Adds missing return statement. 2024-05-17 20:33:56 +02:00
85928a23a3 Merge branch 'legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into legacy 2024-05-17 20:30:42 +02:00
97643d8ce0 Adds condition to require exactly one exit point
Note that Legacy didn't have a check because the actual exits were calculated based on the bottom opening blocks in order to center a leaving player. A warning would instead be displayed upon teleportation when a player tried to teleport to a Stargate with no exit.
2024-05-17 20:30:27 +02:00
Pheotis
314995aa14 Merge pull request #337 from stargate-rewritten/legacy-localisation-update
Manual import of crowdin localisations to legacy.
2024-05-13 15:34:43 -04:00
905aec8101 Makes the duplicate dynmap marker creation warning into a debug message 2024-05-13 20:56:54 +02:00
4c024e7536 Updates configuration-related language lists 2024-05-13 19:01:51 +02:00
Pheotis
7a71d5b8c7 Fixed! 2024-05-13 11:30:56 -04:00
Pheotis
c86fa2120a Manually backported rewrite's crowdin. 2024-05-13 11:21:31 -04:00
0bcf65b2f1 Adds geyser and floodgate soft depends 2024-04-28 11:34:45 +02:00
36792d4ddf Removes applyStartupFixes, and adds controlUpdateDelay
Now, all updating of Stargates' control blocks happens through a queue. The amount of ticks between each time the queue is polled from is configurable using controlUpdateDelay
2024-04-23 18:35:41 +02:00
50e7586942 Updates Vault and KnarLib 2024-04-22 15:05:16 +02:00
9db73b7bae Adds an option for disabling startup fixes
Also fixes configuration default values not being used when the option is missing from the configuration file.
2024-04-21 23:10:26 +02:00
6287a5e492 Only fetches name from UUID if necessary
Also fixes some minor warnings
2024-04-21 16:13:57 +02:00
484d4f4cf1 Caches result of getOfflinePlayer in PortalOwner 2024-04-21 15:31:13 +02:00
29bae9d793 Adds more debug output during post load tasks 2024-04-20 19:08:11 +02:00
420e4fc185 Optimizes sign updating 2024-04-20 18:35:31 +02:00
58373f7d0f Adds some conditions to the portals updated during loading 2024-04-20 18:17:41 +02:00
ca52f58129 Fixes a class type problem when spawning a new vehicle 2024-03-19 14:51:55 +01:00
be6e7ec87b Checks the Floodgate API in addition to the Geyser API 2024-03-19 14:28:17 +01:00
Pheotis
5b825b5037 Bump version 2024-03-06 20:44:21 -05:00
209ae91d9b Reverts MockBukkit update, and removes inconsistent JUnit version 2024-03-06 15:24:28 +01:00
dfeeaf6999 Adds proper geyser checking 2024-03-06 14:59:09 +01:00
184cb38cbb Updates MockBukkit, and fixes some test-related issues
Fixes JUnit 5's After being used instead of AfterEach
Fixes a potential NullPointerException when tearing down GateLayoutTest
2024-03-06 14:25:45 +01:00
74708914f3 Expands hit-box for END_GATEWAY for players with Bedrock names (WIP) 2024-03-06 14:02:28 +01:00
c2ab3dd8b2 Fixes the problem with Dynmap disabling Stargate 2024-03-06 13:57:31 +01:00
2746e4f8ee Merge pull request #336 from stargate-rewritten/legacy-improvements
Legacy improvements
2024-02-22 03:49:17 +01:00
c03b3fc6b0 Removes some redundancy in createPortal 2024-02-20 22:48:36 +01:00
9abf60bb31 Reduces some code complexity 2024-02-20 20:48:29 +01:00
cde20e35d5 Uses matcher.find instead of matcher.matches
Also removes some code smells
2024-02-20 19:45:41 +01:00
9abf6db27a Alters the RegEx for replacing the plugin folder path 2024-02-20 19:29:27 +01:00
ee9bc21099 Fixes some code style issues, and attempts to fix test failures 2024-02-20 18:54:04 +01:00
e57aa4097c Adds a Message enum to avoid string keys 2024-02-20 16:18:38 +01:00
a9e5855194 Improves button and material customization
Allows specifying a comma-separated list of materials and tags for a portal's button, open-material, closed-material and border blocks. A random value is used if more than one material is available.0
Uses the supplied button if any, instead of enforcing the specified button material.
Always protects the button against block breaking.
Fixes an incorrect permission result in the previous commit, which caused players stargate access to be inverted.
2024-02-20 15:15:52 +01:00
b4a6ce1a77 Adds nullability annotations among other things
Adds nullability annotations for all methods
Fixes some nullability problems and inconsistencies
Gets rid of RelativeBlockVector's inner class
Changes RelativeBlockVector to a record
Simplifies FromTheEndTeleportation's storage, and makes it into a minimal record
Removes the putStringInList method
Gets rid of some primitive list usage
Fixes some incorrect method accessibility
Removes some redundancy in PortalOption
2024-02-20 12:43:01 +01:00
894a692e7b Fix #334 2024-02-19 22:22:09 +01:00
9154074a18 Removes a TODO that's already completed 2024-02-17 18:26:26 +01:00
c560063c06 Fixes incorrect formatting and unused imports 2024-02-17 18:22:27 +01:00
a4d4864bdd Checks markerAPIInitialized during dynmap initialization 2024-01-20 16:11:07 +01:00
d975666154 Fix failed tests 2024-01-07 18:36:54 +01:00
95058e86a6 Merge branch 'legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into legacy 2024-01-07 18:27:54 +01:00
Pheotis
8d6b233a8f Preparing for legacy release. 2024-01-07 12:13:39 -05:00
e20ddb52ef Update about.md 2023-12-29 16:55:41 +01:00
f657816533 Modify the about command 2023-12-29 12:43:58 +01:00
f37a196dd9 sg debug -> sg 2023-12-28 17:28:59 +01:00
44aadcd225 Implement the debug command 2023-12-28 17:25:13 +01:00
Thorinwasher
7cfec1190e Merge pull request #304 from furplag/legacy
fix exception occur when invalid yaw ( negative value ) given .
2023-12-28 17:11:26 +01:00
furplag
30cba7051c Update DirectionHelper.java
fix exception occur when invalid yaw ( negative value ) given .
2023-12-28 13:18:04 +09:00
e85f133bc2 Uses depositPlayer for negative economy transactions 2023-08-11 14:42:37 +02:00
0dfbcb0b54 Merge branch 'legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into legacy 2023-06-27 13:47:46 +02:00
3fcae6ca45 Tries to fix a stack trace when Dynmap fails to disable itself 2023-06-27 13:47:35 +02:00
Pheotis
902fc0aa4e Updated readme to prep for minor release. 2023-06-17 09:28:12 -04:00
3e8bfbe5f6 Bumped version 2023-06-17 15:26:49 +02:00
6b6e9d007e Fixes compatibility for pre-1.20 spigot 2023-06-17 14:37:05 +02:00
Pheotis
ef2238a535 Update README.md 2023-06-07 16:56:12 -04:00
27fba221d6 Adds per-sign color settings for the new signs 2023-06-07 22:25:41 +02:00
1c06cf2188 Updates depreciated sign API calls 2023-06-07 18:35:22 +02:00
Pheotis
22f4cb78f6 Bumped Version 2023-06-01 19:24:37 -04:00
Pheotis
732cabd040 Update readme for release bump 2023-06-01 19:24:20 -04:00
9d48374d28 Possibly fixes #296 2023-05-31 17:13:13 +02:00
Pheotis
6f88eecb41 Update changelog and bumps version.
(Preparing for minor release)
2023-05-21 08:21:33 -04:00
0d073a7dad Fixes #291 2023-05-21 10:58:42 +02:00
cbc9e85314 Fixes some formatting again 2023-04-21 17:56:10 +02:00
Pheotis
b3428877ba Cleaned up Config & Updated Changelog 2023-04-21 11:49:27 -04:00
Pheotis
30a5efdadf Bumped version, added changelog, fixed readme . 2023-04-21 10:17:08 -04:00
53126beba3 Adds author to StargateYamlConfiguration 2023-04-21 16:15:03 +02:00
4f7bab8a1a Merge pull request #288 from stargate-rewritten/legacy-config-migration
Legacy config migration
2023-04-21 13:11:56 +00:00
9a215a7c11 Attempts to make SonarCloud happier 2023-04-21 15:10:20 +02:00
e07adacf73 Reduces the complexity of convertYAMLMappingsToComments 2023-04-21 14:53:23 +02:00
05fadfa558 Removes stack trace printing 2023-04-21 14:03:33 +02:00
fab067c94b Fixes #287 2023-04-21 13:56:18 +02:00
45f3bcc415 Improves logging a bit 2023-04-21 13:41:42 +02:00
50015c2912 Adds graceful shutdown for CraftBukkit #286 2023-04-21 12:53:43 +02:00
486149fa01 Improves performance of StargateYamlConfiguration a bit 2023-04-20 18:44:46 +02:00
9d981f2cc6 Fixes a crash caused by StargateYamlConfiguration 2023-04-20 16:02:57 +02:00
aa480faef2 Improves behavior if the config cannot be loaded 2023-04-19 23:13:02 +02:00
24b62ec749 Attempts to properly comment migrated config values
Note that for some reason, YamlConfiguration cannot load the output created by StargateYamlConfiguration causing a major crash
2023-04-19 22:17:29 +02:00
032a4df4d7 Overrides saving 2023-04-19 20:51:35 +02:00
c3752db99b Adds a custom configuration to retain comments 2023-04-19 20:03:23 +02:00
17bb27d553 Merge branch 'legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into legacy 2023-04-19 19:00:07 +02:00
6737a4f789 Adds the missing taxAccounts option 2023-04-19 19:00:02 +02:00
Pheotis
b0b31e04fc Removed some potentially confusing abbreviations 2023-04-19 12:40:22 -04:00
2074904aef Improves configuration comments 2023-04-19 18:34:37 +02:00
421e0b17e2 Updates the resource id for the update checker
This change makes the update checker look at https://www.spigotmc.org/resources/stargate.109355/ for updates, instead of the legacy with incorrect version numbering: https://www.spigotmc.org/resources/stargate-old.87978/
2023-04-19 11:06:52 +00:00
Pheotis
e6c92f9322 Fixed maven shade to handle bstats and knarlib. 2023-03-25 17:02:55 -04:00
bd947d5c94 Merge branch 'temp-legacy' of https://github.com/stargate-rewritten/Stargate-Bukkit into temp-legacy 2023-03-25 02:57:38 +01:00
7e46fa413e Relocates BStats, but KnarLib is not properly shaded 2023-03-25 02:57:23 +01:00
Pheotis
77fec1d5de Fixed a mismatch re the increased character limit. 2023-03-24 21:51:09 -04:00
Pheotis
24890a4b5d Fixed a text oversight. 2023-03-24 21:48:50 -04:00
Pheotis
3834ef04d4 Updated the readme to reflect the merger. 2023-03-24 21:45:55 -04:00
a20a1c2f00 Renames the I-flag to the Q-flag 2023-03-25 02:18:28 +01:00
472aeda2f8 Implements BStats statistics 2023-03-25 02:18:05 +01:00
c1720e05a0 Adds a wool gate amongst other things
Adds the wool gate type from legacy
Adds some missing material tag-related code
Updates the URL for update checking
2023-03-25 01:54:43 +01:00
91d855312d Changes the E flag to a V flag for rewrite consistency 2023-03-24 23:52:18 +01:00
10a16000c6 Removes unused frameTypes variable 2023-03-24 23:46:21 +01:00
454bac6f14 Implements support for character tags 2023-03-24 23:44:17 +01:00
f8cddea188 Adds one missing legacy config conversion 2023-03-24 22:48:37 +01:00
06271a356e Updates Vault repository 2023-03-24 21:57:06 +01:00
79bf297513 Updates some dependencies 2023-03-24 21:47:41 +01:00
36f09d5b29 Updates website URL to a shortcut 2023-03-24 21:29:13 +01:00
5a2766c4c6 Updates website URL 2023-03-24 20:58:18 +01:00
b63bd19db0 Sets the Spigot API version to 1.16 2023-03-24 20:56:34 +01:00
2bdcc0da8d Merge remote-tracking branch 'origin/temp-legacy' into temp-legacy
# Conflicts:
#	pom.xml
2023-03-24 20:50:28 +01:00
1f4aeefad1 Bumps version and removes Jenkinsfile
Bumps version to 0.11.5.0-SNAPSHOT for consistency
2023-03-24 20:47:31 +01:00
Pheotis
12a2d4eb71 Merged temp-legacy's versioning with legacy's
This is now a major version ahead of SGR's legacy.
2023-03-24 15:45:15 -04:00
Pheotis
69ae5ea3de Merge remote-tracking branch 'knarvik/master' into temp-legacy 2023-03-24 15:37:03 -04:00
2dcf7b7af0 Merge branch 'master' into vertical-stargates
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2023-03-24 20:18:22 +01:00
af9142bb05 Adds missing known legacy config migrations
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2023-03-24 20:02:05 +01:00
116e816a18 Bumps Spigot version to 1.19.3
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2022-12-10 22:40:03 +01:00
0f0b8b7087 Merge branch 'dev' into vertical-stargates
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2022-11-26 17:02:18 +01:00
e4539c3623 Changes version to 0.9.4.3-SNAPSHOT
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2022-11-26 17:00:04 +01:00
e1ca1fe8b0 Merge branch 'dev' into vertical-stargates
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
# Conflicts:
#	Jenkinsfile
2022-11-26 16:13:40 +01:00
9ac3c11345 Adds missing distribution management
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-11-26 15:34:34 +01:00
13cdccfc1d Improves Jenkinsfile 2022-11-26 15:15:45 +01:00
6954d46af4 Adds auto-deploy
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-11-26 14:52:18 +01:00
a1f25a794e Merge branch 'master' into vertical-stargates
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2022-11-26 14:39:09 +01:00
31b3423246 Adds the knarcraft git repository
All checks were successful
EpicKnarvik97/Stargate/pipeline/head This commit looks good
2022-11-26 04:05:03 +01:00
a35c07dc9c Updates the JDK used for Jenkins
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-11-22 16:41:55 +01:00
5aed252181 Updates dependencies
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-11-14 00:45:31 +00:00
ef97da9177 Removes KnarLib.setPlugin call
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-11-07 22:27:53 +01:00
a26cc30092 Uses placeholder for plugin version 2022-11-07 10:07:16 +01:00
4fda4c3905 Makes sure to initialize KnarLib properly 2022-11-07 10:01:34 +01:00
2b23e6fc56 Uses KnarLib where possible 2022-11-07 09:57:32 +01:00
c09063c49e Updates README and version to 0.9.4.2
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-10-09 10:46:49 +02:00
b5e2565626 Fixes use of depreciated setWoodType and getWoodType 2022-10-09 10:44:30 +02:00
fbabe7b117 Merge branch 'master' into vertical-stargates
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-06-25 23:25:25 +02:00
5e456a1326 Updates some names and comments for SimpleVectorOperation 2022-06-25 23:24:59 +02:00
11d3dc7a92 Increases hitbox of bungee portals using END_PORTAL to fix server change not triggering
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-06-25 12:11:37 +02:00
1c87d803ff Adds a delayed teleport to really force the player respawn for end_portal Stargate 2022-06-25 11:53:52 +02:00
2076fda4d1 Fixes end portals completely hijacking BungeeCord teleportation 2022-06-25 10:54:19 +02:00
524130c4e0 Avoids some potential NullPointerExceptions related to dynmap integration
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-06-24 03:33:49 +02:00
ce5f3ef52f Avoids a NullPointerException if dynmap is present, but isn't loaded properly 2022-06-24 03:23:59 +02:00
cae34d395b Displays bungee markers properly
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-06-18 16:16:36 +02:00
643a48392b Adds Dynmap integration
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
This adds Dynmap integration for displaying Stargates in Dynmap
API version is changed back to 1.18 as 1.19 is still kind of new
Version is now 0.9.4.1
2022-06-18 16:05:05 +02:00
92c3eadf8f Updates Stargate for Spigot 1.19
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-06-17 12:40:15 +02:00
Michael Smith
4bdcd9992b Fix repository URL 2022-03-25 23:48:40 -07:00
92f452df00 Adds LGPL version to the README
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-03-11 17:16:26 +01:00
6eb7649e0d Clarifies license to prevent any disputes 2022-03-11 17:10:10 +01:00
5c2cbaae58 Merge branch 'master' into dev
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
# Conflicts:
#	README.md
#	pom.xml
#	src/main/resources/plugin.yml
2022-02-17 20:17:11 +01:00
0c69dc8991 Adds the Japanese language file provided by spigot user furplag (0.9.3.7) 2022-02-17 20:08:24 +01:00
68bed24137 Merge branch 'master' of https://git.knarcraft.net/EpicKnarvik97/Stargate
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
 Conflicts:
	README.md
	pom.xml
	src/main/resources/plugin.yml
2022-02-15 18:27:25 +01:00
5c1f9036c2 Merge branch 'dev' of https://git.knarcraft.net/EpicKnarvik97/Stargate into dev
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
 Conflicts:
	README.md
	pom.xml
	src/main/resources/plugin.yml
2022-02-15 18:24:38 +01:00
8488c5abdb Fixes some after-merge problems 2022-02-15 18:21:42 +01:00
e3189e9ab2 Merge branch 'master' into dev
# Conflicts:
#	README.md
#	pom.xml
#	src/main/resources/plugin.yml
2022-02-15 18:16:53 +01:00
8c334ff5f0 Adds simplified chinese, courtesy of YKDZ 2022-02-15 18:15:22 +01:00
4dfce3d325 Updates README and version
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-02-09 18:37:04 +01:00
00462799b9 Fixes the default wait for player after teleport delay 2022-02-09 18:28:47 +01:00
9e78e32db4 Fixes some spacing 2022-02-09 17:20:28 +01:00
28bb6f2109 Improves the generation of the list of available chat colors 2022-02-09 17:19:51 +01:00
61b05bcce9 Improves some code 2022-02-09 16:47:37 +01:00
5f4a90aabb Gives all passengers the same exit velocity as the root vehicle 2022-02-09 16:23:45 +01:00
a481ccf017 Adds some debug messages and makes sure to listen to the vehicle teleport event result 2022-02-09 05:54:57 +01:00
28d84450fb Makes boats keep their boat type when teleported using the CraftBook fix
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-02-08 19:00:44 +01:00
99bceb9165 Updates some test dependencies 2022-02-08 18:44:44 +01:00
a521454020 Updates README and version to 0.9.3.4 2022-02-08 18:43:04 +01:00
6c32de59a8 Makes the delay to prevent client errors configurable 2022-02-08 18:31:26 +01:00
497551d889 Makes the CraftBook remove on eject fix optional 2022-02-08 18:10:02 +01:00
8643a44df6 Moves some code to TeleportHelper 2022-02-08 16:24:47 +01:00
e7b711efcf Makes sure to stop polling the block change thread once it's empty instead of continuing doing nothing 2022-02-07 22:51:09 +01:00
56d59f0d2a Makes sure to check player passengers from the root entity
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-29 00:30:26 +01:00
248caee620 Fixes some minor formatting issues 2022-01-28 23:23:33 +01:00
7f08763624 Changes a lot of teleportation code to be able to teleport entities with passengers
Moves some teleportation code to the TeleportHelper class
Changes some teleportation method names
Tries to reduce some code duplication between teleporters
Recursively teleports entities to make sure the teleported entity will arrive in the same shape it entered as
Makes player checks for teleported vehicles account for any number of players riding any entity in the stack
2022-01-28 23:23:09 +01:00
95293204d5 Adds a new StargateTeleportEvent to combine some duplicated code 2022-01-28 22:58:02 +01:00
8a5c094ce1 Updates README with changes and version to 0.9.3.3
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-28 00:13:32 +01:00
5e79df9f44 Adds an entity spawn listener for preventing Zombified Piglin spawns 2022-01-28 00:13:00 +01:00
99ee5c6978 Adds the SimpleVectorOperation class which is capable of rotating a RelativeBlockVector in 6 directions 2022-01-27 04:28:36 +01:00
132 changed files with 7007 additions and 5130 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
target/ target/
.idea/ .idea/
*.secret
*.db
nbactions.xml
stargate.iml

19
HEADER Normal file
View File

@@ -0,0 +1,19 @@
Stargate - A portal plugin for Bukkit
Copyright (C) 2011 Shaun (sturmeh)
Copyright (C) 2011 Dinnerbone
Copyright (C) 2011-2013 Steven "Drakia" Scott <Contact@TheDgtl.net>
Copyright (C) 2015-2020 Michael Smith (PseudoKnight)
Copyright (C) 2021-2022 Kristian Knarvik (EpicKnarvik97)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

10
Jenkinsfile vendored
View File

@@ -1,7 +1,7 @@
pipeline { pipeline {
agent any agent any
tools { tools {
jdk 'JDK16' jdk 'JDK17'
} }
stages { stages {
stage('Build') { stage('Build') {
@@ -16,10 +16,16 @@ pipeline {
sh 'mvn test' sh 'mvn test'
} }
} }
stage('Verify') {
steps {
echo 'Verifying...'
sh 'mvn verify -Dmaven.test.skip=true'
}
}
stage('Deploy') { stage('Deploy') {
steps { steps {
echo 'Deploying...' echo 'Deploying...'
sh 'mvn verify -Dmaven.test.skip=true' sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
} }
} }

674
LICENSE
View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

916
README.md

File diff suppressed because it is too large Load Diff

185
pom.xml
View File

@@ -4,7 +4,7 @@
<groupId>net.knarcraft</groupId> <groupId>net.knarcraft</groupId>
<artifactId>Stargate</artifactId> <artifactId>Stargate</artifactId>
<version>0.9.3.2</version> <version>0.11.5.12-SNAPSHOT</version>
<licenses> <licenses>
<license> <license>
@@ -15,70 +15,197 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source> <java.version>16</java.version>
<maven.compiler.target>17</maven.compiler.target>
</properties> </properties>
<repositories> <repositories>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<repository> <repository>
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url> <url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository> </repository>
<repository> <repository>
<id>vault-repo</id> <id>jitpack.io</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url> <url>https://jitpack.io</url>
</repository>
<repository>
<id>dynmap</id>
<url>https://repo.mikeprimm.com/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>minebench-repo</id>
<url>https://repo.minebench.de/</url>
</repository>
<repository>
<id>opencollab-snapshot</id>
<url>https://repo.opencollab.dev/main/</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>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.milkbowl.vault</groupId> <groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId> <artifactId>VaultAPI</artifactId>
<version>1.7</version> <version>1.7.1</version>
</dependency> <scope>provided</scope>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.0-M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.18</artifactId>
<version>1.14.0</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>19.0.0</version> <version>26.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>3.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.2.18</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>org.bstats</groupId>
<artifactId>junit</artifactId> <artifactId>bstats-bukkit</artifactId>
<version>4.13.1</version> <version>3.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-base</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>de.themoep</groupId>
<artifactId>minedown</artifactId>
<version>1.7.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>2.2.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.2.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>net.knarcraft.stargate.lib.metrics</shadedPattern>
</relocation>
<relocation>
<pattern>net.knarcraft.knarlib</pattern>
<shadedPattern>net.knarcraft.stargate.lib.knarlib</shadedPattern>
</relocation>
<relocation>
<pattern>de.themoep</pattern>
<shadedPattern>net.knarcraft.stargate.lib.minedown</shadedPattern>
</relocation>
<relocation>
<pattern>org.jetbrains.annotations</pattern>
<shadedPattern>net.knarcraft.blacksmith.lib.annotations</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>de.themoep:minedown</artifact>
<includes>
<include>de/themoep/minedown/**</include>
</includes>
</filter>
<filter>
<artifact>org.bstats</artifact>
<includes>
<include>org/bstats/**</include>
</includes>
</filter>
<filter>
<artifact>net.knarcraft:knarlib</artifact>
<includes>
<include>net/knarcraft/knarlib/**</include>
</includes>
</filter>
<filter>
<artifact>org.jetbrains:annotations</artifact>
<includes>
<include>org/jetbrains/annotations/**</include>
</includes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build> </build>
</project> </project>

View File

@@ -1,35 +1,39 @@
package net.knarcraft.stargate; package net.knarcraft.stargate;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.Translator;
import net.knarcraft.knarlib.plugin.ConfigCommentPlugin;
import net.knarcraft.knarlib.util.ConfigHelper;
import net.knarcraft.knarlib.util.UpdateChecker;
import net.knarcraft.stargate.command.CommandStarGate; import net.knarcraft.stargate.command.CommandStarGate;
import net.knarcraft.stargate.command.StarGateTabCompleter; import net.knarcraft.stargate.command.StarGateTabCompleter;
import net.knarcraft.stargate.config.EconomyConfig;
import net.knarcraft.stargate.config.MessageSender;
import net.knarcraft.stargate.config.StargateConfig; import net.knarcraft.stargate.config.StargateConfig;
import net.knarcraft.stargate.config.StargateGateConfig; import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.config.addons.EconomyConfig;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.listener.BlockEventListener; import net.knarcraft.stargate.container.ControlBlockUpdateRequest;
import net.knarcraft.stargate.listener.EntityEventListener; import net.knarcraft.stargate.listener.EnvironmentChangeListener;
import net.knarcraft.stargate.listener.PlayerEventListener; import net.knarcraft.stargate.listener.PlayerEventListener;
import net.knarcraft.stargate.listener.PluginEventListener; import net.knarcraft.stargate.listener.StargateBreakListener;
import net.knarcraft.stargate.listener.PortalEventListener; import net.knarcraft.stargate.listener.StargateCreateDestroyListener;
import net.knarcraft.stargate.listener.TeleportEventListener; import net.knarcraft.stargate.listener.StargateTeleportListener;
import net.knarcraft.stargate.listener.VehicleEventListener;
import net.knarcraft.stargate.listener.WorldEventListener;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.thread.BlockChangeThread; import net.knarcraft.stargate.thread.BlockChangeThread;
import net.knarcraft.stargate.thread.ChunkUnloadThread; import net.knarcraft.stargate.thread.ChunkUnloadThread;
import net.knarcraft.stargate.thread.ControlBlocksUpdateThread;
import net.knarcraft.stargate.thread.StarGateThread; import net.knarcraft.stargate.thread.StarGateThread;
import net.knarcraft.stargate.utility.UpdateChecker; import net.knarcraft.stargate.utility.BStatsHelper;
import org.bukkit.Server; import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.command.PluginCommand; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
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.JavaPluginLoader; import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.util.LinkedList; import java.util.LinkedList;
@@ -38,24 +42,46 @@ import java.util.Queue;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/*
Stargate - A portal plugin for Bukkit
Copyright (C) 2011 Shaun (sturmeh)
Copyright (C) 2011 Dinnerbone
Copyright (C) 2011-2013 Steven "Drakia" Scott <Contact@TheDgtl.net>
Copyright (C) 2015-2020 Michael Smith (PseudoKnight)
Copyright (C) 2021-2025 Kristian Knarvik (EpicKnarvik97)
The following license notice applies to all source and resource files in the Stargate project:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** /**
* The main class of the Stargate plugin * The main class of the Stargate plugin
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class Stargate extends JavaPlugin { public class Stargate extends ConfigCommentPlugin {
//Used for changing gate open/closed material. private static final String CONFIG_FILE_NAME = "config.yml";
private static final Queue<BlockChangeRequest> blockChangeRequestQueue = new LinkedList<>(); private static final Queue<BlockChangeRequest> controlBlockUpdateRequestQueue = new LinkedList<>();
private static final Queue<ControlBlockUpdateRequest> CONTROL_BLOCK_UPDATE_REQUEST_QUEUE = new LinkedList<>();
private static final Queue<ChunkUnloadRequest> chunkUnloadQueue = new PriorityQueue<>(); private static final Queue<ChunkUnloadRequest> chunkUnloadQueue = new PriorityQueue<>();
private static Logger logger; private static Logger logger;
private static Stargate stargate; private static Stargate stargate;
private static String pluginVersion; private static String pluginVersion;
private static PluginManager pluginManager; private static PluginManager pluginManager;
private static StargateConfig stargateConfig; private static StargateConfig stargateConfig;
private static String updateAvailable = null; private static String updateAvailable = null;
/** /**
@@ -84,7 +110,7 @@ public class Stargate extends JavaPlugin {
* *
* @param version <p>The version of the new update available</p> * @param version <p>The version of the new update available</p>
*/ */
public static void setUpdateAvailable(String version) { public static void setUpdateAvailable(@NotNull String version) {
updateAvailable = version; updateAvailable = version;
} }
@@ -93,6 +119,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The version number if an update is available. Null otherwise</p> * @return <p>The version number if an update is available. Null otherwise</p>
*/ */
@Nullable
public static String getUpdateAvailable() { public static String getUpdateAvailable() {
return updateAvailable; return updateAvailable;
} }
@@ -102,6 +129,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>An instance of this plugin, or null if not instantiated</p> * @return <p>An instance of this plugin, or null if not instantiated</p>
*/ */
@NotNull
public static Stargate getInstance() { public static Stargate getInstance() {
return stargate; return stargate;
} }
@@ -111,28 +139,41 @@ public class Stargate extends JavaPlugin {
* *
* @param request <p>The request to add</p> * @param request <p>The request to add</p>
*/ */
public static void addBlockChangeRequest(BlockChangeRequest request) { public static void addControlBlockUpdateRequest(@Nullable BlockChangeRequest request) {
if (request != null) { if (request != null) {
blockChangeRequestQueue.add(request); controlBlockUpdateRequestQueue.add(request);
} }
} }
/** /**
* Gets the queue containing block change requests * Gets the queue containing control block update requests
* *
* @return <p>A block change request queue</p> * @return <p>A control block update request queue</p>
*/ */
public static Queue<BlockChangeRequest> getBlockChangeRequestQueue() { @NotNull
return blockChangeRequestQueue; public static Queue<BlockChangeRequest> getControlBlockUpdateRequestQueue() {
return controlBlockUpdateRequestQueue;
} }
/** /**
* Gets the sender for sending messages to players * Adds a control block update request to the request queue
* *
* @return <p>The sender for sending messages to players</p> * @param request <p>The request to add</p>
*/ */
public static MessageSender getMessageSender() { public static void addControlBlockUpdateRequest(@Nullable ControlBlockUpdateRequest request) {
return stargateConfig.getMessageSender(); if (request != null) {
CONTROL_BLOCK_UPDATE_REQUEST_QUEUE.add(request);
}
}
/**
* Gets the queue containing button update requests
*
* @return <p>A button update request queue</p>
*/
@NotNull
public static Queue<ControlBlockUpdateRequest> getButtonUpdateRequestQueue() {
return CONTROL_BLOCK_UPDATE_REQUEST_QUEUE;
} }
/** /**
@@ -140,6 +181,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The object containing gate configuration values</p> * @return <p>The object containing gate configuration values</p>
*/ */
@NotNull
public static StargateGateConfig getGateConfig() { public static StargateGateConfig getGateConfig() {
return stargateConfig.getStargateGateConfig(); return stargateConfig.getStargateGateConfig();
} }
@@ -149,6 +191,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>This plugin's version</p> * @return <p>This plugin's version</p>
*/ */
@NotNull
public static String getPluginVersion() { public static String getPluginVersion() {
return pluginVersion; return pluginVersion;
} }
@@ -158,6 +201,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The logger</p> * @return <p>The logger</p>
*/ */
@NotNull
public static Logger getConsoleLogger() { public static Logger getConsoleLogger() {
return logger; return logger;
} }
@@ -178,8 +222,8 @@ public class Stargate extends JavaPlugin {
* @param route <p>The class name/route where something happened</p> * @param route <p>The class name/route where something happened</p>
* @param message <p>A message describing what happened</p> * @param message <p>A message describing what happened</p>
*/ */
public static void debug(String route, String message) { public static void debug(@NotNull String route, @NotNull String message) {
if (stargateConfig == null || stargateConfig.isDebuggingEnabled()) { if (stargateConfig == null || stargateConfig.isNotLoaded() || stargateConfig.isDebuggingEnabled()) {
logger.info("[Stargate::" + route + "] " + message); logger.info("[Stargate::" + route + "] " + message);
} else { } else {
logger.log(Level.FINEST, "[Stargate::" + route + "] " + message); logger.log(Level.FINEST, "[Stargate::" + route + "] " + message);
@@ -191,8 +235,8 @@ public class Stargate extends JavaPlugin {
* *
* @param message <p>The message to log</p> * @param message <p>The message to log</p>
*/ */
public static void logInfo(String message) { public static void logInfo(@NotNull String message) {
logger.info(getBackupString("prefix") + message); log(Level.INFO, message);
} }
/** /**
@@ -200,7 +244,7 @@ public class Stargate extends JavaPlugin {
* *
* @param message <p>The message to log</p> * @param message <p>The message to log</p>
*/ */
public static void logSevere(String message) { public static void logSevere(@NotNull String message) {
log(Level.SEVERE, message); log(Level.SEVERE, message);
} }
@@ -209,7 +253,7 @@ public class Stargate extends JavaPlugin {
* *
* @param message <p>The message to log</p> * @param message <p>The message to log</p>
*/ */
public static void logWarning(String message) { public static void logWarning(@NotNull String message) {
log(Level.WARNING, message); log(Level.WARNING, message);
} }
@@ -219,8 +263,11 @@ public class Stargate extends JavaPlugin {
* @param severity <p>The severity of the event triggering the message</p> * @param severity <p>The severity of the event triggering the message</p>
* @param message <p>The message to log</p> * @param message <p>The message to log</p>
*/ */
private static void log(Level severity, String message) { private static void log(@NotNull Level severity, @NotNull String message) {
logger.log(severity, getBackupString("prefix") + message); if (logger == null) {
logger = Bukkit.getLogger();
}
logger.log(severity, message);
} }
/** /**
@@ -230,6 +277,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The folder for storing the portal database</p> * @return <p>The folder for storing the portal database</p>
*/ */
@NotNull
public static String getPortalFolder() { public static String getPortalFolder() {
return stargateConfig.getPortalFolder(); return stargateConfig.getPortalFolder();
} }
@@ -241,6 +289,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The folder storing gate files</p> * @return <p>The folder storing gate files</p>
*/ */
@NotNull
public static String getGateFolder() { public static String getGateFolder() {
return stargateConfig.getGateFolder(); return stargateConfig.getGateFolder();
} }
@@ -250,69 +299,27 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The default network</p> * @return <p>The default network</p>
*/ */
@NotNull
public static String getDefaultNetwork() { public static String getDefaultNetwork() {
return stargateConfig.getStargateGateConfig().getDefaultPortalNetwork(); return stargateConfig.getStargateGateConfig().getDefaultPortalNetwork();
} }
/** /**
* Gets a translated string given its string key * Gets a backup string given its message key
*
* <p>The name/key is the string before the equals sign in the language files</p>
*
* @param name <p>The name/key of the string to get</p>
* @return <p>The full translated string</p>
*/
public static String getString(String name) {
return stargateConfig.getLanguageLoader().getString(name);
}
/**
* Gets a backup string given its string key
*
* <p>The name/key is the string before the equals sign in the language files</p>
* *
* @param name <p>The name/key of the string to get</p> * @param name <p>The name/key of the string to get</p>
* @return <p>The full string in the backup language (English)</p> * @return <p>The full string in the backup language (English)</p>
*/ */
public static String getBackupString(String name) { public static @NotNull String getBackupString(@NotNull Message name) {
return stargateConfig.getLanguageLoader().getBackupString(name); return stargateConfig.getLanguageLoader().getBackupString(name);
} }
/**
* Replaces a list of variables in a string in the order they are given
*
* @param input <p>The input containing the variables</p>
* @param search <p>The variables to replace</p>
* @param values <p>The replacement values</p>
* @return <p>The input string with the search values replaced with the given values</p>
*/
public static String replaceVars(String input, String[] search, String[] values) {
if (search.length != values.length) {
throw new IllegalArgumentException("The number of search values and replace values do not match.");
}
for (int i = 0; i < search.length; i++) {
input = replaceVars(input, search[i], values[i]);
}
return input;
}
/**
* Replaces a variable in a string
*
* @param input <p>The input containing the variables</p>
* @param search <p>The variable to replace</p>
* @param value <p>The replacement value</p>
* @return <p>The input string with the search replaced with value</p>
*/
public static String replaceVars(String input, String search, String value) {
return input.replace(search, value);
}
/** /**
* Gets this plugin's plugin manager * Gets this plugin's plugin manager
* *
* @return <p>A plugin manager</p> * @return <p>A plugin manager</p>
*/ */
@NotNull
public static PluginManager getPluginManager() { public static PluginManager getPluginManager() {
return pluginManager; return pluginManager;
} }
@@ -322,32 +329,43 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The object containing economy config values</p> * @return <p>The object containing economy config values</p>
*/ */
@NotNull
public static EconomyConfig getEconomyConfig() { public static EconomyConfig getEconomyConfig() {
return stargateConfig.getEconomyConfig(); return stargateConfig.getEconomyConfig();
} }
@Override @Override
public void onDisable() { public void onDisable() {
PortalHandler.closeAllPortals(); PortalUtil.closeAllPortals();
PortalRegistry.clearPortals(); PortalRegistry.clearPortals();
stargateConfig.clearManagedWorlds(); if (stargateConfig != null) {
stargateConfig.clearManagedWorlds();
}
getServer().getScheduler().cancelTasks(this); getServer().getScheduler().cancelTasks(this);
} }
@Override @Override
public void onEnable() { public void onEnable() {
Stargate.stargate = this;
Stargate.logger = getLogger();
ConfigHelper.saveDefaults(this);
PluginDescriptionFile pluginDescriptionFile = this.getDescription(); PluginDescriptionFile pluginDescriptionFile = this.getDescription();
pluginManager = getServer().getPluginManager(); pluginManager = getServer().getPluginManager();
FileConfiguration newConfig = this.getConfig();
this.saveDefaultConfig();
newConfig.options().copyDefaults(true);
logger = Logger.getLogger("Minecraft"); // Set temporary string formatter before strings are loaded
Server server = getServer(); SGFormatBuilder.setStringFormatter(new StringFormatter(this.getDescription().getName(), new Translator()));
stargate = this;
stargateConfig = new StargateConfig(logger); try {
stargateConfig.finishSetup(); stargateConfig = new StargateConfig(logger);
stargateConfig.finishSetup();
} catch (NoClassDefFoundError exception) {
logSevere("Could not properly load. Class not found: " +
exception.getMessage() + "\nThis is probably because you are using CraftBukkit, or other outdated" +
"Minecraft server software. Minecraft server software based on Spigot or Paper is required. Paper" +
" is recommended, and can be downloaded at: https://papermc.io/downloads/paper");
this.onDisable();
return;
}
pluginVersion = pluginDescriptionFile.getVersion(); pluginVersion = pluginDescriptionFile.getVersion();
@@ -359,10 +377,13 @@ public class Stargate extends JavaPlugin {
//Run necessary threads //Run necessary threads
runThreads(); runThreads();
this.registerCommands(); registerCommand("stargate", new CommandStarGate(this), new StarGateTabCompleter());
//Check for any available updates //Check for any available updates
UpdateChecker.checkForUpdate(); UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=97784",
Stargate::getPluginVersion, Stargate::setUpdateAvailable);
BStatsHelper.initialize(this);
} }
/** /**
@@ -372,6 +393,8 @@ public class Stargate extends JavaPlugin {
BukkitScheduler scheduler = getServer().getScheduler(); BukkitScheduler scheduler = getServer().getScheduler();
scheduler.runTaskTimer(this, new StarGateThread(), 0L, 100L); scheduler.runTaskTimer(this, new StarGateThread(), 0L, 100L);
scheduler.runTaskTimer(this, new BlockChangeThread(), 0L, 1L); scheduler.runTaskTimer(this, new BlockChangeThread(), 0L, 1L);
scheduler.runTaskTimer(this, new ControlBlocksUpdateThread(), 0L,
getStargateConfig().getStargateGateConfig().controlUpdateDelay());
scheduler.runTaskTimer(this, new ChunkUnloadThread(), 0L, 100L); scheduler.runTaskTimer(this, new ChunkUnloadThread(), 0L, 100L);
} }
@@ -380,25 +403,10 @@ public class Stargate extends JavaPlugin {
*/ */
private void registerEventListeners() { private void registerEventListeners() {
pluginManager.registerEvents(new PlayerEventListener(), this); pluginManager.registerEvents(new PlayerEventListener(), this);
pluginManager.registerEvents(new BlockEventListener(), this); pluginManager.registerEvents(new StargateCreateDestroyListener(), this);
pluginManager.registerEvents(new StargateTeleportListener(), this);
pluginManager.registerEvents(new VehicleEventListener(), this); pluginManager.registerEvents(new EnvironmentChangeListener(this), this);
pluginManager.registerEvents(new EntityEventListener(), this); pluginManager.registerEvents(new StargateBreakListener(), this);
pluginManager.registerEvents(new PortalEventListener(), this);
pluginManager.registerEvents(new WorldEventListener(), this);
pluginManager.registerEvents(new PluginEventListener(this), this);
pluginManager.registerEvents(new TeleportEventListener(), this);
}
/**
* Registers a command for this plugin
*/
private void registerCommands() {
PluginCommand stargateCommand = this.getCommand("stargate");
if (stargateCommand != null) {
stargateCommand.setExecutor(new CommandStarGate());
stargateCommand.setTabCompleter(new StarGateTabCompleter());
}
} }
/** /**
@@ -406,6 +414,7 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The chunk unload queue</p> * @return <p>The chunk unload queue</p>
*/ */
@NotNull
public static Queue<ChunkUnloadRequest> getChunkUnloadQueue() { public static Queue<ChunkUnloadRequest> getChunkUnloadQueue() {
return chunkUnloadQueue; return chunkUnloadQueue;
} }
@@ -415,7 +424,7 @@ public class Stargate extends JavaPlugin {
* *
* @param request <p>The new chunk unload request to add</p> * @param request <p>The new chunk unload request to add</p>
*/ */
public static void addChunkUnloadRequest(ChunkUnloadRequest request) { public static void addChunkUnloadRequest(@NotNull ChunkUnloadRequest request) {
chunkUnloadQueue.removeIf((item) -> item.getChunkToUnload().equals(request.getChunkToUnload())); chunkUnloadQueue.removeIf((item) -> item.getChunkToUnload().equals(request.getChunkToUnload()));
chunkUnloadQueue.add(request); chunkUnloadQueue.add(request);
} }
@@ -425,7 +434,9 @@ public class Stargate extends JavaPlugin {
* *
* @return <p>The stargate configuration</p> * @return <p>The stargate configuration</p>
*/ */
@NotNull
public static StargateConfig getStargateConfig() { public static StargateConfig getStargateConfig() {
return stargateConfig; return stargateConfig;
} }
} }

View File

@@ -1,12 +1,20 @@
package net.knarcraft.stargate.command; package net.knarcraft.stargate.command;
import de.themoep.minedown.MineDown;
import net.knarcraft.knarlib.util.FileHelper;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/** /**
* This command represents the plugin's about command * This command represents the plugin's about command
*/ */
@@ -18,13 +26,22 @@ public class CommandAbout implements CommandExecutor {
ChatColor textColor = ChatColor.GOLD; ChatColor textColor = ChatColor.GOLD;
ChatColor highlightColor = ChatColor.GREEN; ChatColor highlightColor = ChatColor.GREEN;
commandSender.sendMessage(textColor + "Stargate Plugin originally created by " + highlightColor +
"Drakia" + textColor + ", and revived by " + highlightColor + "EpicKnarvik97");
commandSender.sendMessage(textColor + "Go to " + highlightColor + try (InputStream inputStream = FileHelper.getInputStreamForInternalFile("/messages/about.md")) {
"https://git.knarcraft.net/EpicKnarvik97/Stargate " + textColor + "for the official repository"); if (inputStream != null) {
String author = Stargate.getStargateConfig().getLanguageLoader().getString("author"); List<String> lines = FileHelper.readLines(FileHelper.getBufferedReaderFromInputStream(inputStream));
if (!author.isEmpty()) String aboutMessageString = String.join("\n", lines);
BaseComponent[] component = MineDown.parse(aboutMessageString);
commandSender.spigot().sendMessage(component);
}
} catch (IOException ioException) {
commandSender.sendMessage("Internal error");
}
String author = Stargate.getStargateConfig().getLanguageLoader().getString(Message.AUTHOR);
if (!author.isEmpty()) {
commandSender.sendMessage(textColor + "Language created by " + highlightColor + author); commandSender.sendMessage(textColor + "Language created by " + highlightColor + author);
}
return true; return true;
} }

View File

@@ -4,11 +4,15 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.ConfigOption; import net.knarcraft.stargate.config.ConfigOption;
import net.knarcraft.stargate.config.ConfigTag; import net.knarcraft.stargate.config.ConfigTag;
import net.knarcraft.stargate.config.OptionDataType; import net.knarcraft.stargate.config.OptionDataType;
import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.config.addons.DynmapManager;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.PortalSignDrawer; import net.knarcraft.stargate.portal.PortalSignDrawer;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@@ -16,6 +20,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
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;
@@ -29,8 +34,8 @@ public class CommandConfig implements CommandExecutor {
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) { @NotNull String[] args) {
if (commandSender instanceof Player player) { if (commandSender instanceof Player player) {
if (!player.hasPermission("stargate.admin.config")) { if (!PermissionHelper.hasPermission(player, Permission.CONFIG)) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied"); new SGFormatBuilder("Permission Denied").error(commandSender);
return true; return true;
} }
} }
@@ -65,7 +70,8 @@ public class CommandConfig implements CommandExecutor {
* @param commandSender <p>The command sender that changed the value</p> * @param commandSender <p>The command sender that changed the value</p>
* @param value <p>The new value of the config option</p> * @param value <p>The new value of the config option</p>
*/ */
private void updateConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) { private void updateConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender,
@NotNull String value) {
FileConfiguration configuration = Stargate.getInstance().getConfig(); FileConfiguration configuration = Stargate.getInstance().getConfig();
//Validate any sign colors //Validate any sign colors
@@ -119,7 +125,8 @@ public class CommandConfig implements CommandExecutor {
* @param value <p>The new value of the config option</p> * @param value <p>The new value of the config option</p>
* @param configuration <p>The configuration file to save to</p> * @param configuration <p>The configuration file to save to</p>
*/ */
private void updateBooleanConfigValue(ConfigOption selectedOption, String value, FileConfiguration configuration) { private void updateBooleanConfigValue(@NotNull ConfigOption selectedOption, @NotNull String value,
@NotNull FileConfiguration configuration) {
boolean newValue = Boolean.parseBoolean(value); boolean newValue = Boolean.parseBoolean(value);
if (selectedOption == ConfigOption.ENABLE_BUNGEE && newValue != Stargate.getGateConfig().enableBungee()) { if (selectedOption == ConfigOption.ENABLE_BUNGEE && newValue != Stargate.getGateConfig().enableBungee()) {
Stargate.getStargateConfig().startStopBungeeListener(newValue); Stargate.getStargateConfig().startStopBungeeListener(newValue);
@@ -135,7 +142,8 @@ public class CommandConfig implements CommandExecutor {
* @param commandSender <p>The command sender that changed the value</p> * @param commandSender <p>The command sender that changed the value</p>
* @param value <p>The new value of the config option</p> * @param value <p>The new value of the config option</p>
*/ */
private void updateStringConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) { private void updateStringConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender,
@NotNull String value) {
if (selectedOption == ConfigOption.GATE_FOLDER || selectedOption == ConfigOption.PORTAL_FOLDER || if (selectedOption == ConfigOption.GATE_FOLDER || selectedOption == ConfigOption.PORTAL_FOLDER ||
selectedOption == ConfigOption.DEFAULT_GATE_NETWORK) { selectedOption == ConfigOption.DEFAULT_GATE_NETWORK) {
if (value.contains("../") || value.contains("..\\")) { if (value.contains("../") || value.contains("..\\")) {
@@ -161,13 +169,13 @@ public class CommandConfig implements CommandExecutor {
* @param commandSender <p>The command sender that changed the value</p> * @param commandSender <p>The command sender that changed the value</p>
* @param arguments <p>The arguments for the new config option</p> * @param arguments <p>The arguments for the new config option</p>
*/ */
private void updateListConfigValue(ConfigOption selectedOption, CommandSender commandSender, String[] arguments) { private void updateListConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender,
@NotNull String[] arguments) {
FileConfiguration configuration = Stargate.getInstance().getConfig(); FileConfiguration configuration = Stargate.getInstance().getConfig();
if (selectedOption == ConfigOption.PER_SIGN_COLORS) { if (selectedOption == ConfigOption.PER_SIGN_COLORS) {
if (arguments.length < 4) { if (arguments.length < 4) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Usage: /sg config perSignColors " + new SGFormatBuilder("Usage: /sg config perSignColors <SIGN_TYPE> <MAIN_COLOR> <HIGHLIGHTING_COLOR>").error(commandSender);
"<SIGN_TYPE> <MAIN_COLOR> <HIGHLIGHTING_COLOR>");
return; return;
} }
@@ -190,22 +198,24 @@ public class CommandConfig implements CommandExecutor {
* @param arguments <p>The arguments given by the user</p> * @param arguments <p>The arguments given by the user</p>
* @return <p>The per-sign color string to update with, or null if the input was invalid</p> * @return <p>The per-sign color string to update with, or null if the input was invalid</p>
*/ */
private String parsePerSignColorInput(CommandSender commandSender, String[] arguments) { @Nullable
private String parsePerSignColorInput(@NotNull CommandSender commandSender, @NotNull String[] arguments) {
//Make sure the sign type is an actual sign //Make sure the sign type is an actual sign
if (Material.matchMaterial(arguments[1] + "_SIGN") == null) { if (Material.matchMaterial(arguments[1] + "_SIGN") == null) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "The given sign type is invalid"); new SGFormatBuilder("The given sign type is invalid").error(commandSender);
return null; return null;
} }
String colorString = arguments[1] + ":"; String colorString = arguments[1] + ":";
//Validate the colors given by the user //Validate the colors given by the user
String[] errorMessage = new String[]{"The given main sign color is invalid!", "The given highlight sign color is invalid!"}; String[] errorMessage = new String[]{"The given main sign color is invalid!", "The given highlight sign color " +
"is invalid!"};
String[] newColors = new String[2]; String[] newColors = new String[2];
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
if (validatePerSignColor(arguments[i + 2])) { if (validatePerSignColor(arguments[i + 2])) {
newColors[i] = arguments[i + 2]; newColors[i] = arguments[i + 2];
} else { } else {
Stargate.getMessageSender().sendErrorMessage(commandSender, errorMessage[i]); new SGFormatBuilder(errorMessage[i]).error(commandSender);
return null; return null;
} }
} }
@@ -220,9 +230,11 @@ public class CommandConfig implements CommandExecutor {
* @param colorString <p>The new color string to replace any previous value with</p> * @param colorString <p>The new color string to replace any previous value with</p>
* @param configuration <p>The file configuration to update with the new per-sign colors</p> * @param configuration <p>The file configuration to update with the new per-sign colors</p>
*/ */
private void updatePerSignColors(String signType, String colorString, FileConfiguration configuration) { private void updatePerSignColors(@NotNull String signType, @NotNull String colorString,
@NotNull FileConfiguration configuration) {
List<String> newColorStrings = new ArrayList<>(); List<String> newColorStrings = new ArrayList<>();
List<?> oldColors = (List<?>) Stargate.getStargateConfig().getConfigOptionsReference().get(ConfigOption.PER_SIGN_COLORS); List<?> oldColors = (List<?>) Stargate.getStargateConfig().getConfigOptionsReference().get(
ConfigOption.PER_SIGN_COLORS);
for (Object object : oldColors) { for (Object object : oldColors) {
newColorStrings.add(String.valueOf(object)); newColorStrings.add(String.valueOf(object));
} }
@@ -239,7 +251,7 @@ public class CommandConfig implements CommandExecutor {
* @param color <p>The color chosen by the user</p> * @param color <p>The color chosen by the user</p>
* @return <p>True if the given color is valid</p> * @return <p>True if the given color is valid</p>
*/ */
private boolean validatePerSignColor(String color) { private boolean validatePerSignColor(@NotNull String color) {
ChatColor newHighlightColor = parseColor(color); ChatColor newHighlightColor = parseColor(color);
return newHighlightColor != null || color.equalsIgnoreCase("default") || return newHighlightColor != null || color.equalsIgnoreCase("default") ||
color.equalsIgnoreCase("inverted"); color.equalsIgnoreCase("inverted");
@@ -251,11 +263,11 @@ public class CommandConfig implements CommandExecutor {
* @param selectedOption <p>The config option that was changed</p> * @param selectedOption <p>The config option that was changed</p>
* @param commandSender <p>The command sender that executed the config command</p> * @param commandSender <p>The command sender that executed the config command</p>
*/ */
private void saveAndReload(ConfigOption selectedOption, CommandSender commandSender) { private void saveAndReload(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender) {
//Save the config file and reload if necessary //Save the config file and reload if necessary
Stargate.getInstance().saveConfig(); Stargate.getInstance().saveConfig();
Stargate.getMessageSender().sendSuccessMessage(commandSender, "Config updated"); new SGFormatBuilder("Config updated").success(commandSender);
//Reload whatever is necessary //Reload whatever is necessary
reloadIfNecessary(commandSender, selectedOption); reloadIfNecessary(commandSender, selectedOption);
@@ -268,7 +280,8 @@ public class CommandConfig implements CommandExecutor {
* @param commandSender <p>The command sender to alert if the color is invalid</p> * @param commandSender <p>The command sender to alert if the color is invalid</p>
* @param value <p>The new option value</p> * @param value <p>The new option value</p>
*/ */
private boolean registerColor(ConfigOption selectedOption, String value, CommandSender commandSender) { private boolean registerColor(@NotNull ConfigOption selectedOption, @NotNull String value,
@NotNull CommandSender commandSender) {
ChatColor parsedColor = parseColor(value); ChatColor parsedColor = parseColor(value);
if (parsedColor == null) { if (parsedColor == null) {
commandSender.sendMessage(ChatColor.RED + "Invalid color given"); commandSender.sendMessage(ChatColor.RED + "Invalid color given");
@@ -291,7 +304,8 @@ public class CommandConfig implements CommandExecutor {
* @param value <p>The value to parse</p> * @param value <p>The value to parse</p>
* @return <p>The parsed color or null</p> * @return <p>The parsed color or null</p>
*/ */
private ChatColor parseColor(String value) { @Nullable
private ChatColor parseColor(@NotNull String value) {
try { try {
return ChatColor.of(value.toUpperCase()); return ChatColor.of(value.toUpperCase());
} catch (IllegalArgumentException | NullPointerException ignored) { } catch (IllegalArgumentException | NullPointerException ignored) {
@@ -307,7 +321,9 @@ public class CommandConfig implements CommandExecutor {
* @param value <p>The value given</p> * @param value <p>The value given</p>
* @return <p>An integer, or null if it was invalid</p> * @return <p>An integer, or null if it was invalid</p>
*/ */
private Integer getInteger(CommandSender commandSender, ConfigOption selectedOption, String value) { @Nullable
private Integer getInteger(@NotNull CommandSender commandSender, @NotNull ConfigOption selectedOption,
@NotNull String value) {
try { try {
int intValue = Integer.parseInt(value); int intValue = Integer.parseInt(value);
@@ -331,7 +347,9 @@ public class CommandConfig implements CommandExecutor {
* @param value <p>The value given</p> * @param value <p>The value given</p>
* @return <p>A double, or null if it was invalid</p> * @return <p>A double, or null if it was invalid</p>
*/ */
private Double getDouble(CommandSender commandSender, ConfigOption selectedOption, String value) { @Nullable
private Double getDouble(@NotNull CommandSender commandSender, @NotNull ConfigOption selectedOption,
@NotNull String value) {
try { try {
double doubleValue = Double.parseDouble(value); double doubleValue = Double.parseDouble(value);
@@ -353,7 +371,7 @@ public class CommandConfig implements CommandExecutor {
* @param commandSender <p>The command sender initiating the reload</p> * @param commandSender <p>The command sender initiating the reload</p>
* @param configOption <p>The changed config option</p> * @param configOption <p>The changed config option</p>
*/ */
private void reloadIfNecessary(CommandSender commandSender, ConfigOption configOption) { private void reloadIfNecessary(@NotNull CommandSender commandSender, @NotNull ConfigOption configOption) {
if (ConfigTag.requiresFullReload(configOption)) { if (ConfigTag.requiresFullReload(configOption)) {
//Reload everything //Reload everything
Stargate.getStargateConfig().reload(commandSender); Stargate.getStargateConfig().reload(commandSender);
@@ -378,6 +396,10 @@ public class CommandConfig implements CommandExecutor {
//Load or unload Vault and Economy as necessary //Load or unload Vault and Economy as necessary
Stargate.getStargateConfig().reloadEconomy(); Stargate.getStargateConfig().reloadEconomy();
} }
if (ConfigTag.requiresDynmapReload(configOption)) {
//Regenerate all Dynmap markers
DynmapManager.addAllPortalMarkers();
}
} }
} }
@@ -387,7 +409,7 @@ public class CommandConfig implements CommandExecutor {
* @param sender <p>The command sender that sent the command</p> * @param sender <p>The command sender that sent the command</p>
* @param option <p>The config option to print information about</p> * @param option <p>The config option to print information about</p>
*/ */
private void printConfigOptionValue(CommandSender sender, ConfigOption option) { private void printConfigOptionValue(@NotNull CommandSender sender, @NotNull ConfigOption option) {
Object value = Stargate.getStargateConfig().getConfigOptions().get(option); Object value = Stargate.getStargateConfig().getConfigOptions().get(option);
sender.sendMessage(getOptionDescription(option)); sender.sendMessage(getOptionDescription(option));
sender.sendMessage(ChatColor.GREEN + "Current value: " + ChatColor.GOLD + value); sender.sendMessage(ChatColor.GREEN + "Current value: " + ChatColor.GOLD + value);
@@ -398,8 +420,8 @@ public class CommandConfig implements CommandExecutor {
* *
* @param sender <p>The command sender to display the config list to</p> * @param sender <p>The command sender to display the config list to</p>
*/ */
private void displayConfigValues(CommandSender sender) { private void displayConfigValues(@NotNull CommandSender sender) {
sender.sendMessage(ChatColor.GREEN + Stargate.getBackupString("prefix") + ChatColor.GOLD + sender.sendMessage(ChatColor.GREEN + Stargate.getBackupString(Message.PREFIX) + ChatColor.GOLD +
"Config values:"); "Config values:");
for (ConfigOption option : ConfigOption.values()) { for (ConfigOption option : ConfigOption.values()) {
sender.sendMessage(getOptionDescription(option)); sender.sendMessage(getOptionDescription(option));
@@ -412,11 +434,12 @@ public class CommandConfig implements CommandExecutor {
* @param option <p>The option to describe</p> * @param option <p>The option to describe</p>
* @return <p>A string describing the config option</p> * @return <p>A string describing the config option</p>
*/ */
private String getOptionDescription(ConfigOption option) { @NotNull
private String getOptionDescription(@NotNull ConfigOption option) {
Object defaultValue = option.getDefaultValue(); Object defaultValue = option.getDefaultValue();
String stringValue = String.valueOf(defaultValue); String stringValue = String.valueOf(defaultValue);
if (option.getDataType() == OptionDataType.STRING_LIST) { if (option.getDataType() == OptionDataType.STRING_LIST) {
stringValue = "[" + StringUtils.join((String[]) defaultValue, ",") + "]"; stringValue = "[" + String.join(",", (String[]) defaultValue) + "]";
} }
return ChatColor.GOLD + option.getName() + ChatColor.WHITE + " - " + ChatColor.GREEN + option.getDescription() + return ChatColor.GOLD + option.getName() + ChatColor.WHITE + " - " + ChatColor.GREEN + option.getDescription() +
ChatColor.DARK_GRAY + " (Default: " + ChatColor.GRAY + stringValue + ChatColor.DARK_GRAY + ")"; ChatColor.DARK_GRAY + " (Default: " + ChatColor.GRAY + stringValue + ChatColor.DARK_GRAY + ")";

View File

@@ -1,6 +1,9 @@
package net.knarcraft.stargate.command; package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -16,8 +19,8 @@ public class CommandReload implements CommandExecutor {
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) { @NotNull String[] args) {
if (commandSender instanceof Player player) { if (commandSender instanceof Player player) {
if (!player.hasPermission("stargate.admin.reload")) { if (!PermissionHelper.hasPermission(player, Permission.RELOAD)) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied"); new SGFormatBuilder("Permission Denied").error(commandSender);
return true; return true;
} }
} }

View File

@@ -1,13 +1,15 @@
package net.knarcraft.stargate.command; package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.ArrayUtils; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/** /**
* This command represents any command which starts with stargate * This command represents any command which starts with stargate
* *
@@ -15,6 +17,11 @@ import org.jetbrains.annotations.NotNull;
* the plugin itself, not commands for functions of the plugin.</p> * the plugin itself, not commands for functions of the plugin.</p>
*/ */
public class CommandStarGate implements CommandExecutor { public class CommandStarGate implements CommandExecutor {
private final Plugin stargate;
public CommandStarGate(Plugin stargate) {
this.stargate = stargate;
}
@Override @Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -25,14 +32,15 @@ public class CommandStarGate implements CommandExecutor {
} else if (args[0].equalsIgnoreCase("reload")) { } else if (args[0].equalsIgnoreCase("reload")) {
return new CommandReload().onCommand(commandSender, command, s, args); return new CommandReload().onCommand(commandSender, command, s, args);
} else if (args[0].equalsIgnoreCase("config")) { } else if (args[0].equalsIgnoreCase("config")) {
String[] subArgs = (String[]) ArrayUtils.remove(args, 0); String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
return new CommandConfig().onCommand(commandSender, command, s, subArgs); return new CommandConfig().onCommand(commandSender, command, s, subArgs);
} }
return false; return false;
} else { } else {
commandSender.sendMessage(ChatColor.GOLD + "Stargate version " + commandSender.sendMessage(ChatColor.GREEN + "Stargate version " + ChatColor.GOLD + stargate.getDescription().getVersion()
ChatColor.GREEN + Stargate.getPluginVersion()); + ChatColor.GREEN + " running on " + ChatColor.GOLD + Bukkit.getServer().getVersion());
return true; return true;
} }
} }
} }

View File

@@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.knarlib.util.TabCompletionHelper.filterMatchingStartsWith;
/** /**
* This is the completer for stargates config sub-command (/sg config) * This is the completer for stargates config sub-command (/sg config)
*/ */
@@ -48,27 +50,10 @@ public class ConfigTabCompleter implements TabCompleter {
for (ConfigOption option : ConfigOption.values()) { for (ConfigOption option : ConfigOption.values()) {
configOptionNames.add(option.getName()); configOptionNames.add(option.getName());
} }
return filterMatching(configOptionNames, args[0]); return filterMatchingStartsWith(configOptionNames, args[0]);
} }
} }
/**
* Find completable strings which match the text typed by the command's sender
*
* @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 which start with the player's typed text</p>
*/
private List<String> filterMatching(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;
}
/** /**
* Get possible values for the selected option * Get possible values for the selected option
* *
@@ -76,30 +61,31 @@ public class ConfigTabCompleter implements TabCompleter {
* @param typedText <p>The beginning of the typed text, for filtering matching results</p> * @param typedText <p>The beginning of the typed text, for filtering matching results</p>
* @return <p>Some or all of the valid values for the option</p> * @return <p>Some or all of the valid values for the option</p>
*/ */
private List<String> getPossibleOptionValues(ConfigOption selectedOption, String typedText) { @Nullable
private List<String> getPossibleOptionValues(@NotNull ConfigOption selectedOption,
@NotNull String typedText) {
switch (selectedOption) { switch (selectedOption) {
case LANGUAGE: case LANGUAGE -> {
//Return available languages //Return available languages
return filterMatching(languages, typedText); return filterMatchingStartsWith(languages, typedText);
case GATE_FOLDER: }
case PORTAL_FOLDER: case GATE_FOLDER, PORTAL_FOLDER, DEFAULT_GATE_NETWORK -> {
case DEFAULT_GATE_NETWORK:
//Just return the default value as most values should be possible //Just return the default value as most values should be possible
if (typedText.trim().isEmpty()) { if (typedText.trim().isEmpty()) {
return putStringInList((String) selectedOption.getDefaultValue()); return List.of((String) selectedOption.getDefaultValue());
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }
case MAIN_SIGN_COLOR: }
case HIGHLIGHT_SIGN_COLOR: case MAIN_SIGN_COLOR, HIGHLIGHT_SIGN_COLOR, FREE_GATES_COLOR -> {
case FREE_GATES_COLOR:
//Return all colors //Return all colors
return filterMatching(chatColors, typedText); return filterMatchingStartsWith(chatColors, typedText);
}
} }
//If the config value is a boolean, show the two boolean values //If the config value is a boolean, show the two boolean values
if (selectedOption.getDataType() == OptionDataType.BOOLEAN) { if (selectedOption.getDataType() == OptionDataType.BOOLEAN) {
return filterMatching(booleans, typedText); return filterMatchingStartsWith(booleans, typedText);
} }
//If the config value is an integer, display some valid numbers //If the config value is an integer, display some valid numbers
@@ -129,11 +115,13 @@ public class ConfigTabCompleter implements TabCompleter {
* @param args <p>The arguments given by the user</p> * @param args <p>The arguments given by the user</p>
* @return <p>Some or all of the valid values for the option</p> * @return <p>Some or all of the valid values for the option</p>
*/ */
private List<String> getPossibleStringListOptionValues(ConfigOption selectedOption, String[] args) { @NotNull
private List<String> getPossibleStringListOptionValues(@NotNull ConfigOption selectedOption,
@NotNull String[] args) {
if (selectedOption == ConfigOption.PER_SIGN_COLORS) { if (selectedOption == ConfigOption.PER_SIGN_COLORS) {
return getPerSignColorCompletion(args); return getPerSignColorCompletion(args);
} else { } else {
return null; return new ArrayList<>();
} }
} }
@@ -143,29 +131,18 @@ public class ConfigTabCompleter implements TabCompleter {
* @param args <p>The arguments given by the user</p> * @param args <p>The arguments given by the user</p>
* @return <p>The options to give the user</p> * @return <p>The options to give the user</p>
*/ */
private List<String> getPerSignColorCompletion(String[] args) { @NotNull
private List<String> getPerSignColorCompletion(@NotNull String[] args) {
if (args.length < 3) { if (args.length < 3) {
return filterMatching(signTypes, args[1]); return filterMatchingStartsWith(signTypes, args[1]);
} else if (args.length < 4) { } else if (args.length < 4) {
return filterMatching(extendedColors, args[2]); return filterMatchingStartsWith(extendedColors, args[2]);
} else if (args.length < 5) { } else if (args.length < 5) {
return filterMatching(extendedColors, args[3]); return filterMatchingStartsWith(extendedColors, args[3]);
} }
return new ArrayList<>(); return new ArrayList<>();
} }
/**
* Puts a single string value into a string list
*
* @param value <p>The string to make into a list</p>
* @return <p>A list containing the string value</p>
*/
private List<String> putStringInList(String value) {
List<String> list = new ArrayList<>();
list.add(value);
return list;
}
/** /**
* Initializes all lists of auto-completable values * Initializes all lists of auto-completable values
*/ */
@@ -215,24 +192,13 @@ public class ConfigTabCompleter implements TabCompleter {
* *
* @return <p>The available chat colors</p> * @return <p>The available chat colors</p>
*/ */
@NotNull
private List<ChatColor> getChatColors() { private List<ChatColor> getChatColors() {
List<ChatColor> chatColors = new ArrayList<>(); List<ChatColor> chatColors = new ArrayList<>();
chatColors.add(ChatColor.WHITE); char[] colors = new char[]{'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
chatColors.add(ChatColor.BLUE); for (char color : colors) {
chatColors.add(ChatColor.DARK_BLUE); chatColors.add(ChatColor.getByChar(color));
chatColors.add(ChatColor.DARK_PURPLE); }
chatColors.add(ChatColor.LIGHT_PURPLE);
chatColors.add(ChatColor.GOLD);
chatColors.add(ChatColor.GREEN);
chatColors.add(ChatColor.BLACK);
chatColors.add(ChatColor.DARK_GREEN);
chatColors.add(ChatColor.DARK_RED);
chatColors.add(ChatColor.RED);
chatColors.add(ChatColor.AQUA);
chatColors.add(ChatColor.DARK_AQUA);
chatColors.add(ChatColor.DARK_GRAY);
chatColors.add(ChatColor.GRAY);
chatColors.add(ChatColor.YELLOW);
chatColors.add(ChatColor.of("#ed76d9")); chatColors.add(ChatColor.of("#ed76d9"));
chatColors.add(ChatColor.of("#ffecb7")); chatColors.add(ChatColor.of("#ffecb7"));
return chatColors; return chatColors;
@@ -243,17 +209,23 @@ public class ConfigTabCompleter implements TabCompleter {
*/ */
private void initializeLanguages() { private void initializeLanguages() {
languages = new ArrayList<>(); languages = new ArrayList<>();
languages.add("cs");
languages.add("de"); languages.add("de");
languages.add("en"); languages.add("en");
languages.add("es"); languages.add("es");
languages.add("fr"); languages.add("fr");
languages.add("hu"); languages.add("hu");
languages.add("it"); languages.add("it");
languages.add("nb-no"); languages.add("ja");
languages.add("nb");
languages.add("nl"); languages.add("nl");
languages.add("nn-no"); languages.add("nn");
languages.add("pt-br"); languages.add("pt");
languages.add("ru"); languages.add("ru");
languages.add("sv");
languages.add("tr");
languages.add("uk");
languages.add("zh");
//TODO: Generate this list dynamically by listing the language files in the jar and adding the user's custom //TODO: Generate this list dynamically by listing the language files in the jar and adding the user's custom
// language files // language files
} }

View File

@@ -1,6 +1,7 @@
package net.knarcraft.stargate.command; package net.knarcraft.stargate.command;
import org.apache.commons.lang.ArrayUtils; import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
@@ -9,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -17,8 +19,9 @@ import java.util.List;
public class StarGateTabCompleter implements TabCompleter { public class StarGateTabCompleter implements TabCompleter {
@Override @Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @Nullable
@NotNull String s, @NotNull String[] args) { public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (args.length == 1) { if (args.length == 1) {
List<String> commands = getAvailableCommands(commandSender); List<String> commands = getAvailableCommands(commandSender);
List<String> matchingCommands = new ArrayList<>(); List<String> matchingCommands = new ArrayList<>();
@@ -29,7 +32,7 @@ public class StarGateTabCompleter implements TabCompleter {
} }
return matchingCommands; return matchingCommands;
} else if (args.length > 1 && args[0].equalsIgnoreCase("config")) { } else if (args.length > 1 && args[0].equalsIgnoreCase("config")) {
String[] subArgs = (String[]) ArrayUtils.remove(args, 0); String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
return new ConfigTabCompleter().onTabComplete(commandSender, command, s, subArgs); return new ConfigTabCompleter().onTabComplete(commandSender, command, s, subArgs);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
@@ -42,13 +45,14 @@ public class StarGateTabCompleter implements TabCompleter {
* @param commandSender <p>The command sender to get available commands for</p> * @param commandSender <p>The command sender to get available commands for</p>
* @return <p>The commands available to the command sender</p> * @return <p>The commands available to the command sender</p>
*/ */
private List<String> getAvailableCommands(CommandSender commandSender) { @NotNull
private List<String> getAvailableCommands(@NotNull CommandSender commandSender) {
List<String> commands = new ArrayList<>(); List<String> commands = new ArrayList<>();
commands.add("about"); commands.add("about");
if (!(commandSender instanceof Player player) || player.hasPermission("stargate.admin.reload")) { if (!(commandSender instanceof Player player) || PermissionHelper.hasPermission(player, Permission.RELOAD)) {
commands.add("reload"); commands.add("reload");
} }
if (!(commandSender instanceof Player player) || player.hasPermission("stargate.admin.config")) { if (!(commandSender instanceof Player player) || PermissionHelper.hasPermission(player, Permission.CONFIG)) {
commands.add("config"); commands.add("config");
} }
return commands; return commands;

View File

@@ -1,5 +1,8 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* A ConfigOption represents one of the available config options * A ConfigOption represents one of the available config options
*/ */
@@ -50,9 +53,13 @@ public enum ConfigOption {
*/ */
HIGHLIGHT_SIGN_COLOR("gates.cosmetic.highlightSignColor", "The text color used for highlighting stargate signs", "WHITE"), HIGHLIGHT_SIGN_COLOR("gates.cosmetic.highlightSignColor", "The text color used for highlighting stargate signs", "WHITE"),
/**
* The colors to use for each type of sign
*/
PER_SIGN_COLORS("gates.cosmetic.perSignColors", "The per-sign color specification", new String[]{ PER_SIGN_COLORS("gates.cosmetic.perSignColors", "The per-sign color specification", new String[]{
"'ACACIA:default,default'", "'BIRCH:default,default'", "'CRIMSON:inverted,inverted'", "'DARK_OAK:inverted,inverted'", "'ACACIA:default,default'", "'BIRCH:default,default'", "'CRIMSON:inverted,inverted'", "'DARK_OAK:inverted,inverted'",
"'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'"}), "'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'",
"'BAMBOO:default,default'", "'CHERRY:default,default'"}),
/** /**
* Whether to destroy portals when any blocks are broken by explosions * Whether to destroy portals when any blocks are broken by explosions
@@ -102,6 +109,20 @@ public enum ConfigOption {
HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures", HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures",
"Whether to enable players to teleport a creature on a leash", true), "Whether to enable players to teleport a creature on a leash", true),
/**
* Whether to enable a fix that makes teleportation of minecarts/boats work even with craftbook's vehicle removal
*/
ENABLE_CRAFT_BOOK_REMOVE_ON_EJECT_FIX("gates.functionality.enableCraftBookRemoveOnEjectFix",
"Whether to enable a fix that causes loss of NBT data, but allows vehicle teleportation to work " +
"when CraftBook's remove minecart/boat on eject setting is enabled", false),
/**
* The delay between teleporting a vehicle and adding the player as passenger
*/
WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY("advanced.waitForPlayerAfterTeleportDelay",
"The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, " +
"a value of 6 is required to avoid client glitches after teleporting on a vehicle.", 6),
/** /**
* Whether to enable economy support for taking payment from players creating/destroying/using stargates * Whether to enable economy support for taking payment from players creating/destroying/using stargates
*/ */
@@ -133,6 +154,11 @@ public enum ConfigOption {
CHARGE_FREE_DESTINATION("economy.chargeFreeDestination", CHARGE_FREE_DESTINATION("economy.chargeFreeDestination",
"Whether to require payment if the destination is free, but the entrance stargate is not", true), "Whether to require payment if the destination is free, but the entrance stargate is not", true),
/**
* The account to transfer all paid fees to
*/
TAX_ACCOUNT("economy.taxAccount", "The UUID of the account all fees are paid to (except for money to the Stargate owner)", ""),
/** /**
* Whether to mark free gates with a different color * Whether to mark free gates with a different color
*/ */
@@ -161,7 +187,25 @@ public enum ConfigOption {
/** /**
* The velocity of players exiting a stargate, relative to the entry velocity * The velocity of players exiting a stargate, relative to the entry velocity
*/ */
EXIT_VELOCITY("gates.exitVelocity", "The velocity of players exiting stargates, relative to the entry velocity", 0.1D); EXIT_VELOCITY("gates.exitVelocity", "The velocity of players exiting stargates, relative to the entry velocity", 0.1D),
/**
* Whether to enable showing Stargates in Dynmap
*/
ENABLE_DYNMAP("dynmap.enableDynmap", "Whether to display Stargates in Dynmap's map", true),
/**
* Whether to hide Dynmap icons by default
*/
DYNMAP_ICONS_DEFAULT_HIDDEN("dynmap.dynmapIconsHiddenByDefault",
"Whether to hide Stargate's Dynmap icons by default, requiring the user to enable them.", true),
/**
* The amount of ticks to wait when processing the Stargate control update queue
*/
CONTROL_UPDATE_QUEUE_DELAY("gates.integrity.controlUpdateDelay",
"The delay between each time a Stargate's controls are updated after startup", 3),
;
private final String configNode; private final String configNode;
private final String description; private final String description;
@@ -175,7 +219,7 @@ public enum ConfigOption {
* @param description <p>The description of what this config option does</p> * @param description <p>The description of what this config option does</p>
* @param defaultValue <p>The default value of this config option</p> * @param defaultValue <p>The default value of this config option</p>
*/ */
ConfigOption(String configNode, String description, Object defaultValue) { ConfigOption(@NotNull String configNode, @NotNull String description, @NotNull Object defaultValue) {
this.configNode = configNode; this.configNode = configNode;
this.description = description; this.description = description;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
@@ -201,7 +245,7 @@ public enum ConfigOption {
* @param name <p>The name of the config option to get</p> * @param name <p>The name of the config option to get</p>
* @return <p>The corresponding config option, or null if the name is invalid</p> * @return <p>The corresponding config option, or null if the name is invalid</p>
*/ */
public static ConfigOption getByName(String name) { public static @Nullable ConfigOption getByName(@NotNull String name) {
for (ConfigOption option : ConfigOption.values()) { for (ConfigOption option : ConfigOption.values()) {
if (option.getName().equalsIgnoreCase(name)) { if (option.getName().equalsIgnoreCase(name)) {
return option; return option;
@@ -215,7 +259,7 @@ public enum ConfigOption {
* *
* @return <p>The name of this config option</p> * @return <p>The name of this config option</p>
*/ */
public String getName() { public @NotNull String getName() {
if (!this.configNode.contains(".")) { if (!this.configNode.contains(".")) {
return this.configNode; return this.configNode;
} }
@@ -228,7 +272,7 @@ public enum ConfigOption {
* *
* @return <p>The data type used</p> * @return <p>The data type used</p>
*/ */
public OptionDataType getDataType() { public @NotNull OptionDataType getDataType() {
return this.dataType; return this.dataType;
} }
@@ -237,7 +281,7 @@ public enum ConfigOption {
* *
* @return <p>This config option's config node</p> * @return <p>This config option's config node</p>
*/ */
public String getConfigNode() { public @NotNull String getConfigNode() {
return this.configNode; return this.configNode;
} }
@@ -246,7 +290,7 @@ public enum ConfigOption {
* *
* @return <p>The description of this config option</p> * @return <p>The description of this config option</p>
*/ */
public String getDescription() { public @NotNull String getDescription() {
return this.description; return this.description;
} }
@@ -255,7 +299,7 @@ public enum ConfigOption {
* *
* @return <p>This config option's default value</p> * @return <p>This config option's default value</p>
*/ */
public Object getDefaultValue() { public @NotNull Object getDefaultValue() {
return this.defaultValue; return this.defaultValue;
} }

View File

@@ -1,24 +1,38 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config;
import java.util.Arrays; import org.jetbrains.annotations.NotNull;
import java.util.Set;
/** /**
* A config tag groups config values by a property * A config tag groups config values by a property
*/ */
public enum ConfigTag { public enum ConfigTag {
COLOR(new ConfigOption[]{ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR, /**
ConfigOption.HIGHLIGHT_SIGN_COLOR, ConfigOption.PER_SIGN_COLORS}), * Color-related configuration options
FOLDER(new ConfigOption[]{ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER}); */
COLOR(Set.of(ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR, ConfigOption.HIGHLIGHT_SIGN_COLOR,
ConfigOption.PER_SIGN_COLORS)),
private final ConfigOption[] taggedOptions; /**
* Folder-altering configuration options
*/
FOLDER(Set.of(ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER)),
/**
* Dynmap-related configuration options
*/
DYNMAP(Set.of(ConfigOption.ENABLE_DYNMAP, ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN));
private final Set<ConfigOption> taggedOptions;
/** /**
* Instantiates a new config tag * Instantiates a new config tag
* *
* @param taggedOptions <p>The config options included in this tag</p> * @param taggedOptions <p>The config options included in this tag</p>
*/ */
ConfigTag(ConfigOption[] taggedOptions) { ConfigTag(@NotNull Set<ConfigOption> taggedOptions) {
this.taggedOptions = taggedOptions; this.taggedOptions = taggedOptions;
} }
@@ -28,8 +42,8 @@ public enum ConfigTag {
* @param option <p>The config option to check</p> * @param option <p>The config option to check</p>
* @return <p>True of the config option is tagged</p> * @return <p>True of the config option is tagged</p>
*/ */
public boolean isTagged(ConfigOption option) { public boolean isTagged(@NotNull ConfigOption option) {
return Arrays.stream(taggedOptions).anyMatch((item) -> item == option); return taggedOptions.contains(option);
} }
/** /**
@@ -38,7 +52,7 @@ public enum ConfigTag {
* @param configOption <p>The config option to check</p> * @param configOption <p>The config option to check</p>
* @return <p>True if changing the config option requires a "reload of colors" to take effect</p> * @return <p>True if changing the config option requires a "reload of colors" to take effect</p>
*/ */
public static boolean requiresColorReload(ConfigOption configOption) { public static boolean requiresColorReload(@NotNull ConfigOption configOption) {
return (COLOR.isTagged(configOption) && configOption != ConfigOption.FREE_GATES_COLOR); return (COLOR.isTagged(configOption) && configOption != ConfigOption.FREE_GATES_COLOR);
} }
@@ -48,17 +62,27 @@ public enum ConfigTag {
* @param option <p>The config option to check</p> * @param option <p>The config option to check</p>
* @return <p>True if changing the config option requires a full reload to take effect</p> * @return <p>True if changing the config option requires a full reload to take effect</p>
*/ */
public static boolean requiresFullReload(ConfigOption option) { public static boolean requiresFullReload(@NotNull ConfigOption option) {
return FOLDER.isTagged(option); return FOLDER.isTagged(option);
} }
/**
* Checks whether a given config option requires a re-load of all Dynmap markers
*
* @param configOption <p>The config option to check</p>
* @return <p>True if changing the config option requires a reload of all dynmap markers</p>
*/
public static boolean requiresDynmapReload(@NotNull ConfigOption configOption) {
return DYNMAP.isTagged(configOption);
}
/** /**
* Checks whether a given config option requires a portal reload to take effect * Checks whether a given config option requires a portal reload to take effect
* *
* @param option <p>The config option to check</p> * @param option <p>The config option to check</p>
* @return <p>True if changing the config option requires a portal reload to take effect</p> * @return <p>True if changing the config option requires a portal reload to take effect</p>
*/ */
public static boolean requiresPortalReload(ConfigOption option) { public static boolean requiresPortalReload(@NotNull ConfigOption option) {
return COLOR.isTagged(option) || FOLDER.isTagged(option) || option == ConfigOption.VERIFY_PORTALS; return COLOR.isTagged(option) || FOLDER.isTagged(option) || option == ConfigOption.VERIFY_PORTALS;
} }
@@ -68,7 +92,7 @@ public enum ConfigTag {
* @param option <p>The config option to check</p> * @param option <p>The config option to check</p>
* @return <p>True if the language loader requires a reload</p> * @return <p>True if the language loader requires a reload</p>
*/ */
public static boolean requiresLanguageReload(ConfigOption option) { public static boolean requiresLanguageReload(@NotNull ConfigOption option) {
return option == ConfigOption.LANGUAGE; return option == ConfigOption.LANGUAGE;
} }
@@ -78,7 +102,7 @@ public enum ConfigTag {
* @param option <p>The config option to check</p> * @param option <p>The config option to check</p>
* @return <p>True if economy requires a reload</p> * @return <p>True if economy requires a reload</p>
*/ */
public static boolean requiresEconomyReload(ConfigOption option) { public static boolean requiresEconomyReload(@NotNull ConfigOption option) {
return option == ConfigOption.USE_ECONOMY; return option == ConfigOption.USE_ECONOMY;
} }

View File

@@ -1,61 +0,0 @@
package net.knarcraft.stargate.config;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
/**
* The message sender is responsible sending messages to players with correct coloring and formatting
*/
public final class MessageSender {
private final LanguageLoader languageLoader;
/**
* Instantiates a new message sender
*
* @param languageLoader <p>The language loader to get translated strings from</p>
*/
public MessageSender(LanguageLoader languageLoader) {
this.languageLoader = languageLoader;
}
/**
* Sends an error message to a player
*
* @param player <p>The player to send the message to</p>
* @param message <p>The message to send</p>
*/
public void sendErrorMessage(CommandSender player, String message) {
sendMessage(player, message, true);
}
/**
* Sends a success message to a player
*
* @param player <p>The player to send the message to</p>
* @param message <p>The message to send</p>
*/
public void sendSuccessMessage(CommandSender player, String message) {
sendMessage(player, message, false);
}
/**
* Sends a message to a player
*
* @param sender <p>The player to send the message to</p>
* @param message <p>The message to send</p>
* @param error <p>Whether the message sent is an error</p>
*/
private void sendMessage(CommandSender sender, String message, boolean error) {
if (message.isEmpty()) {
return;
}
message = ChatColor.translateAlternateColorCodes('&', message);
if (error) {
sender.sendMessage(ChatColor.RED + languageLoader.getString("prefix") + ChatColor.WHITE + message);
} else {
sender.sendMessage(ChatColor.GREEN + languageLoader.getString("prefix") + ChatColor.WHITE + message);
}
}
}

View File

@@ -0,0 +1,68 @@
package net.knarcraft.stargate.config;
import org.jetbrains.annotations.NotNull;
/**
* A representation of all Stargate permissions
*/
public enum Permission {
ADMIN("admin"),
CONFIG("admin.config"),
RELOAD("admin.reload"),
CREATE_BUNGEE("admin.bungee"),
DYE_SIGN("admin.dye"),
SEE_HIDDEN("admin.hidden"),
USE_PRIVATE("admin.private"),
FREE_USAGE("free.use"),
FREE_DESTRUCTION("free.destroy"),
FREE_CREATION("free.create"),
ACCESS_SERVER("server"),
ACCESS_NETWORK("network"),
ACCESS_WORLD("world"),
CREATE_GATE("create.gate"),
CREATE_NETWORK("create.network"),
CREATE_PERSONAL("create.personal"),
DESTROY_NETWORK("destroy.network"),
DESTROY_PERSONAL("destroy.personal"),
OPTION_HIDDEN("option.hidden"),
OPTION_ALWAYS_ON("option.alwayson"),
OPTIONS_PRIVATE("options.private"),
OPTIONS_FREE("options.free"),
OPTIONS_BACKWARDS("options.backwards"),
OPTIONS_SHOW("options.show"),
OPTIONS_NO_NETWORK("options.nonetwork"),
OPTIONS_RANDOM("options.random"),
OPTIONS_BUNGEE("options.bungee"),
OPTIONS_QUIET("options.quiet"),
OPTIONS_INVISIBLE("options.invisible"),
;
private final String node;
/**
* Instantiates a new permission
*
* @param node <p>The permission node</p>
*/
Permission(@NotNull String node) {
this.node = node;
}
/**
* Gets the permission node of this permission
*
* @return <p>The node of this permission</p>
*/
@NotNull
public String getNode() {
return "stargate." + this.node;
}
}

View File

@@ -1,23 +1,32 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.Translator;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ConfigHelper;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.addons.DynmapManager;
import net.knarcraft.stargate.config.addons.EconomyConfig;
import net.knarcraft.stargate.config.formatting.LanguageLoader;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.listener.BungeeCordListener;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.gate.GateHandler; import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.thread.BlockChangeThread; import net.knarcraft.stargate.thread.BlockChangeThread;
import net.knarcraft.stargate.utility.FileHelper; import net.knarcraft.stargate.utility.BungeeHelper;
import net.knarcraft.stargate.utility.PortalFileHelper; import net.knarcraft.stargate.utility.PortalFileHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.messaging.Messenger; import org.bukkit.plugin.messaging.Messenger;
import org.dynmap.DynmapAPI;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@@ -25,6 +34,8 @@ import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* The stargate config is responsible for keeping track of all configuration values * The stargate config is responsible for keeping track of all configuration values
@@ -36,7 +47,6 @@ public final class StargateConfig {
private final HashSet<String> managedWorlds = new HashSet<>(); private final HashSet<String> managedWorlds = new HashSet<>();
private StargateGateConfig stargateGateConfig; private StargateGateConfig stargateGateConfig;
private MessageSender messageSender;
private final LanguageLoader languageLoader; private final LanguageLoader languageLoader;
private EconomyConfig economyConfig; private EconomyConfig economyConfig;
private final Logger logger; private final Logger logger;
@@ -45,6 +55,7 @@ public final class StargateConfig {
private String gateFolder; private String gateFolder;
private String portalFolder; private String portalFolder;
private String languageName = "en"; private String languageName = "en";
private boolean isLoaded = false;
private final Map<ConfigOption, Object> configOptions; private final Map<ConfigOption, Object> configOptions;
@@ -53,7 +64,7 @@ public final class StargateConfig {
* *
* @param logger <p>The logger to use for logging errors</p> * @param logger <p>The logger to use for logging errors</p>
*/ */
public StargateConfig(Logger logger) { public StargateConfig(@NotNull Logger logger) {
this.logger = logger; this.logger = logger;
configOptions = new HashMap<>(); configOptions = new HashMap<>();
@@ -71,6 +82,7 @@ public final class StargateConfig {
* *
* @return <p>A reference to the config options map</p> * @return <p>A reference to the config options map</p>
*/ */
@NotNull
public Map<ConfigOption, Object> getConfigOptionsReference() { public Map<ConfigOption, Object> getConfigOptionsReference() {
return configOptions; return configOptions;
} }
@@ -90,17 +102,48 @@ public final class StargateConfig {
languageLoader.setChosenLanguage(languageName); languageLoader.setChosenLanguage(languageName);
languageLoader.reload(); languageLoader.reload();
messageSender = new MessageSender(languageLoader); // Update prefix of the format builder
SGFormatBuilder.setStringFormatter(getStringFormatter());
if (isDebuggingEnabled()) { if (isDebuggingEnabled()) {
languageLoader.debug(); languageLoader.debug();
} }
this.createMissingFolders();
this.loadGates(); this.loadGates();
this.createMissingFolders();
this.loadAllPortals(); this.loadAllPortals();
//Set up vault economy if vault has been loaded //Set up vault economy if vault has been loaded
setupVaultEconomy(); setupVaultEconomy();
//Set up dynmap
try {
DynmapAPI dynmapAPI = (DynmapAPI) Bukkit.getPluginManager().getPlugin("dynmap");
if (dynmapAPI != null) {
try {
DynmapManager.initialize(dynmapAPI);
DynmapManager.addAllPortalMarkers();
} catch (NullPointerException ignored) {
logger.warning("Dynmap started in an invalid state. Check your log/console for dynmap-related " +
"problems. Dynmap integration cannot be initialized.");
}
}
} catch (NoClassDefFoundError error) {
logger.warning("Dynmap seems to be unavailable, even though its API is registered. Dynmap " +
"integration is disabled.");
DynmapManager.disable();
}
this.isLoaded = true;
}
/**
* Gets whether this configuration has been fully loaded
*
* @return <p>True if not fully loaded</p>
*/
public boolean isNotLoaded() {
return !this.isLoaded;
} }
/** /**
@@ -108,6 +151,7 @@ public final class StargateConfig {
* *
* @return <p>The loaded config options</p> * @return <p>The loaded config options</p>
*/ */
@NotNull
public Map<ConfigOption, Object> getConfigOptions() { public Map<ConfigOption, Object> getConfigOptions() {
return new HashMap<>(configOptions); return new HashMap<>(configOptions);
} }
@@ -119,6 +163,7 @@ public final class StargateConfig {
* *
* @return <p>The open portals queue</p> * @return <p>The open portals queue</p>
*/ */
@NotNull
public Queue<Portal> getOpenPortalsQueue() { public Queue<Portal> getOpenPortalsQueue() {
return openPortalsQueue; return openPortalsQueue;
} }
@@ -130,6 +175,7 @@ public final class StargateConfig {
* *
* @return <p>The active portals queue</p> * @return <p>The active portals queue</p>
*/ */
@NotNull
public Queue<Portal> getActivePortalsQueue() { public Queue<Portal> getActivePortalsQueue() {
return activePortalsQueue; return activePortalsQueue;
} }
@@ -152,11 +198,30 @@ public final class StargateConfig {
return (boolean) configOptions.get(ConfigOption.PERMISSION_DEBUG); return (boolean) configOptions.get(ConfigOption.PERMISSION_DEBUG);
} }
/**
* Gets whether Dynmap integration is disabled
*
* @return <p>Whether Dynmap integration is disabled</p>
*/
public boolean isDynmapDisabled() {
return !((boolean) configOptions.get(ConfigOption.ENABLE_DYNMAP));
}
/**
* Gets whether Dynmap icons should be hidden by default
*
* @return <p>Whether Dynmap icons should be hidden by default</p>
*/
public boolean hideDynmapIcons() {
return (boolean) configOptions.get(ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN);
}
/** /**
* Gets the object containing economy config values * Gets the object containing economy config values
* *
* @return <p>The object containing economy config values</p> * @return <p>The object containing economy config values</p>
*/ */
@NotNull
public EconomyConfig getEconomyConfig() { public EconomyConfig getEconomyConfig() {
return this.economyConfig; return this.economyConfig;
} }
@@ -166,16 +231,16 @@ public final class StargateConfig {
* *
* @param sender <p>The sender of the reload request</p> * @param sender <p>The sender of the reload request</p>
*/ */
public void reload(CommandSender sender) { public void reload(@NotNull CommandSender sender) {
//Unload all saved data //Unload all saved data
unload(); unload();
//Perform all block change requests to prevent mismatch if a gate's open-material changes. Changing the //Perform all block change requests to prevent mismatch if a gate's open-material changes. Changing the
// closed-material still requires a restart. // closed-material still requires a restart.
BlockChangeRequest firstElement = Stargate.getBlockChangeRequestQueue().peek(); BlockChangeRequest firstElement = Stargate.getControlBlockUpdateRequestQueue().peek();
while (firstElement != null) { while (firstElement != null) {
BlockChangeThread.pollQueue(); BlockChangeThread.pollQueue();
firstElement = Stargate.getBlockChangeRequestQueue().peek(); firstElement = Stargate.getControlBlockUpdateRequestQueue().peek();
} }
//Store the old enable bungee state in case it changes //Store the old enable bungee state in case it changes
@@ -189,7 +254,13 @@ public final class StargateConfig {
startStopBungeeListener(stargateGateConfig.enableBungee()); startStopBungeeListener(stargateGateConfig.enableBungee());
} }
messageSender.sendErrorMessage(sender, languageLoader.getString("reloaded")); //Reload portal markers
DynmapManager.addAllPortalMarkers();
// Update prefix of the format builder
SGFormatBuilder.setStringFormatter(getStringFormatter());
new SGFormatBuilder(Message.RELOADED).error(sender);
} }
/** /**
@@ -213,7 +284,7 @@ public final class StargateConfig {
} }
//Force all portals to close //Force all portals to close
closeAllOpenPortals(); closeAllOpenPortals();
PortalHandler.closeAllPortals(); PortalUtil.closeAllPortals();
//Clear queues and lists //Clear queues and lists
activePortalsQueue.clear(); activePortalsQueue.clear();
@@ -236,6 +307,7 @@ public final class StargateConfig {
* *
* @return <p>The managed worlds</p> * @return <p>The managed worlds</p>
*/ */
@NotNull
public Set<String> getManagedWorlds() { public Set<String> getManagedWorlds() {
return new HashSet<>(managedWorlds); return new HashSet<>(managedWorlds);
} }
@@ -245,7 +317,7 @@ public final class StargateConfig {
* *
* @param worldName <p>The name of the world to manage</p> * @param worldName <p>The name of the world to manage</p>
*/ */
public void addManagedWorld(String worldName) { public void addManagedWorld(@NotNull String worldName) {
managedWorlds.add(worldName); managedWorlds.add(worldName);
} }
@@ -254,7 +326,7 @@ public final class StargateConfig {
* *
* @param worldName <p>The name of the world to stop managing</p> * @param worldName <p>The name of the world to stop managing</p>
*/ */
public void removeManagedWorld(String worldName) { public void removeManagedWorld(@NotNull String worldName) {
managedWorlds.remove(worldName); managedWorlds.remove(worldName);
} }
@@ -291,7 +363,21 @@ public final class StargateConfig {
if (start) { if (start) {
messenger.registerOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel); messenger.registerOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel);
messenger.registerIncomingPluginChannel(Stargate.getInstance(), bungeeChannel, new BungeeCordListener()); messenger.registerIncomingPluginChannel(Stargate.getInstance(), bungeeChannel, (channel, unused, message) -> {
//Ignore plugin messages if some other plugin message is received
if (!channel.equals(BungeeHelper.getBungeeChannel())) {
return;
}
//Try to read the plugin message
String receivedMessage = BungeeHelper.readPluginMessage(message);
if (receivedMessage == null) {
return;
}
//Use the message to initiate teleportation
BungeeHelper.handleTeleportMessage(receivedMessage);
});
} else { } else {
messenger.unregisterIncomingPluginChannel(Stargate.getInstance(), bungeeChannel); messenger.unregisterIncomingPluginChannel(Stargate.getInstance(), bungeeChannel);
messenger.unregisterOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel); messenger.unregisterOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel);
@@ -336,9 +422,11 @@ public final class StargateConfig {
FileConfiguration newConfig = Stargate.getInstance().getConfig(); FileConfiguration newConfig = Stargate.getInstance().getConfig();
boolean isMigrating = false; boolean isMigrating = false;
if (newConfig.getString("lang") != null || newConfig.getString("economy.freeGatesGreen") != null) { if (newConfig.getString("lang") != null || newConfig.getString("economy.taxAccount") == null) {
migrateConfig(newConfig); ConfigHelper.migrateConfig(Stargate.getInstance());
isMigrating = true; isMigrating = true;
Stargate.getInstance().reloadConfig();
newConfig = Stargate.getInstance().getConfig();
} }
//Copy missing default values if any values are missing //Copy missing default values if any values are missing
@@ -352,13 +440,10 @@ public final class StargateConfig {
//Load the option using its correct data type //Load the option using its correct data type
switch (option.getDataType()) { switch (option.getDataType()) {
case STRING_LIST -> optionValue = newConfig.getStringList(configNode); case STRING_LIST -> optionValue = newConfig.getStringList(configNode);
case STRING -> { case STRING -> optionValue = newConfig.getString(configNode, (String) option.getDefaultValue()).trim();
String value = newConfig.getString(configNode); case BOOLEAN -> optionValue = newConfig.getBoolean(configNode, (boolean) option.getDefaultValue());
optionValue = value != null ? value.trim() : ""; case INTEGER -> optionValue = newConfig.getInt(configNode, (int) option.getDefaultValue());
} case DOUBLE -> optionValue = newConfig.getDouble(configNode, (double) option.getDefaultValue());
case BOOLEAN -> optionValue = newConfig.getBoolean(configNode);
case INTEGER -> optionValue = newConfig.getInt(configNode);
case DOUBLE -> optionValue = newConfig.getDouble(configNode);
default -> throw new IllegalArgumentException("Invalid config data type encountered"); default -> throw new IllegalArgumentException("Invalid config data type encountered");
} }
configOptions.put(option, optionValue); configOptions.put(option, optionValue);
@@ -369,10 +454,24 @@ public final class StargateConfig {
//Get important folders from the config //Get important folders from the config
portalFolder = (String) configOptions.get(ConfigOption.PORTAL_FOLDER); portalFolder = (String) configOptions.get(ConfigOption.PORTAL_FOLDER);
if (portalFolder.isEmpty()) {
portalFolder = dataFolderPath + "/portals/";
} else {
portalFolder = replacePluginFolderPath(portalFolder);
}
Stargate.debug("StargateConfig::loadConfig", "Portal folder is " + portalFolder);
gateFolder = (String) configOptions.get(ConfigOption.GATE_FOLDER); gateFolder = (String) configOptions.get(ConfigOption.GATE_FOLDER);
if (gateFolder.isEmpty()) {
gateFolder = dataFolderPath + "/gates/";
} else {
gateFolder = replacePluginFolderPath(gateFolder);
}
Stargate.debug("StargateConfig::loadConfig", "Gate folder is " + gateFolder);
//If users have an outdated config, assume they also need to update their default gates //If users have an outdated config, assume they also need to update their default gates
if (isMigrating) { if (isMigrating) {
this.createMissingFolders();
GateHandler.writeDefaultGatesToFolder(gateFolder); GateHandler.writeDefaultGatesToFolder(gateFolder);
} }
@@ -385,11 +484,29 @@ public final class StargateConfig {
Stargate.getInstance().saveConfig(); Stargate.getInstance().saveConfig();
} }
/**
* Replaces "plugins/Stargate" in a folder path, and replaces it with the full path relative to the data folder
*
* @param input <p>The input string to replace in</p>
* @return <p>The replaced path, or the input if not applicable</p>
*/
@NotNull
private String replacePluginFolderPath(@NotNull String input) {
Pattern pattern = Pattern.compile("(?i)^plugins[\\\\/]Stargate");
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
return dataFolderPath + matcher.replaceAll("");
} else {
return input;
}
}
/** /**
* Gets the object containing configuration values regarding gates * Gets the object containing configuration values regarding gates
* *
* @return <p>Gets the gate config</p> * @return <p>Gets the gate config</p>
*/ */
@NotNull
public StargateGateConfig getStargateGateConfig() { public StargateGateConfig getStargateGateConfig() {
return stargateGateConfig; return stargateGateConfig;
} }
@@ -402,53 +519,15 @@ public final class StargateConfig {
Stargate.logInfo(String.format("Loaded %s gate layouts", GateHandler.getGateCount())); Stargate.logInfo(String.format("Loaded %s gate layouts", GateHandler.getGateCount()));
} }
/**
* Changes all configuration values from the old name to the new name
*
* @param newConfig <p>The config to read from and write to</p>
*/
private void migrateConfig(FileConfiguration newConfig) {
//Save the old config just in case something goes wrong
try {
newConfig.save(dataFolderPath + "/config.yml.old");
} catch (IOException e) {
Stargate.debug("Stargate::migrateConfig", "Unable to save old backup and do migration");
e.printStackTrace();
return;
}
//Read all available config migrations
Map<String, String> migrationFields;
try {
migrationFields = FileHelper.readKeyValuePairs(FileHelper.getBufferedReaderFromInputStream(
FileHelper.getInputStreamForInternalFile("/config-migrations.txt")));
} catch (IOException e) {
Stargate.debug("Stargate::migrateConfig", "Unable to load config migration file");
e.printStackTrace();
return;
}
//Replace old config names with the new ones
for (String key : migrationFields.keySet()) {
if (newConfig.contains(key)) {
String newPath = migrationFields.get(key);
Object oldValue = newConfig.get(key);
if (!newPath.trim().isEmpty()) {
newConfig.set(newPath, oldValue);
}
newConfig.set(key, null);
}
}
}
/** /**
* Loads economy from Vault * Loads economy from Vault
*/ */
private void setupVaultEconomy() { private void setupVaultEconomy() {
EconomyConfig economyConfig = getEconomyConfig(); EconomyConfig economyConfig = getEconomyConfig();
if (economyConfig.setupEconomy(Stargate.getPluginManager()) && economyConfig.getEconomy() != null) { if (economyConfig.setupEconomy(Stargate.getPluginManager()) && economyConfig.getEconomy() != null &&
economyConfig.getVault() != null) {
String vaultVersion = economyConfig.getVault().getDescription().getVersion(); String vaultVersion = economyConfig.getVault().getDescription().getVersion();
Stargate.logInfo(Stargate.replaceVars(Stargate.getString("vaultLoaded"), "%version%", vaultVersion)); Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOADED).replace("%version%", vaultVersion).toString());
} }
} }
@@ -468,18 +547,24 @@ public final class StargateConfig {
* Creates missing folders * Creates missing folders
*/ */
private void createMissingFolders() { private void createMissingFolders() {
File newPortalDir = new File(portalFolder); createMissingFolder(new File(gateFolder), "Unable to create gate directory");
if (!newPortalDir.exists()) { createMissingFolder(new File(portalFolder), "Unable to create portal directory");
if (!newPortalDir.mkdirs()) {
logger.severe("Unable to create portal directory");
}
}
File newFile = new File(portalFolder, Stargate.getInstance().getServer().getWorlds().get(0).getName() + File newFile = new File(portalFolder, Stargate.getInstance().getServer().getWorlds().get(0).getName() +
".db"); ".db");
if (!newFile.exists() && !newFile.getParentFile().exists()) { if (!newFile.exists() && !newFile.getParentFile().exists() && !newFile.getParentFile().mkdirs()) {
if (!newFile.getParentFile().mkdirs()) { logger.severe("Unable to create portal database folder: " + newFile.getParentFile().getPath());
logger.severe("Unable to create portal database folder: " + newFile.getParentFile().getPath()); }
} }
/**
* Creates the given folder if it's missing
*
* @param folder <p>The folder to create</p>
* @param errorMessage <p>The error message to display if unable to create the folder</p>
*/
private void createMissingFolder(File folder, String errorMessage) {
if (!folder.exists() && !folder.mkdirs()) {
logger.severe(errorMessage);
} }
} }
@@ -488,6 +573,7 @@ public final class StargateConfig {
* *
* @return <p>The portal folder</p> * @return <p>The portal folder</p>
*/ */
@NotNull
public String getPortalFolder() { public String getPortalFolder() {
return portalFolder; return portalFolder;
} }
@@ -499,25 +585,57 @@ public final class StargateConfig {
* *
* @return <p>The folder storing gate files</p> * @return <p>The folder storing gate files</p>
*/ */
@NotNull
public String getGateFolder() { public String getGateFolder() {
return gateFolder; return gateFolder;
} }
/**
* Gets the sender for sending messages to players
*
* @return <p>The sender for sending messages to players</p>
*/
public MessageSender getMessageSender() {
return messageSender;
}
/** /**
* Gets the language loader containing translated strings * Gets the language loader containing translated strings
* *
* @return <p>The language loader</p> * @return <p>The language loader</p>
*/ */
@NotNull
public LanguageLoader getLanguageLoader() { public LanguageLoader getLanguageLoader() {
return languageLoader; return languageLoader;
} }
/**
* Gets the string formatter to use
*/
@NotNull
private StringFormatter getStringFormatter() {
// In order to allow automatic customization of prefix color, parse it properly
String rawPrefix = getLanguageLoader().getString(Message.PREFIX);
String colorPattern = "(?:[&§][a-fA-F0-9klmnor]|&?#[0-9a-fA-F]{6}|§x(?:§[a-fA-F0-9]){6})*";
Pattern pattern = Pattern.compile("(" + colorPattern + "\\[" + colorPattern + ")(\\w+)(" +
colorPattern + "]" + colorPattern + ")");
return getStringFormatter(rawPrefix, pattern);
}
/**
* Gets the string formatter to use
*
* @param rawPrefix <p>The formatter prefix to parse</p>
* @param pattern <p>The pattern to use for parsing</p>
*/
private static @NotNull StringFormatter getStringFormatter(String rawPrefix, Pattern pattern) {
String prefix = rawPrefix;
String namePrefix = "[";
String nameSuffix = "]";
Matcher matcher = pattern.matcher(rawPrefix);
if (matcher.find()) {
namePrefix = matcher.group(1).trim();
prefix = matcher.group(2).trim();
nameSuffix = matcher.group(3).trim();
}
StringFormatter stringFormatter = new StringFormatter(prefix, new Translator());
stringFormatter.setColorConversion(ColorConversion.RGB);
stringFormatter.setNamePrefix(namePrefix);
stringFormatter.setNameSuffix(nameSuffix);
return stringFormatter;
}
} }

View File

@@ -1,11 +1,12 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config;
import net.knarcraft.knarlib.util.ColorHelper;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.PortalSignDrawer; import net.knarcraft.stargate.portal.PortalSignDrawer;
import net.knarcraft.stargate.utility.ColorHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.Material; import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -16,6 +17,7 @@ import java.util.Map;
* The Stargate gate config keeps track of all global config values related to gates * The Stargate gate config keeps track of all global config values related to gates
*/ */
public final class StargateGateConfig { public final class StargateGateConfig {
private static final int activeTime = 10; private static final int activeTime = 10;
private static final int openTime = 10; private static final int openTime = 10;
private final Map<ConfigOption, Object> configOptions; private final Map<ConfigOption, Object> configOptions;
@@ -25,7 +27,7 @@ public final class StargateGateConfig {
* *
* @param configOptions <p>The loaded config options to use</p> * @param configOptions <p>The loaded config options to use</p>
*/ */
public StargateGateConfig(Map<ConfigOption, Object> configOptions) { public StargateGateConfig(@NotNull Map<ConfigOption, Object> configOptions) {
this.configOptions = configOptions; this.configOptions = configOptions;
loadGateConfig(); loadGateConfig();
} }
@@ -99,6 +101,17 @@ public final class StargateGateConfig {
return (boolean) configOptions.get(ConfigOption.HANDLE_CREATURE_TRANSPORTATION); return (boolean) configOptions.get(ConfigOption.HANDLE_CREATURE_TRANSPORTATION);
} }
/**
* Gets the delay to wait between each update of a Stargate's control block
*
* <p>This only affects the queued control updates during startup. It does not affect normal gameplay.</p>
*
* @return <p>The amount of ticks to delay control updates by</p>
*/
public int controlUpdateDelay() {
return (int) configOptions.get(ConfigOption.CONTROL_UPDATE_QUEUE_DELAY);
}
/** /**
* Gets whether vehicles containing a creature, but not a player should be handled * Gets whether vehicles containing a creature, but not a player should be handled
* *
@@ -125,6 +138,29 @@ public final class StargateGateConfig {
return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES); return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES);
} }
/**
* Gets whether the CraftBook vehicle removal fix is enabled
*
* <p>If enabled, minecarts and boats should be re-created instead of teleported.</p>
*
* @return <p>True if the CraftBook vehicle removal fix is enabled</p>
*/
public boolean enableCraftBookRemoveOnEjectFix() {
return (boolean) configOptions.get(ConfigOption.ENABLE_CRAFT_BOOK_REMOVE_ON_EJECT_FIX);
}
/**
* Gets the delay to use before adding a player as passenger of a teleported vehicle
*
* @return <p>The delay to use before adding a player as passenger of a teleported vehicle</p>
*/
public int waitForPlayerAfterTeleportDelay() {
if ((int) configOptions.get(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY) < 2) {
configOptions.put(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY, 6);
}
return (int) configOptions.get(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY);
}
/** /**
* Gets whether the list of destinations within a network should be sorted * Gets whether the list of destinations within a network should be sorted
* *
@@ -224,8 +260,8 @@ public final class StargateGateConfig {
* @param defaultColors <p>The specified default colors</p> * @param defaultColors <p>The specified default colors</p>
* @param colorMaps <p>The list of color maps to save the resulting colors to</p> * @param colorMaps <p>The list of color maps to save the resulting colors to</p>
*/ */
private void parsePerSignColors(Object signColorSpecification, ChatColor[] defaultColors, private void parsePerSignColors(@NotNull Object signColorSpecification, @NotNull ChatColor[] defaultColors,
List<Map<Material, ChatColor>> colorMaps) { @NotNull List<Map<Material, ChatColor>> colorMaps) {
String[] specificationData = String.valueOf(signColorSpecification).split(":"); String[] specificationData = String.valueOf(signColorSpecification).split(":");
Material[] signMaterials = new Material[]{Material.matchMaterial(specificationData[0] + "_SIGN"), Material[] signMaterials = new Material[]{Material.matchMaterial(specificationData[0] + "_SIGN"),
Material.matchMaterial(specificationData[0] + "_WALL_SIGN")}; Material.matchMaterial(specificationData[0] + "_WALL_SIGN")};
@@ -256,14 +292,14 @@ public final class StargateGateConfig {
* @param signMaterials <p>The materials to load this color for</p> * @param signMaterials <p>The materials to load this color for</p>
* @param colorMaps <p>The list of color maps to save the resulting color to</p> * @param colorMaps <p>The list of color maps to save the resulting color to</p>
*/ */
private void loadPerSignColor(String[] colors, int colorIndex, ChatColor[] defaultColors, Material[] signMaterials, private void loadPerSignColor(@NotNull String[] colors, int colorIndex, @NotNull ChatColor[] defaultColors,
List<Map<Material, ChatColor>> colorMaps) { @NotNull Material[] signMaterials, @NotNull List<Map<Material, ChatColor>> colorMaps) {
ChatColor parsedColor; ChatColor parsedColor;
if (colors[colorIndex].equalsIgnoreCase("inverted")) { if (colors[colorIndex].equalsIgnoreCase("inverted")) {
//Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor //Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor
java.awt.Color color = defaultColors[colorIndex].getColor(); java.awt.Color color = defaultColors[colorIndex].getColor();
parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), color.getGreen(),
color.getGreen(), color.getBlue()))); color.getBlue())));
} else { } else {
try { try {
parsedColor = ChatColor.of(colors[colorIndex]); parsedColor = ChatColor.of(colors[colorIndex]);
@@ -285,19 +321,22 @@ public final class StargateGateConfig {
* *
* @param mainSignColor <p>A string representing the main sign color</p> * @param mainSignColor <p>A string representing the main sign color</p>
*/ */
private void loadPerSignColor(String mainSignColor, String highlightSignColor) { private void loadPerSignColor(@NotNull String mainSignColor, @NotNull String highlightSignColor) {
try { try {
PortalSignDrawer.setMainColor(ChatColor.of(mainSignColor.toUpperCase())); PortalSignDrawer.setMainColor(ChatColor.of(mainSignColor.toUpperCase()));
} catch (IllegalArgumentException | NullPointerException exception) { } catch (IllegalArgumentException | NullPointerException exception) {
Stargate.logWarning("You have specified an invalid main sign color in your config.yml. Defaulting to BLACK"); Stargate.logWarning("You have specified an invalid main sign color in your config.yml (" + mainSignColor +
"). Defaulting to BLACK");
PortalSignDrawer.setMainColor(ChatColor.BLACK); PortalSignDrawer.setMainColor(ChatColor.BLACK);
} }
try { try {
PortalSignDrawer.setHighlightColor(ChatColor.of(highlightSignColor.toUpperCase())); PortalSignDrawer.setHighlightColor(ChatColor.of(highlightSignColor.toUpperCase()));
} catch (IllegalArgumentException | NullPointerException exception) { } catch (IllegalArgumentException | NullPointerException exception) {
Stargate.logWarning("You have specified an invalid highlighting sign color in your config.yml. Defaulting to WHITE"); Stargate.logWarning("You have specified an invalid highlighting sign color in your config.yml (" +
highlightSignColor + "). Defaulting to WHITE");
PortalSignDrawer.setHighlightColor(ChatColor.WHITE); PortalSignDrawer.setHighlightColor(ChatColor.WHITE);
} }
} }
} }

View File

@@ -0,0 +1,160 @@
package net.knarcraft.stargate.config.addons;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry;
import org.bukkit.Location;
import org.bukkit.World;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.GenericMarker;
import org.dynmap.markers.Marker;
import org.dynmap.markers.MarkerIcon;
import org.dynmap.markers.MarkerSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A manager for dealing with everything Dynmap
*/
public final class DynmapManager {
private static MarkerSet markerSet;
private static MarkerIcon portalIcon;
private DynmapManager() {
}
/**
* Initializes the dynmap manager
*
* @param dynmapAPI <p>A reference</p>
* @throws NullPointerException <p>If dynmap has an invalid state</p>
*/
public static void initialize(@Nullable DynmapAPI dynmapAPI) throws NullPointerException {
if (dynmapAPI == null || !dynmapAPI.markerAPIInitialized() || dynmapAPI.getMarkerAPI() == null) {
markerSet = null;
portalIcon = null;
} else {
markerSet = dynmapAPI.getMarkerAPI().createMarkerSet("stargate", "Stargate", null, false);
if (markerSet != null) {
markerSet.setHideByDefault(Stargate.getStargateConfig().hideDynmapIcons());
}
portalIcon = dynmapAPI.getMarkerAPI().getMarkerIcon("portal");
}
}
/**
* Adds all portal markers for all current portals
*/
public static void addAllPortalMarkers() {
if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) {
//Remove any existing markers if dynmap has been disabled after startup
if (markerSet != null) {
markerSet.getMarkers().forEach(GenericMarker::deleteMarker);
}
return;
}
markerSet.setHideByDefault(Stargate.getStargateConfig().hideDynmapIcons());
//Remove all existing markers for a clean start
markerSet.getMarkers().forEach(GenericMarker::deleteMarker);
for (Portal portal : PortalRegistry.getAllPortals()) {
addPortalMarker(portal);
}
}
/**
* Adds a portal marker for the given portal
*
* @param portal <p>The portal to add a marker for</p>
*/
public static void addPortalMarker(@NotNull Portal portal) {
if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) {
return;
}
World world = portal.getLocation().getWorld();
if (portal.getOptions().isHidden() || world == null) {
return;
}
Location location;
@Nullable RelativeBlockVector exit = portal.getGate().getLayout().getExit();
if (exit == null) {
location = portal.getLocation().getTopLeft();
} else {
location = portal.getBlockAt(exit);
}
String markerId = getPortalMarkerId(portal);
if (markerSet.findMarker(markerId) != null) {
Stargate.debug("DynmapManager::addPortalMarker", "Skipped marker creation, as the portal " +
"marker " + markerId + " already exists");
return;
}
Marker marker = markerSet.createMarker(markerId, portal.getName(), world.getName(),
location.getX(), location.getY(), location.getZ(), portalIcon, false);
if (marker == null) {
Stargate.logWarning(String.format(
"""
Unable to create marker for portal
Portal marker id: %s
Portal name: %s
Portal world: %s
Portal location: %s,%s,%s""",
markerId, portal.getName(), world.getName(), location.getX(), location.getY(),
location.getZ()));
return;
}
String networkPrompt;
if (portal.getOptions().isBungee()) {
networkPrompt = "Server";
} else {
networkPrompt = "Network";
}
String markerDescription = String.format("<b>Name:</b> %s<br /><b>%s:</b> %s<br /><b>Destination:</b> " +
"%s<br /><b>Owner:</b> %s<br />", portal.getName(), networkPrompt, portal.getNetwork(),
portal.getDestinationName(), portal.getOwner().getName());
marker.setDescription(markerDescription);
marker.setLabel(portal.getName(), true);
if (portalIcon != null) {
marker.setMarkerIcon(portalIcon);
}
}
/**
* Removes the portal marker for the given portal
*
* @param portal <p>The portal to remove the marker for</p>
*/
public static void removePortalMarker(@NotNull Portal portal) {
if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) {
return;
}
Marker marker = markerSet.findMarker(getPortalMarkerId(portal));
if (marker != null) {
marker.deleteMarker();
}
}
/**
* Disables Dynmap integration
*/
public static void disable() {
markerSet = null;
portalIcon = null;
}
/**
* Gets the id used for the given portal's marker
*
* @param portal <p>The portal to get a marker id for</p>
* @return <p></p>
*/
private static String getPortalMarkerId(@NotNull Portal portal) {
return portal.getNetwork() + "-:-" + portal.getName();
}
}

View File

@@ -1,6 +1,10 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config.addons;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.ConfigOption;
import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.PortalSignDrawer; import net.knarcraft.stargate.portal.PortalSignDrawer;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PermissionHelper;
@@ -11,6 +15,8 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
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 org.jetbrains.annotations.Nullable;
import java.util.Map; import java.util.Map;
@@ -29,7 +35,7 @@ public final class EconomyConfig {
* *
* @param configOptions <p>The loaded config options to read</p> * @param configOptions <p>The loaded config options to read</p>
*/ */
public EconomyConfig(Map<ConfigOption, Object> configOptions) { public EconomyConfig(@NotNull Map<ConfigOption, Object> configOptions) {
this.configOptions = configOptions; this.configOptions = configOptions;
try { try {
String freeColor = (String) configOptions.get(ConfigOption.FREE_GATES_COLOR); String freeColor = (String) configOptions.get(ConfigOption.FREE_GATES_COLOR);
@@ -62,6 +68,7 @@ public final class EconomyConfig {
* *
* @return <p>An economy object, or null if economy is disabled or not initialized</p> * @return <p>An economy object, or null if economy is disabled or not initialized</p>
*/ */
@Nullable
public Economy getEconomy() { public Economy getEconomy() {
return economy; return economy;
} }
@@ -71,6 +78,7 @@ public final class EconomyConfig {
* *
* @return <p>An instance of the Vault plugin, or null if Vault is not loaded</p> * @return <p>An instance of the Vault plugin, or null if Vault is not loaded</p>
*/ */
@Nullable
public Plugin getVault() { public Plugin getVault() {
return vault; return vault;
} }
@@ -132,6 +140,16 @@ public final class EconomyConfig {
return (Integer) configOptions.get(ConfigOption.DESTROY_COST); return (Integer) configOptions.get(ConfigOption.DESTROY_COST);
} }
/**
* Gets the account all taxes are paid to
*
* @return <p>The account all taxes are paid to</p>
*/
@Nullable
public String getTaxAccount() {
return (String) configOptions.get(ConfigOption.TAX_ACCOUNT);
}
/** /**
* Checks whether the given player can afford the given fee * Checks whether the given player can afford the given fee
* *
@@ -149,6 +167,7 @@ public final class EconomyConfig {
* @param amount <p>The amount to display</p> * @param amount <p>The amount to display</p>
* @return <p>A formatted text string describing the amount</p> * @return <p>A formatted text string describing the amount</p>
*/ */
@NotNull
public String format(int amount) { public String format(int amount) {
if (isEconomyEnabled()) { if (isEconomyEnabled()) {
return economy.format(amount); return economy.format(amount);
@@ -163,7 +182,7 @@ public final class EconomyConfig {
* @param pluginManager <p>The plugin manager to get plugins from</p> * @param pluginManager <p>The plugin manager to get plugins from</p>
* @return <p>True if economy was enabled</p> * @return <p>True if economy was enabled</p>
*/ */
public boolean setupEconomy(PluginManager pluginManager) { public boolean setupEconomy(@NotNull PluginManager pluginManager) {
if (!isEconomyEnabled()) { if (!isEconomyEnabled()) {
return false; return false;
} }
@@ -177,10 +196,10 @@ public final class EconomyConfig {
this.vault = vault; this.vault = vault;
return true; return true;
} else { } else {
Stargate.logInfo(Stargate.getString("ecoLoadError")); Stargate.logInfo(new SGFormatBuilder(Message.ECONOMY_LOAD_ERROR).toString());
} }
} else { } else {
Stargate.logInfo(Stargate.getString("vaultLoadError")); Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOAD_ERROR).toString());
} }
configOptions.put(ConfigOption.USE_ECONOMY, false); configOptions.put(ConfigOption.USE_ECONOMY, false);
return false; return false;
@@ -202,8 +221,8 @@ public final class EconomyConfig {
* @param gate <p>The gate type used</p> * @param gate <p>The gate type used</p>
* @return <p>The cost of creating the gate</p> * @return <p>The cost of creating the gate</p>
*/ */
public int getCreateCost(Player player, Gate gate) { public int getCreateCost(@NotNull Player player, @NotNull Gate gate) {
if (isFree(player, "create")) { if (isFree(player, Permission.FREE_CREATION)) {
return 0; return 0;
} else { } else {
return gate.getCreateCost(); return gate.getCreateCost();
@@ -217,8 +236,8 @@ public final class EconomyConfig {
* @param gate <p>The gate type used</p> * @param gate <p>The gate type used</p>
* @return <p>The cost of destroying the gate</p> * @return <p>The cost of destroying the gate</p>
*/ */
public int getDestroyCost(Player player, Gate gate) { public int getDestroyCost(@NotNull Player player, @NotNull Gate gate) {
if (isFree(player, "destroy")) { if (isFree(player, Permission.FREE_DESTRUCTION)) {
return 0; return 0;
} else { } else {
return gate.getDestroyCost(); return gate.getDestroyCost();
@@ -228,12 +247,12 @@ public final class EconomyConfig {
/** /**
* Determines if a player can do a gate action for free * Determines if a player can do a gate action for free
* *
* @param player <p>The player to check</p> * @param player <p>The player to check</p>
* @param permissionNode <p>The free.permissionNode necessary to allow free gate {action}</p> * @param permission <p>The permission necessary to allow free gate {action}</p>
* @return <p></p> * @return <p></p>
*/ */
private boolean isFree(Player player, String permissionNode) { private boolean isFree(@NotNull Player player, @NotNull Permission permission) {
return !useEconomy() || PermissionHelper.hasPermission(player, "stargate.free." + permissionNode); return !useEconomy() || PermissionHelper.hasPermission(player, permission.getNode());
} }
} }

View File

@@ -1,16 +1,18 @@
package net.knarcraft.stargate.config; package net.knarcraft.stargate.config.formatting;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.FileHelper;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.FileHelper; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* This class is responsible for loading all strings which are translated into several languages * This class is responsible for loading all strings which are translated into several languages
@@ -18,9 +20,9 @@ import java.util.Set;
public final class LanguageLoader { public final class LanguageLoader {
private final String languageFolder; private final String languageFolder;
private final Map<String, String> loadedBackupStrings; private final Map<Message, String> loadedBackupStrings;
private String chosenLanguage; private String chosenLanguage;
private Map<String, String> loadedStringTranslations; private Map<Message, String> loadedStringTranslations;
/** /**
* Instantiates a new language loader * Instantiates a new language loader
@@ -29,7 +31,7 @@ public final class LanguageLoader {
* *
* @param languageFolder <p>The folder containing the language files</p> * @param languageFolder <p>The folder containing the language files</p>
*/ */
public LanguageLoader(String languageFolder) { public LanguageLoader(@NotNull String languageFolder) {
this.languageFolder = languageFolder; this.languageFolder = languageFolder;
File testFile = new File(languageFolder, "en.txt"); File testFile = new File(languageFolder, "en.txt");
if (!testFile.exists()) { if (!testFile.exists()) {
@@ -44,7 +46,7 @@ public final class LanguageLoader {
loadedBackupStrings = load("en", inputStream); loadedBackupStrings = load("en", inputStream);
} else { } else {
loadedBackupStrings = null; loadedBackupStrings = null;
Stargate.getConsoleLogger().severe("[stargate] Error loading backup language. " + Stargate.logSevere("Error loading backup language. " +
"There may be missing text in-game"); "There may be missing text in-game");
} }
} }
@@ -59,32 +61,34 @@ public final class LanguageLoader {
} }
/** /**
* Gets the string to display given its name/key * Gets the string to display given its message key
* *
* @param name <p>The name/key of the string to display</p> * @param message <p>The message to display</p>
* @return <p>The string in the user's preferred language</p> * @return <p>The message in the user's preferred language</p>
*/ */
public String getString(String name) { @NotNull
public String getString(@NotNull Message message) {
String value = null; String value = null;
if (loadedStringTranslations != null) { if (loadedStringTranslations != null) {
value = loadedStringTranslations.get(name); value = loadedStringTranslations.get(message);
} }
if (value == null) { if (value == null) {
value = getBackupString(name); value = getBackupString(message);
} }
return value; return value;
} }
/** /**
* Gets the string to display given its name/key * Gets the string to display given its message key
* *
* @param name <p>The name/key of the string to display</p> * @param message <p>The message to display</p>
* @return <p>The string in the backup language (English)</p> * @return <p>The string in the backup language (English)</p>
*/ */
public String getBackupString(String name) { @NotNull
public String getBackupString(@NotNull Message message) {
String value = null; String value = null;
if (loadedBackupStrings != null) { if (loadedBackupStrings != null) {
value = loadedBackupStrings.get(name); value = loadedBackupStrings.get(message);
} }
if (value == null) { if (value == null) {
return ""; return "";
@@ -97,7 +101,7 @@ public final class LanguageLoader {
* *
* @param chosenLanguage <p>The new plugin language</p> * @param chosenLanguage <p>The new plugin language</p>
*/ */
public void setChosenLanguage(String chosenLanguage) { public void setChosenLanguage(@NotNull String chosenLanguage) {
this.chosenLanguage = chosenLanguage; this.chosenLanguage = chosenLanguage;
} }
@@ -106,21 +110,21 @@ public final class LanguageLoader {
* *
* @param language <p>The language to update</p> * @param language <p>The language to update</p>
*/ */
private void updateLanguage(String language) { private void updateLanguage(@NotNull String language) {
Map<String, String> currentLanguageValues = load(language); Map<Message, String> currentLanguageValues = load(language);
InputStream inputStream = getClass().getResourceAsStream("/lang/" + language + ".txt"); InputStream inputStream = FileHelper.getInputStreamForInternalFile("/lang/" + language + ".txt");
if (inputStream == null) { if (inputStream == null) {
Stargate.logInfo(String.format("The language %s is not available. Falling back to english, You can add a " + Stargate.logInfo(String.format("Unable to find internal language file for %s. This will normally not " +
"custom language by creating a new text file in the lang directory.", language)); "cause any problems, except newly added translatable strings won't be automatically added", language));
Stargate.debug("LanguageLoader::updateLanguage", String.format("Unable to load /lang/%s.txt", language)); Stargate.debug("LanguageLoader::updateLanguage", String.format("Unable to load /lang/%s.txt", language));
return; return;
} }
try { try {
readChangedLanguageStrings(inputStream, language, currentLanguageValues); readChangedLanguageStrings(inputStream, language, currentLanguageValues);
} catch (IOException ex) { } catch (IOException exception) {
ex.printStackTrace(); Stargate.logSevere("Unable to read language strings! Message: " + exception.getMessage());
} }
} }
@@ -132,11 +136,12 @@ public final class LanguageLoader {
* @param currentLanguageValues <p>The current values of the loaded/processed language</p> * @param currentLanguageValues <p>The current values of the loaded/processed language</p>
* @throws IOException <p>if unable to read a language file</p> * @throws IOException <p>if unable to read a language file</p>
*/ */
private void readChangedLanguageStrings(InputStream inputStream, String language, Map<String, private void readChangedLanguageStrings(@NotNull InputStream inputStream, @NotNull String language,
String> currentLanguageValues) throws IOException { @Nullable Map<Message, String> currentLanguageValues) throws IOException {
//Get language values //Get language values
BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream);
Map<String, String> internalLanguageValues = FileHelper.readKeyValuePairs(bufferedReader); Map<Message, String> internalLanguageValues = fromStringMap(FileHelper.readKeyValuePairs(bufferedReader,
"=", ColorConversion.NORMAL));
//If currentLanguageValues is null; the chosen language has not been used before //If currentLanguageValues is null; the chosen language has not been used before
if (currentLanguageValues == null) { if (currentLanguageValues == null) {
@@ -147,11 +152,14 @@ public final class LanguageLoader {
//If a key is not found in the language file, add the one in the internal file. Must update the external file //If a key is not found in the language file, add the one in the internal file. Must update the external file
if (!internalLanguageValues.keySet().equals(currentLanguageValues.keySet())) { if (!internalLanguageValues.keySet().equals(currentLanguageValues.keySet())) {
Map<String, String> newLanguageValues = new HashMap<>(); Map<Message, String> newLanguageValues = new EnumMap<>(Message.class);
boolean updateNecessary = false; boolean updateNecessary = false;
for (String key : internalLanguageValues.keySet()) { for (Map.Entry<Message, String> entry : internalLanguageValues.entrySet()) {
Message key = entry.getKey();
String value = entry.getValue();
if (currentLanguageValues.get(key) == null) { if (currentLanguageValues.get(key) == null) {
newLanguageValues.put(key, internalLanguageValues.get(key)); newLanguageValues.put(key, value);
//Found at least one value in the internal file not in the external file. Need to update //Found at least one value in the internal file not in the external file. Need to update
updateNecessary = true; updateNecessary = true;
} else { } else {
@@ -175,20 +183,20 @@ public final class LanguageLoader {
* @param customLanguageStrings <p>Any custom language strings not recognized</p> * @param customLanguageStrings <p>Any custom language strings not recognized</p>
* @throws IOException <p>If unable to write to the language file</p> * @throws IOException <p>If unable to write to the language file</p>
*/ */
private void updateLanguageFile(String language, Map<String, String> languageStrings, private void updateLanguageFile(@NotNull String language, @NotNull Map<Message, String> languageStrings,
Map<String, String> customLanguageStrings) throws IOException { @Nullable Map<Message, String> customLanguageStrings) throws IOException {
BufferedWriter bufferedWriter = FileHelper.getBufferedWriterFromString(languageFolder + language + ".txt"); BufferedWriter bufferedWriter = FileHelper.getBufferedWriterFromString(languageFolder + language + ".txt");
//Output normal Language data //Output normal Language data
for (String key : languageStrings.keySet()) { for (Map.Entry<Message, String> entry : languageStrings.entrySet()) {
bufferedWriter.write(key + "=" + languageStrings.get(key)); bufferedWriter.write(entry.getKey() + "=" + entry.getValue());
bufferedWriter.newLine(); bufferedWriter.newLine();
} }
bufferedWriter.newLine(); bufferedWriter.newLine();
//Output any custom language strings the user had //Output any custom language strings the user had
if (customLanguageStrings != null) { if (customLanguageStrings != null) {
for (String key : customLanguageStrings.keySet()) { for (Map.Entry<Message, String> entry : customLanguageStrings.entrySet()) {
bufferedWriter.write(key + "=" + customLanguageStrings.get(key)); bufferedWriter.write(entry.getKey() + "=" + entry.getValue());
bufferedWriter.newLine(); bufferedWriter.newLine();
} }
} }
@@ -201,7 +209,8 @@ public final class LanguageLoader {
* @param lang <p>The language to load</p> * @param lang <p>The language to load</p>
* @return <p>A mapping between loaded string indexes and the strings to display</p> * @return <p>A mapping between loaded string indexes and the strings to display</p>
*/ */
private Map<String, String> load(String lang) { @Nullable
private Map<Message, String> load(@NotNull String lang) {
return load(lang, null); return load(lang, null);
} }
@@ -212,8 +221,8 @@ public final class LanguageLoader {
* @param inputStream <p>An optional input stream to use. Defaults to using a file input stream</p> * @param inputStream <p>An optional input stream to use. Defaults to using a file input stream</p>
* @return <p>A mapping between loaded string indexes and the strings to display</p> * @return <p>A mapping between loaded string indexes and the strings to display</p>
*/ */
private Map<String, String> load(String lang, InputStream inputStream) { @Nullable
Map<String, String> strings; private Map<Message, String> load(@NotNull String lang, @Nullable InputStream inputStream) {
BufferedReader bufferedReader; BufferedReader bufferedReader;
try { try {
if (inputStream == null) { if (inputStream == null) {
@@ -221,14 +230,13 @@ public final class LanguageLoader {
} else { } else {
bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream);
} }
strings = FileHelper.readKeyValuePairs(bufferedReader); return fromStringMap(FileHelper.readKeyValuePairs(bufferedReader, "=", ColorConversion.NORMAL));
} catch (Exception e) { } catch (Exception exception) {
if (Stargate.getStargateConfig().isDebuggingEnabled()) { if (Stargate.getStargateConfig().isDebuggingEnabled()) {
Stargate.getConsoleLogger().info("[Stargate] Unable to load language " + lang); Stargate.logInfo("Unable to load language " + lang);
} }
return null; return null;
} }
return strings;
} }
/** /**
@@ -236,20 +244,41 @@ public final class LanguageLoader {
*/ */
public void debug() { public void debug() {
if (loadedStringTranslations != null) { if (loadedStringTranslations != null) {
Set<String> keys = loadedStringTranslations.keySet(); for (Map.Entry<Message, String> entry : loadedStringTranslations.entrySet()) {
for (String key : keys) { Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", entry.getKey() +
Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", key + " => " + " => " + entry.getValue());
loadedStringTranslations.get(key));
} }
} }
if (loadedBackupStrings == null) { if (loadedBackupStrings == null) {
return; return;
} }
Set<String> keys = loadedBackupStrings.keySet();
for (String key : keys) { for (Map.Entry<Message, String> entry : loadedBackupStrings.entrySet()) {
Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", key + " => " + Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", entry.getKey() + " => " +
loadedBackupStrings.get(key)); entry.getValue());
} }
} }
/**
* Converts a map from string key to message into a map from message key to message
*
* @param configurationStrings <p>The map to convert</p>
* @return <p>The converted map</p>
*/
@NotNull
private Map<Message, String> fromStringMap(@NotNull Map<String, String> configurationStrings) {
Map<Message, String> output = new EnumMap<>(Message.class);
for (Map.Entry<String, String> entry : configurationStrings.entrySet()) {
Message message = Message.getFromKey(entry.getKey());
if (message == null) {
Stargate.logWarning("Found unrecognized language key " + entry.getKey());
continue;
}
output.put(message, entry.getValue());
}
return output;
}
} }

View File

@@ -0,0 +1,251 @@
package net.knarcraft.stargate.config.formatting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Translated messages displayed to players
*/
public enum Message {
/**
* The prefix displayed in front of all messages shown in the chat
*/
PREFIX("prefix"),
/**
* The message displayed when a player is teleported
*/
TELEPORTED("teleportMsg"),
/**
* The message displayed when a player destroys a Stargate
*/
DESTROYED("destroyMsg"),
/**
* The message displayed when the currently selected Stargate destination is invalid
*/
INVALID_DESTINATION("invalidMsg"),
/**
* The message displayed when the destination portal is busy with another player
*/
DESTINATION_BLOCKED("blockMsg"),
/**
* The message displayed when the Stargate has no destinations available to the player
*/
NO_DESTINATION("destEmpty"),
/**
* The message displayed when a player is denied access to any action
*/
ACCESS_DENIED("denyMsg"),
/**
* The message displayed when the plugin is reloaded
*/
RELOADED("reloaded"),
/**
* The message displayed when a player has some currency deducted from their account
*/
ECONOMY_DEDUCTED("ecoDeduct"),
/**
* The message displayed when a player has some currency refunded to their account
*/
ECONOMY_REFUNDED("ecoRefund"),
/**
* The message displayed when a player obtains some currency to their account (from portal usage)
*/
ECONOMY_OBTAINED("ecoObtain"),
/**
* The message displayed when the player has an insufficient amount of currency to perform an action
*/
ECONOMY_INSUFFICIENT("ecoInFunds"),
/**
* The message displayed when economy fails to load
*/
ECONOMY_LOAD_ERROR("ecoLoadError"),
/**
* The message displayed when Vault fails to load
*/
VAULT_LOAD_ERROR("vaultLoadError"),
/**
* The message displayed when Vault successfully loads
*/
VAULT_LOADED("vaultLoaded"),
/**
* The message displayed when a Stargate is successfully created
*/
CREATED("createMsg"),
/**
* The message displayed when a player is denied from creating a Stargate on the selected network
*/
CREATION_NETWORK_DENIED("createNetDeny"),
/**
* The message displayed when a player is denied from creating a Stargate of the given gate type
*/
CREATION_GATE_DENIED("createGateDeny"),
/**
* The message displayed when a Stargate is created on the player's personal network
*/
CREATION_PERSONAL("createPersonal"),
/**
* The message displayed when the name of a Stargate is too short or too long
*/
CREATION_NAME_LENGTH("createNameLength"),
/**
* The message displayed when another Stargate on the network has the same name as the new Stargate
*/
CREATION_NAME_COLLISION("createExists"),
/**
* The message displayed when the specified network is full
*/
CREATION_NETWORK_FULL("createFull"),
/**
* The message displayed when a player is denied from creating a Stargate in the current world
*/
CREATION_WORLD_DENIED("createWorldDeny"),
/**
* The message displayed when a gate is physically conflicting with another
*/
CREATION_CONFLICT("createConflict"),
/**
* The right-click prompt displayed on Stargate signs
*/
SIGN_RIGHT_CLICK("signRightClick"),
/**
* The to use prompt displayed on Stargate signs
*/
SIGN_TO_USE("signToUse"),
/**
* The random string displayed on Stargate signs
*/
SIGN_RANDOM("signRandom"),
/**
* The disconnected string displayed on Stargate signs
*/
SIGN_DISCONNECTED("signDisconnected"),
/**
* The invalid gate string displayed on Stargate signs
*/
SIGN_INVALID("signInvalidGate"),
/**
* The message displayed if trying to create a bungee gate when bungee is disabled
*/
BUNGEE_DISABLED("bungeeDisabled"),
/**
* The message displayed when a player is denied from creating a bungee Stargate
*/
BUNGEE_CREATION_DENIED("bungeeDeny"),
/**
* The message displayed if a Stargate is missing the destination, the network or both
*/
BUNGEE_MISSING_INFO("bungeeEmpty"),
/**
* The teleportation prompt shown on bungee signs
*/
BUNGEE_SIGN("bungeeSign"),
/**
* The format of the title of the portal info shown in chat
*/
PORTAL_INFO_TITLE("portalInfoTitle"),
/**
* The format of the name of the portal info shown in chat
*/
PORTAL_INFO_NAME("portalInfoName"),
/**
* The format of the destination of the portal info shown in chat
*/
PORTAL_INFO_DESTINATION("portalInfoDestination"),
/**
* The format of the network of the portal info shown in chat
*/
PORTAL_INFO_NETWORK("portalInfoNetwork"),
/**
* The format of the server of the portal info shown in chat
*/
PORTAL_INFO_SERVER("portalInfoServer"),
/**
* The author that created the loaded translation
*/
AUTHOR("author"),
;
private final String key;
/**
* Instantiates a new message
*
* @param key <p>The key of the message in the language files</p>
*/
Message(@NotNull String key) {
this.key = key;
}
/**
* Gets the language file key for this message
*
* @return <p>This message's key</p>
*/
@NotNull
public String getKey() {
return this.key;
}
/**
* Gets the message corresponding to the given key
*
* @param key <p>The key to get a message from</p>
* @return <p>The message, or null if not found</p>
*/
@Nullable
public static Message getFromKey(@NotNull String key) {
for (Message message : Message.values()) {
if (message.getKey().equalsIgnoreCase(key)) {
return message;
}
}
return null;
}
@Override
@NotNull
public String toString() {
return this.getKey();
}
}

View File

@@ -0,0 +1,40 @@
package net.knarcraft.stargate.config.formatting;
import net.knarcraft.knarlib.formatting.FormatBuilder;
import net.knarcraft.stargate.Stargate;
import org.jetbrains.annotations.NotNull;
/**
* A customized format builder for automatic translation of Stargate messages
*/
public class SGFormatBuilder extends FormatBuilder {
/**
* Instantiates a new format builder
*/
public SGFormatBuilder() {
super();
}
/**
* Instantiates a new format builder
*
* <p>If the input is a list, it will be joined using the default delimiter: ",".</p>
*
* @param input <p>The input to use as the initial string of this format builder</p>
* @throws IllegalStateException <p>If the string formatter has not been set, and the input is a translatable message</p>
*/
public <K> SGFormatBuilder(@NotNull K input) throws IllegalStateException {
super(input);
}
@Override
@NotNull
protected <K> String asString(@NotNull K input, @NotNull String delimiter) {
if (input instanceof Message message) {
return Stargate.getStargateConfig().getLanguageLoader().getString(message);
}
return super.asString(input, delimiter);
}
}

View File

@@ -0,0 +1,49 @@
package net.knarcraft.stargate.config.material;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* A specifier for a Bukkit material
*/
public class BukkitMaterialSpecifier implements MaterialSpecifier {
private final Material material;
/**
* Instantiates a new material specifier
*
* @param material <p>The material to specify</p>
*/
public BukkitMaterialSpecifier(@NotNull Material material) {
this.material = material;
}
@Override
@NotNull
public String asString() {
return this.material.name();
}
@Override
@NotNull
public Set<Material> asMaterials() {
return Set.of(this.material);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof BukkitMaterialSpecifier bukkitMaterialSpecifier)) {
return false;
}
return this.material == bukkitMaterialSpecifier.material;
}
@Override
public int hashCode() {
return material.hashCode();
}
}

View File

@@ -0,0 +1,49 @@
package net.knarcraft.stargate.config.material;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* A specifier for a Bukkit material tag
*/
public class BukkitTagSpecifier implements MaterialSpecifier {
private final Tag<Material> tag;
/**
* Instantiates a new tag specifier
*
* @param tag <p>The tag to specify</p>
*/
public BukkitTagSpecifier(@NotNull Tag<Material> tag) {
this.tag = tag;
}
@Override
public @NotNull String asString() {
return "#" + this.tag.getKey().toString().replaceFirst("minecraft:", "");
}
@Override
public @NotNull Set<Material> asMaterials() {
return this.tag.getValues();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof BukkitTagSpecifier bukkitMaterialSpecifier)) {
return false;
}
return this.tag == bukkitMaterialSpecifier.tag;
}
@Override
public int hashCode() {
return tag.hashCode();
}
}

View File

@@ -0,0 +1,29 @@
package net.knarcraft.stargate.config.material;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* An interface describing a specifier for one or more Bukkit materials
*/
public interface MaterialSpecifier {
/**
* Gets the string representation of the material specifier
*
* <p>This is used when saving the value to a gate file</p>
*/
@NotNull
String asString();
/**
* Gets all the materials the specifier specifies
*
* <p>This is used when registering gate materials</p>
*/
@NotNull
Set<Material> asMaterials();
}

View File

@@ -2,13 +2,16 @@ package net.knarcraft.stargate.container;
import org.bukkit.Axis; import org.bukkit.Axis;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Represents a request for changing a block into another material * Represents a request for changing a block into another material
*/ */
public class BlockChangeRequest { public class BlockChangeRequest {
private final BlockLocation blockLocation; private final Block blockLocation;
private final Material newMaterial; private final Material newMaterial;
private final Axis newAxis; private final Axis newAxis;
@@ -19,7 +22,7 @@ public class BlockChangeRequest {
* @param material <p>The new material to change the block to</p> * @param material <p>The new material to change the block to</p>
* @param axis <p>The new axis to orient the block along</p> * @param axis <p>The new axis to orient the block along</p>
*/ */
public BlockChangeRequest(BlockLocation blockLocation, Material material, Axis axis) { public BlockChangeRequest(@NotNull Block blockLocation, @NotNull Material material, @Nullable Axis axis) {
this.blockLocation = blockLocation; this.blockLocation = blockLocation;
newMaterial = material; newMaterial = material;
newAxis = axis; newAxis = axis;
@@ -30,7 +33,8 @@ public class BlockChangeRequest {
* *
* @return <p>The location of the block</p> * @return <p>The location of the block</p>
*/ */
public BlockLocation getBlockLocation() { @NotNull
public Block getBlockLocation() {
return blockLocation; return blockLocation;
} }
@@ -39,6 +43,7 @@ public class BlockChangeRequest {
* *
* @return <p>The material to change the block into</p> * @return <p>The material to change the block into</p>
*/ */
@NotNull
public Material getMaterial() { public Material getMaterial() {
return newMaterial; return newMaterial;
} }
@@ -48,6 +53,7 @@ public class BlockChangeRequest {
* *
* @return <p>The axis to orient the block along</p> * @return <p>The axis to orient the block along</p>
*/ */
@Nullable
public Axis getAxis() { public Axis getAxis() {
return newAxis; return newAxis;
} }

View File

@@ -5,11 +5,9 @@ import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Sign;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* This class represents a block location * This class represents a block location
@@ -20,8 +18,6 @@ import org.bukkit.util.Vector;
*/ */
public class BlockLocation extends Location { public class BlockLocation extends Location {
private BlockLocation parent = null;
/** /**
* Creates a new block location * Creates a new block location
* *
@@ -30,7 +26,7 @@ public class BlockLocation extends Location {
* @param y <p>The y coordinate of the block</p> * @param y <p>The y coordinate of the block</p>
* @param z <p>The z coordinate of the block</p> * @param z <p>The z coordinate of the block</p>
*/ */
public BlockLocation(World world, int x, int y, int z) { public BlockLocation(@NotNull World world, int x, int y, int z) {
super(world, x, y, z); super(world, x, y, z);
} }
@@ -39,7 +35,7 @@ public class BlockLocation extends Location {
* *
* @param block <p>The block to get the location of</p> * @param block <p>The block to get the location of</p>
*/ */
public BlockLocation(Block block) { public BlockLocation(@NotNull Block block) {
super(block.getWorld(), block.getX(), block.getY(), block.getZ()); super(block.getWorld(), block.getX(), block.getY(), block.getZ());
} }
@@ -49,7 +45,7 @@ public class BlockLocation extends Location {
* @param world <p>The world the block exists in</p> * @param world <p>The world the block exists in</p>
* @param string <p>A comma separated list of x, y and z coordinates as integers</p> * @param string <p>A comma separated list of x, y and z coordinates as integers</p>
*/ */
public BlockLocation(World world, String string) { public BlockLocation(@NotNull World world, @NotNull String string) {
super(world, Integer.parseInt(string.split(",")[0]), Integer.parseInt(string.split(",")[1]), super(world, Integer.parseInt(string.split(",")[0]), Integer.parseInt(string.split(",")[1]),
Integer.parseInt(string.split(",")[2])); Integer.parseInt(string.split(",")[2]));
} }
@@ -62,6 +58,7 @@ public class BlockLocation extends Location {
* @param z <p>The number of blocks to move in the z-direction</p> * @param z <p>The number of blocks to move in the z-direction</p>
* @return <p>A new block location</p> * @return <p>A new block location</p>
*/ */
@NotNull
public BlockLocation makeRelativeBlockLocation(int x, int y, int z) { public BlockLocation makeRelativeBlockLocation(int x, int y, int z) {
return (BlockLocation) this.clone().add(x, y, z); return (BlockLocation) this.clone().add(x, y, z);
} }
@@ -75,6 +72,7 @@ public class BlockLocation extends Location {
* @param yaw <p>The number of blocks to move in the z-direction</p> * @param yaw <p>The number of blocks to move in the z-direction</p>
* @return <p>A new location</p> * @return <p>A new location</p>
*/ */
@NotNull
public Location makeRelativeLocation(double x, double y, double z, float yaw) { public Location makeRelativeLocation(double x, double y, double z, float yaw) {
Location newLocation = this.clone(); Location newLocation = this.clone();
newLocation.setYaw(yaw); newLocation.setYaw(yaw);
@@ -89,9 +87,10 @@ public class BlockLocation extends Location {
* @param yaw <p>The yaw pointing outwards from a portal (in the relative vector's out direction)</p> * @param yaw <p>The yaw pointing outwards from a portal (in the relative vector's out direction)</p>
* @return <p>A location relative to this location</p> * @return <p>A location relative to this location</p>
*/ */
public BlockLocation getRelativeLocation(RelativeBlockVector relativeVector, double yaw) { @NotNull
Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(relativeVector.getRight(), public BlockLocation getRelativeLocation(@NotNull RelativeBlockVector relativeVector, double yaw) {
relativeVector.getDown(), relativeVector.getOut(), yaw); Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(relativeVector.right(),
relativeVector.down(), relativeVector.out(), yaw);
return makeRelativeBlockLocation(realVector.getBlockX(), realVector.getBlockY(), realVector.getBlockZ()); return makeRelativeBlockLocation(realVector.getBlockX(), realVector.getBlockY(), realVector.getBlockZ());
} }
@@ -107,6 +106,7 @@ public class BlockLocation extends Location {
* @param portalYaw <p>The yaw when looking out from the portal</p> * @param portalYaw <p>The yaw when looking out from the portal</p>
* @return A new location relative to this block location * @return A new location relative to this block location
*/ */
@NotNull
public Location getRelativeLocation(double right, double down, double out, float portalYaw) { public Location getRelativeLocation(double right, double down, double out, float portalYaw) {
Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(right, down, out, portalYaw); Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(right, down, out, portalYaw);
return makeRelativeLocation(0.5 + realVector.getBlockX(), realVector.getBlockY(), return makeRelativeLocation(0.5 + realVector.getBlockX(), realVector.getBlockY(),
@@ -118,6 +118,7 @@ public class BlockLocation extends Location {
* *
* @return <p>The block's material type</p> * @return <p>The block's material type</p>
*/ */
@NotNull
public Material getType() { public Material getType() {
return this.getBlock().getType(); return this.getBlock().getType();
} }
@@ -127,7 +128,7 @@ public class BlockLocation extends Location {
* *
* @param type <p>The block's new material type</p> * @param type <p>The block's new material type</p>
*/ */
public void setType(Material type) { public void setType(@NotNull Material type) {
this.getBlock().setType(type); this.getBlock().setType(type);
} }
@@ -136,54 +137,13 @@ public class BlockLocation extends Location {
* *
* @return <p>The location representing this block location</p> * @return <p>The location representing this block location</p>
*/ */
@NotNull
public Location getLocation() { public Location getLocation() {
return this.clone(); return this.clone();
} }
/**
* Gets this block location's parent block
*
* <p>The parent block is the block the item at this block location is attached to. Usually this is the block a
* sign or wall sign is attached to.</p>
*
* @return <p>This block location's parent block</p>
*/
public Block getParent() {
if (parent == null) {
findParent();
}
if (parent == null) {
return null;
}
return parent.getBlock();
}
/**
* Tries to find the parent block location
*
* <p>If this block location is a sign, the parent is the block location of the block the sign is connected to.</p>
*/
private void findParent() {
int offsetX = 0;
int offsetY = 0;
int offsetZ = 0;
BlockData blockData = getBlock().getBlockData();
if (blockData instanceof Directional) {
//Get the offset of the block "behind" this block
BlockFace facing = ((Directional) blockData).getFacing().getOppositeFace();
offsetX = facing.getModX();
offsetZ = facing.getModZ();
} else if (blockData instanceof Sign) {
//Get offset the block beneath the sign
offsetY = -1;
} else {
return;
}
parent = this.makeRelativeBlockLocation(offsetX, offsetY, offsetZ);
}
@Override @Override
@NotNull
public String toString() { public String toString() {
return String.valueOf(this.getBlockX()) + ',' + this.getBlockY() + ',' + this.getBlockZ(); return String.valueOf(this.getBlockX()) + ',' + this.getBlockY() + ',' + this.getBlockZ();
} }
@@ -203,7 +163,7 @@ public class BlockLocation extends Location {
} }
@Override @Override
public boolean equals(Object object) { public boolean equals(@Nullable Object object) {
if (this == object) { if (this == object) {
return true; return true;
} }

View File

@@ -17,7 +17,7 @@ public class ChunkUnloadRequest implements Comparable<ChunkUnloadRequest> {
* @param chunkToUnload <p>The chunk to request the unloading of</p> * @param chunkToUnload <p>The chunk to request the unloading of</p>
* @param timeUntilUnload <p>The time in milliseconds to wait before unloading the chunk</p> * @param timeUntilUnload <p>The time in milliseconds to wait before unloading the chunk</p>
*/ */
public ChunkUnloadRequest(Chunk chunkToUnload, Long timeUntilUnload) { public ChunkUnloadRequest(@NotNull Chunk chunkToUnload, @NotNull Long timeUntilUnload) {
this.chunkToUnload = chunkToUnload; this.chunkToUnload = chunkToUnload;
long systemNanoTime = System.nanoTime(); long systemNanoTime = System.nanoTime();
this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000); this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000);
@@ -28,6 +28,7 @@ public class ChunkUnloadRequest implements Comparable<ChunkUnloadRequest> {
* *
* @return <p>The chunk to unload</p> * @return <p>The chunk to unload</p>
*/ */
@NotNull
public Chunk getChunkToUnload() { public Chunk getChunkToUnload() {
return this.chunkToUnload; return this.chunkToUnload;
} }
@@ -37,11 +38,13 @@ public class ChunkUnloadRequest implements Comparable<ChunkUnloadRequest> {
* *
* @return <p>The system nano time denoting when the chunk is to be unloaded</p> * @return <p>The system nano time denoting when the chunk is to be unloaded</p>
*/ */
@NotNull
public Long getUnloadNanoTime() { public Long getUnloadNanoTime() {
return this.unloadNanoTime; return this.unloadNanoTime;
} }
@Override @Override
@NotNull
public String toString() { public String toString() {
return "{" + chunkToUnload + ", " + unloadNanoTime + "}"; return "{" + chunkToUnload + ", " + unloadNanoTime + "}";
} }

View File

@@ -0,0 +1,12 @@
package net.knarcraft.stargate.container;
import net.knarcraft.stargate.portal.Portal;
import org.jetbrains.annotations.NotNull;
/**
* A request for updating a portal's control blocks
*
* @param portal <p>The portal to update the control blocks for</p>
*/
public record ControlBlockUpdateRequest(@NotNull Portal portal) {
}

View File

@@ -1,60 +1,16 @@
package net.knarcraft.stargate.container; package net.knarcraft.stargate.container;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull;
/** /**
* This class represents a player teleporting from the end to the over-world using an artificial end portal * This class represents a teleportation from the end to the over-world using an artificial end portal
* *
* <p>This is necessary because a player entering an end portal in the end is a special case. Instead of being * <p>This is necessary because a player entering an end portal in the end is a special case. Instead of being
* teleported, the player is respawned. Because of this, the teleportation needs to be saved and later used to hijack * teleported, the player is respawned. Because of this, the teleportation needs to be saved and later used to hijack
* the position of where the player is to respawn.</p> * the position of where the player is to respawn.</p>
*
* @param exitPortal <p>The portal the player should exit from when arriving in the over-world</p>
*/ */
public class FromTheEndTeleportation { public record FromTheEndTeleportation(@NotNull Portal exitPortal) {
private final Player teleportingPlayer;
private final Portal exitPortal;
/**
* Instantiates a new teleportation from the end
*
* @param teleportingPlayer <p>The teleporting player</p>
* @param exitPortal <p>The portal to exit from</p>
*/
public FromTheEndTeleportation(Player teleportingPlayer, Portal exitPortal) {
this.teleportingPlayer = teleportingPlayer;
this.exitPortal = exitPortal;
}
/**
* Gets the teleporting player
*
* @return <p>The teleporting player</p>
*/
public Player getPlayer() {
return this.teleportingPlayer;
}
/**
* Gets the portal to exit from
*
* @return <p>The portal to exit from</p>
*/
public Portal getExit() {
return this.exitPortal;
}
@Override
public int hashCode() {
return teleportingPlayer.hashCode();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof FromTheEndTeleportation otherTeleportation)) {
return false;
}
return teleportingPlayer.equals(otherTeleportation.teleportingPlayer);
}
} }

View File

@@ -1,5 +1,9 @@
package net.knarcraft.stargate.container; package net.knarcraft.stargate.container;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* This stores a block location as a vector relative to a position * This stores a block location as a vector relative to a position
* *
@@ -7,67 +11,57 @@ package net.knarcraft.stargate.container;
* top-left block of a gate (top-left when looking at the side with the sign). The right is therefore the distance * top-left block of a gate (top-left when looking at the side with the sign). The right is therefore the distance
* from the top-left corner towards the top-right corner. Down is the distance from the top-left corner towards the * from the top-left corner towards the top-right corner. Down is the distance from the top-left corner towards the
* bottom-left corner. Out is the distance outward from the gate.</p> * bottom-left corner. Out is the distance outward from the gate.</p>
*
* <p>Relative block vectors start from a top-left corner. A yaw is used to orient a relative block vector in the
* "real world". In terms of a gate layout, the origin is 0,0. Right is towards the end of the line. Down is to the
* next line. Out is towards the observer.</p>
*
* @param right <p>The distance rightward relative to the origin</p>
* @param down <p>The distance downward relative to the origin</p>
* @param out <p>The distance outward relative to the origin</p>
*/ */
public class RelativeBlockVector { public record RelativeBlockVector(int right, int down, int out) {
private final int right;
private final int down;
private final int out;
/** /**
* A specifier for one of the relative block vector's three properties * Adds the given value to this relative block vector's "right" property
*
* @param valueToAdd <p>The value to add</p>
* @return <p>The new resulting vector</p>
*/ */
public enum Property { @NotNull
/** public RelativeBlockVector addRight(int valueToAdd) {
* Specifies the relative block vector's right property return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out);
*/
RIGHT,
/**
* Specifies the relative block vector's down property
*/
DOWN,
/**
* Specifies the relative block vector's out property
*/
OUT
} }
/** /**
* Instantiates a new relative block vector * Adds the given value to this relative block vector's "down" property
* *
* <p>Relative block vectors start from a top-left corner. A yaw is used to orient a relative block vector in the * @param valueToAdd <p>The value to add</p>
* "real world". * @return <p>The new resulting vector</p>
* In terms of a gate layout, the origin is 0,0. Right is towards the end of the line. Down is to the
* next line. Out is towards the observer.</p>
*
* @param right <p>The distance rightward relative to the origin</p>
* @param down <p>The distance downward relative to the origin</p>
* @param out <p>The distance outward relative to the origin</p>
*/ */
public RelativeBlockVector(int right, int down, int out) { @NotNull
this.right = right; public RelativeBlockVector addDown(int valueToAdd) {
this.down = down; return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out);
this.out = out;
} }
/** /**
* Adds a value to one of the properties of this relative block vector * Adds the given value to this relative block vector's "out" property
* *
* @param propertyToAddTo <p>The property to add to</p> * @param valueToAdd <p>The value to add</p>
* @param valueToAdd <p>The value to add to the property (negative to move in the opposite direction)</p> * @return <p>The new resulting vector</p>
* @return <p>A new relative block vector with the property altered</p>
*/ */
public RelativeBlockVector addToVector(Property propertyToAddTo, int valueToAdd) { @NotNull
switch (propertyToAddTo) { public RelativeBlockVector addOut(int valueToAdd) {
case RIGHT: return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd);
return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out); }
case DOWN:
return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out); /**
case OUT: * Gets a relative vector in the real space representing this relative block vector
return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd); *
default: * @return <p>A vector representing this relative block vector</p>
throw new IllegalArgumentException("Invalid relative block vector property given"); */
} public Vector toVector() {
return new Vector(this.right, -this.down, this.out);
} }
/** /**
@@ -75,44 +69,19 @@ public class RelativeBlockVector {
* *
* @return <p>This vector, but inverted</p> * @return <p>This vector, but inverted</p>
*/ */
@NotNull
public RelativeBlockVector invert() { public RelativeBlockVector invert() {
return new RelativeBlockVector(-this.right, -this.down, -this.out); return new RelativeBlockVector(-this.right, -this.down, -this.out);
} }
/**
* Gets the distance to the right relative to the origin
*
* @return <p>The distance to the right relative to the origin</p>
*/
public int getRight() {
return right;
}
/**
* Gets the distance downward relative to the origin
*
* @return <p>The distance downward relative to the origin</p>
*/
public int getDown() {
return down;
}
/**
* Gets the distance outward relative to the origin
*
* @return <p>The distance outward relative to the origin</p>
*/
public int getOut() {
return out;
}
@Override @Override
@NotNull
public String toString() { public String toString() {
return String.format("(right = %d, down = %d, out = %d)", right, down, out); return String.format("(right = %d, down = %d, out = %d)", right, down, out);
} }
@Override @Override
public boolean equals(Object other) { public boolean equals(@Nullable Object other) {
if (other == this) { if (other == this) {
return true; return true;
} }

View File

@@ -1,9 +1,11 @@
package net.knarcraft.stargate.container; package net.knarcraft.stargate.container;
import net.knarcraft.stargate.utility.ColorHelper; import net.knarcraft.knarlib.util.ColorHelper;
import net.knarcraft.stargate.utility.SignHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.jetbrains.annotations.NotNull;
/** /**
* A class that keeps track of the sign colors for a given sign * A class that keeps track of the sign colors for a given sign
@@ -22,11 +24,11 @@ public class SignData {
* @param mainSignColor <p>The main color to use for the sign</p> * @param mainSignColor <p>The main color to use for the sign</p>
* @param highlightSignColor <p>The highlighting color to use for the sign</p> * @param highlightSignColor <p>The highlighting color to use for the sign</p>
*/ */
public SignData(Sign sign, ChatColor mainSignColor, ChatColor highlightSignColor) { public SignData(@NotNull Sign sign, @NotNull ChatColor mainSignColor, @NotNull ChatColor highlightSignColor) {
this.sign = sign; this.sign = sign;
this.mainSignColor = mainSignColor; this.mainSignColor = mainSignColor;
this.highlightSignColor = highlightSignColor; this.highlightSignColor = highlightSignColor;
this.dyedColor = sign.getColor(); this.dyedColor = SignHelper.getDye(sign);
} }
/** /**
@@ -34,6 +36,7 @@ public class SignData {
* *
* @return <p>The sign of this sign colors object</p> * @return <p>The sign of this sign colors object</p>
*/ */
@NotNull
public Sign getSign() { public Sign getSign() {
return sign; return sign;
} }
@@ -43,6 +46,7 @@ public class SignData {
* *
* @return <p>The main color of the sign</p> * @return <p>The main color of the sign</p>
*/ */
@NotNull
public ChatColor getMainSignColor() { public ChatColor getMainSignColor() {
if (dyedColor != DyeColor.BLACK) { if (dyedColor != DyeColor.BLACK) {
return ColorHelper.fromColor(dyedColor.getColor()); return ColorHelper.fromColor(dyedColor.getColor());
@@ -56,6 +60,7 @@ public class SignData {
* *
* @return <p>The highlighting color of the sign</p> * @return <p>The highlighting color of the sign</p>
*/ */
@NotNull
public ChatColor getHighlightSignColor() { public ChatColor getHighlightSignColor() {
if (dyedColor != DyeColor.BLACK) { if (dyedColor != DyeColor.BLACK) {
return ColorHelper.fromColor(ColorHelper.invert(dyedColor.getColor())); return ColorHelper.fromColor(ColorHelper.invert(dyedColor.getColor()));

View File

@@ -24,7 +24,7 @@ public class StargateAccessEvent extends StargatePlayerEvent {
* @param portal <p>The portal involved in the event</p> * @param portal <p>The portal involved in the event</p>
* @param deny <p>Whether the stargate access should be denied</p> * @param deny <p>Whether the stargate access should be denied</p>
*/ */
public StargateAccessEvent(Player player, Portal portal, boolean deny) { public StargateAccessEvent(@NotNull Player player, @NotNull Portal portal, boolean deny) {
super(portal, player); super(portal, player);
this.deny = deny; this.deny = deny;
@@ -53,6 +53,7 @@ public class StargateAccessEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -28,7 +28,8 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* @param destinations <p>The destinations available to the player using the portal</p> * @param destinations <p>The destinations available to the player using the portal</p>
* @param destination <p>The currently selected destination</p> * @param destination <p>The currently selected destination</p>
*/ */
public StargateActivateEvent(Portal portal, Player player, List<String> destinations, String destination) { public StargateActivateEvent(@NotNull Portal portal, @NotNull Player player, @NotNull List<String> destinations,
@NotNull String destination) {
super(portal, player); super(portal, player);
this.destinations = destinations; this.destinations = destinations;
@@ -40,6 +41,7 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* *
* @return <p>The destinations available for the portal</p> * @return <p>The destinations available for the portal</p>
*/ */
@NotNull
public List<String> getDestinations() { public List<String> getDestinations() {
return destinations; return destinations;
} }
@@ -49,7 +51,7 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* *
* @param destinations <p>The new list of available destinations</p> * @param destinations <p>The new list of available destinations</p>
*/ */
public void setDestinations(List<String> destinations) { public void setDestinations(@NotNull List<String> destinations) {
this.destinations = destinations; this.destinations = destinations;
} }
@@ -58,6 +60,7 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* *
* @return <p>The selected destination</p> * @return <p>The selected destination</p>
*/ */
@NotNull
public String getDestination() { public String getDestination() {
return destination; return destination;
} }
@@ -67,7 +70,7 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* *
* @param destination <p>The new selected destination</p> * @param destination <p>The new selected destination</p>
*/ */
public void setDestination(String destination) { public void setDestination(@NotNull String destination) {
this.destination = destination; this.destination = destination;
} }
@@ -76,6 +79,7 @@ public class StargateActivateEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -22,7 +22,7 @@ public class StargateCloseEvent extends StargateEvent {
* @param portal <p>The portal to close</p> * @param portal <p>The portal to close</p>
* @param force <p>Whether to force the gate to close, even if set as always-on</p> * @param force <p>Whether to force the gate to close, even if set as always-on</p>
*/ */
public StargateCloseEvent(Portal portal, boolean force) { public StargateCloseEvent(@NotNull Portal portal, boolean force) {
super(portal); super(portal);
this.force = force; this.force = force;
@@ -51,6 +51,7 @@ public class StargateCloseEvent extends StargateEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -29,7 +29,8 @@ public class StargateCreateEvent extends StargatePlayerEvent {
* @param denyReason <p>The reason stargate creation was denied</p> * @param denyReason <p>The reason stargate creation was denied</p>
* @param cost <p>The cost of creating the new star gate</p> * @param cost <p>The cost of creating the new star gate</p>
*/ */
public StargateCreateEvent(Player player, Portal portal, String[] lines, boolean deny, String denyReason, int cost) { public StargateCreateEvent(@NotNull Player player, @NotNull Portal portal, @NotNull String[] lines, boolean deny,
@NotNull String denyReason, int cost) {
super(portal, player); super(portal, player);
this.lines = lines; this.lines = lines;
this.deny = deny; this.deny = deny;
@@ -44,6 +45,7 @@ public class StargateCreateEvent extends StargatePlayerEvent {
* @return <p>The text on the given line</p> * @return <p>The text on the given line</p>
* @throws IndexOutOfBoundsException <p>If given a line index less than zero or above three</p> * @throws IndexOutOfBoundsException <p>If given a line index less than zero or above three</p>
*/ */
@NotNull
public String getLine(int index) throws IndexOutOfBoundsException { public String getLine(int index) throws IndexOutOfBoundsException {
return lines[index]; return lines[index];
} }
@@ -71,6 +73,7 @@ public class StargateCreateEvent extends StargatePlayerEvent {
* *
* @return <p>The reason the stargate creation was denied</p> * @return <p>The reason the stargate creation was denied</p>
*/ */
@NotNull
public String getDenyReason() { public String getDenyReason() {
return denyReason; return denyReason;
} }
@@ -80,7 +83,7 @@ public class StargateCreateEvent extends StargatePlayerEvent {
* *
* @param denyReason <p>The new reason why the stargate creation was denied</p> * @param denyReason <p>The new reason why the stargate creation was denied</p>
*/ */
public void setDenyReason(String denyReason) { public void setDenyReason(@NotNull String denyReason) {
this.denyReason = denyReason; this.denyReason = denyReason;
} }
@@ -107,6 +110,7 @@ public class StargateCreateEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -20,7 +20,7 @@ public class StargateDeactivateEvent extends StargateEvent {
* *
* @param portal <p>The portal which was deactivated</p> * @param portal <p>The portal which was deactivated</p>
*/ */
public StargateDeactivateEvent(Portal portal) { public StargateDeactivateEvent(@NotNull Portal portal) {
super(portal); super(portal);
} }
@@ -29,6 +29,7 @@ public class StargateDeactivateEvent extends StargateEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -27,7 +27,8 @@ public class StargateDestroyEvent extends StargatePlayerEvent {
* @param denyMsg <p>The message to display if the event is denied</p> * @param denyMsg <p>The message to display if the event is denied</p>
* @param cost <p>The cost of destroying the portal</p> * @param cost <p>The cost of destroying the portal</p>
*/ */
public StargateDestroyEvent(Portal portal, Player player, boolean deny, String denyMsg, int cost) { public StargateDestroyEvent(@NotNull Portal portal, @NotNull Player player, boolean deny, @NotNull String denyMsg,
int cost) {
super(portal, player); super(portal, player);
this.deny = deny; this.deny = deny;
this.denyReason = denyMsg; this.denyReason = denyMsg;
@@ -57,6 +58,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent {
* *
* @return <p>The reason the event was denied</p> * @return <p>The reason the event was denied</p>
*/ */
@NotNull
public String getDenyReason() { public String getDenyReason() {
return denyReason; return denyReason;
} }
@@ -66,7 +68,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent {
* *
* @param denyReason <p>The reason the event was denied</p> * @param denyReason <p>The reason the event was denied</p>
*/ */
public void setDenyReason(String denyReason) { public void setDenyReason(@NotNull String denyReason) {
this.denyReason = denyReason; this.denyReason = denyReason;
} }
@@ -93,6 +95,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
* <p>This event can be used to overwrite the location the entity is teleported to.</p> * <p>This event can be used to overwrite the location the entity is teleported to.</p>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class StargateEntityPortalEvent extends StargateEvent { public class StargateEntityPortalEvent extends StargateEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
final Entity travellingEntity; final Entity travellingEntity;
@@ -27,7 +27,8 @@ public class StargateEntityPortalEvent extends StargateEvent {
* @param destination <p>The destination the entity should exit from</p> * @param destination <p>The destination the entity should exit from</p>
* @param exit <p>The exit location of the destination portal the entity will be teleported to</p> * @param exit <p>The exit location of the destination portal the entity will be teleported to</p>
*/ */
public StargateEntityPortalEvent(Entity travellingEntity, Portal portal, Portal destination, Location exit) { public StargateEntityPortalEvent(@NotNull Entity travellingEntity, @NotNull Portal portal,
@NotNull Portal destination, @NotNull Location exit) {
super(portal); super(portal);
this.travellingEntity = travellingEntity; this.travellingEntity = travellingEntity;
@@ -40,6 +41,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @return <p>The non-player teleporting</p> * @return <p>The non-player teleporting</p>
*/ */
@NotNull
public Entity getEntity() { public Entity getEntity() {
return travellingEntity; return travellingEntity;
} }
@@ -49,6 +51,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @return <p>The destination portal</p> * @return <p>The destination portal</p>
*/ */
@NotNull
public Portal getDestination() { public Portal getDestination() {
return destination; return destination;
} }
@@ -58,6 +61,8 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @return <p>Location of the exit point</p> * @return <p>Location of the exit point</p>
*/ */
@Override
@NotNull
public Location getExit() { public Location getExit() {
return exit; return exit;
} }
@@ -67,7 +72,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @param location <p>The new location of the entity's exit point</p> * @param location <p>The new location of the entity's exit point</p>
*/ */
public void setExit(Location location) { public void setExit(@NotNull Location location) {
this.exit = location; this.exit = location;
} }
@@ -76,6 +81,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -3,6 +3,7 @@ package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
/** /**
* An abstract event describing any stargate event * An abstract event describing any stargate event
@@ -18,7 +19,7 @@ public abstract class StargateEvent extends Event implements Cancellable {
* *
* @param portal <p>The portal involved in this stargate event</p> * @param portal <p>The portal involved in this stargate event</p>
*/ */
StargateEvent(Portal portal) { StargateEvent(@NotNull Portal portal) {
this.portal = portal; this.portal = portal;
this.cancelled = false; this.cancelled = false;
} }
@@ -28,6 +29,7 @@ public abstract class StargateEvent extends Event implements Cancellable {
* *
* @return <p>The portal involved in this stargate event</p> * @return <p>The portal involved in this stargate event</p>
*/ */
@NotNull
public Portal getPortal() { public Portal getPortal() {
return portal; return portal;
} }

View File

@@ -4,6 +4,7 @@ import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* This event should be called whenever a player opens a stargate * This event should be called whenever a player opens a stargate
@@ -23,7 +24,7 @@ public class StargateOpenEvent extends StargatePlayerEvent {
* @param portal <p>The opened portal</p> * @param portal <p>The opened portal</p>
* @param force <p>Whether to force the portal open</p> * @param force <p>Whether to force the portal open</p>
*/ */
public StargateOpenEvent(Player player, Portal portal, boolean force) { public StargateOpenEvent(@Nullable Player player, @NotNull Portal portal, boolean force) {
super(portal, player); super(portal, player);
this.force = force; this.force = force;
@@ -52,6 +53,7 @@ public class StargateOpenEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -2,6 +2,8 @@ package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* An abstract event describing any stargate event where a player is involved * An abstract event describing any stargate event where a player is involved
@@ -16,7 +18,7 @@ public abstract class StargatePlayerEvent extends StargateEvent {
* *
* @param portal <p>The portal involved in this stargate event</p> * @param portal <p>The portal involved in this stargate event</p>
*/ */
StargatePlayerEvent(Portal portal, Player player) { StargatePlayerEvent(@NotNull Portal portal, @Nullable Player player) {
super(portal); super(portal);
this.player = player; this.player = player;
} }
@@ -26,6 +28,7 @@ public abstract class StargatePlayerEvent extends StargateEvent {
* *
* @return <p>The player creating the star gate</p> * @return <p>The player creating the star gate</p>
*/ */
@Nullable
public Player getPlayer() { public Player getPlayer() {
return player; return player;
} }

View File

@@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
* <p>This event can be used to overwrite the location the player is teleported to.</p> * <p>This event can be used to overwrite the location the player is teleported to.</p>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class StargatePlayerPortalEvent extends StargatePlayerEvent { public class StargatePlayerPortalEvent extends StargatePlayerEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final Portal destination; private final Portal destination;
@@ -26,7 +26,8 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* @param destination <p>The destination the player should exit from</p> * @param destination <p>The destination the player should exit from</p>
* @param exit <p>The exit location of the destination portal the user will be teleported to</p> * @param exit <p>The exit location of the destination portal the user will be teleported to</p>
*/ */
public StargatePlayerPortalEvent(Player player, Portal portal, Portal destination, Location exit) { public StargatePlayerPortalEvent(@NotNull Player player, @NotNull Portal portal, @NotNull Portal destination,
@NotNull Location exit) {
super(portal, player); super(portal, player);
this.destination = destination; this.destination = destination;
@@ -38,6 +39,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* *
* @return <p>The destination portal</p> * @return <p>The destination portal</p>
*/ */
@NotNull
public Portal getDestination() { public Portal getDestination() {
return destination; return destination;
} }
@@ -47,6 +49,8 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* *
* @return <p>Location of the exit point</p> * @return <p>Location of the exit point</p>
*/ */
@Override
@NotNull
public Location getExit() { public Location getExit() {
return exit; return exit;
} }
@@ -56,7 +60,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* *
* @param location <p>The new location of the player's exit point</p> * @param location <p>The new location of the player's exit point</p>
*/ */
public void setExit(Location location) { public void setExit(@NotNull Location location) {
this.exit = location; this.exit = location;
} }
@@ -65,6 +69,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* *
* @return <p>A handler-list with all event handlers</p> * @return <p>A handler-list with all event handlers</p>
*/ */
@NotNull
public static HandlerList getHandlerList() { public static HandlerList getHandlerList() {
return handlers; return handlers;
} }

View File

@@ -0,0 +1,20 @@
package net.knarcraft.stargate.event;
import org.bukkit.Location;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* A generic teleportation event
*/
public interface StargateTeleportEvent extends Cancellable {
/**
* Return the location of the players exit point
*
* @return <p>Location of the exit point</p>
*/
@NotNull
Location getExit();
}

View File

@@ -1,280 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.event.StargateDestroyEvent;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalCreator;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.Player;
import org.bukkit.entity.Snowman;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPistonEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.EntityBlockFormEvent;
import org.bukkit.event.block.SignChangeEvent;
import java.util.List;
/**
* This class is responsible for listening to relevant block events related to creating and breaking portals
*/
@SuppressWarnings("unused")
public class BlockEventListener implements Listener {
/**
* Detects snowmen ruining portals
*
* <p>If entrance protection or portal verification is enabled, the snowman will be prevented from placing snow in
* the portal entrance.</p>
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onBlockFormedByEntity(EntityBlockFormEvent event) {
if (event.isCancelled() || (!Stargate.getGateConfig().protectEntrance() &&
!Stargate.getGateConfig().verifyPortals())) {
return;
}
//We are only interested in snowman events
if (!(event.getEntity() instanceof Snowman)) {
return;
}
//Cancel the event if a snowman is trying to place snow in the portal's entrance
if (PortalHandler.getByEntrance(event.getBlock()) != null) {
event.setCancelled(true);
}
}
/**
* Detects sign changes to detect if the user is creating a new gate
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onSignChange(SignChangeEvent event) {
if (event.isCancelled()) {
return;
}
Player player = event.getPlayer();
Block block = event.getBlock();
//Ignore normal signs
if (!(block.getBlockData() instanceof WallSign)) {
return;
}
final Portal portal = new PortalCreator(event, player).createPortal();
//Not creating a gate, just placing a sign
if (portal == null) {
return;
}
//Remove the sign if the no sign option is enabled
if (portal.getOptions().hasNoSign()) {
Material replaceMaterial = PortalFileHelper.decideRemovalMaterial(portal.getSignLocation(), portal);
BlockChangeRequest request = new BlockChangeRequest(portal.getSignLocation(), replaceMaterial, null);
Stargate.addBlockChangeRequest(request);
}
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("createMsg"));
Stargate.debug("onSignChange", "Initialized stargate: " + portal.getName());
Stargate.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(),
portal::drawSign, 1);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockPlace(BlockPlaceEvent event) {
if (event.isCancelled() || !Stargate.getGateConfig().protectEntrance()) {
return;
}
Block block = event.getBlock();
Player player = event.getPlayer();
Portal portal = PortalHandler.getByEntrance(block);
if (portal != null) {
//Prevent blocks from being placed in the entrance, if protectEntrance is enabled, as breaking the block
// would destroy the portal
event.setCancelled(true);
}
}
/**
* Detects block breaking to detect if the user is destroying a gate
*
* @param event <p>The triggered event</p>
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockBreak(BlockBreakEvent event) {
if (event.isCancelled()) {
return;
}
Block block = event.getBlock();
Player player = event.getPlayer();
//Decide if a portal is broken
Portal portal = PortalHandler.getByBlock(block);
if (portal == null && Stargate.getGateConfig().protectEntrance()) {
portal = PortalHandler.getByEntrance(block);
}
if (portal == null) {
return;
}
boolean deny = false;
String denyMessage = "";
//Decide if the user can destroy the portal
if (!PermissionHelper.canDestroyPortal(player, portal)) {
denyMessage = Stargate.getString("denyMsg");
deny = true;
Stargate.logInfo(String.format("%s tried to destroy gate", player.getName()));
}
int cost = Stargate.getEconomyConfig().getDestroyCost(player, portal.getGate());
//Create and call a StarGateDestroyEvent
StargateDestroyEvent destroyEvent = new StargateDestroyEvent(portal, player, deny, denyMessage, cost);
Stargate.getInstance().getServer().getPluginManager().callEvent(destroyEvent);
if (destroyEvent.isCancelled()) {
event.setCancelled(true);
return;
}
//Destroy denied
if (destroyEvent.getDeny()) {
if (!destroyEvent.getDenyReason().trim().isEmpty()) {
Stargate.getMessageSender().sendErrorMessage(player, destroyEvent.getDenyReason());
}
event.setCancelled(true);
return;
}
//Take care of payment transactions
if (!handleEconomyPayment(destroyEvent, player, portal, event)) {
return;
}
PortalRegistry.unregisterPortal(portal, true);
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("destroyMsg"));
}
/**
* Handles economy payment for breaking the portal
*
* @param destroyEvent <p>The destroy event</p>
* @param player <p>The player which triggered the event</p>
* @param portal <p>The broken portal</p>
* @param event <p>The break event</p>
* @return <p>True if the payment was successful. False if the event was cancelled</p>
*/
private boolean handleEconomyPayment(StargateDestroyEvent destroyEvent, Player player, Portal portal,
BlockBreakEvent event) {
int cost = destroyEvent.getCost();
if (cost != 0) {
String portalName = portal.getName();
//Cannot pay
if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) {
Stargate.debug("onBlockBreak", "Insufficient Funds");
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
event.setCancelled(true);
return false;
}
//Tell the player they've paid or deceived money
if (cost > 0) {
EconomyHelper.sendDeductMessage(portalName, player, cost);
} else {
EconomyHelper.sendRefundMessage(portalName, player, cost);
}
}
return true;
}
/**
* Prevents any block physics events which may damage parts of the portal
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onBlockPhysics(BlockPhysicsEvent event) {
Block block = event.getBlock();
Portal portal = null;
if (block.getType() == Material.NETHER_PORTAL) {
portal = PortalHandler.getByEntrance(block);
} else if (MaterialHelper.isButtonCompatible(block.getType())) {
portal = PortalHandler.getByControl(block);
}
if (portal != null) {
event.setCancelled(true);
}
}
/**
* Cancels any block move events which may cause a block to enter the opening of a portal
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onBlockFromTo(BlockFromToEvent event) {
Portal portal = PortalHandler.getByEntrance(event.getBlock());
if (portal != null) {
event.setCancelled((event.getBlock().getY() == event.getToBlock().getY()));
}
}
/**
* Cancels any piston extend events if the target block is part of a portal
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onPistonExtend(BlockPistonExtendEvent event) {
cancelPistonEvent(event, event.getBlocks());
}
/**
* Cancels any piston retract events if the target block is part of a portal
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onPistonRetract(BlockPistonRetractEvent event) {
if (!event.isSticky()) {
return;
}
cancelPistonEvent(event, event.getBlocks());
}
/**
* Cancels a piston event if it would destroy a portal
*
* @param event <p>The event to cancel</p>
* @param blocks <p>The blocks included in the event</p>
*/
private void cancelPistonEvent(BlockPistonEvent event, List<Block> blocks) {
for (Block block : blocks) {
Portal portal = PortalHandler.getByBlock(block);
if (portal != null) {
event.setCancelled(true);
return;
}
}
}
}

View File

@@ -1,42 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.utility.BungeeHelper;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
/**
* This listener teleports a user if a valid message is received from BungeeCord
*
* <p>Specifically, if a string starts with SGBungee encoded to be readable by readUTF followed by
* [PlayerUUID]delimiter[DestinationPortal] is received on the BungeeCord channel, this listener will teleport the
* player to the destination portal.</p>
*/
public class BungeeCordListener implements PluginMessageListener {
/**
* Receives plugin messages
*
* @param channel <p>The channel the message was received on</p>
* @param unused <p>Unused.</p>
* @param message <p>The message received from the plugin</p>
*/
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player unused, byte[] message) {
//Ignore plugin messages if some other plugin message is received
if (!channel.equals(BungeeHelper.getBungeeChannel())) {
return;
}
//Try to read the plugin message
String receivedMessage = BungeeHelper.readPluginMessage(message);
if (receivedMessage == null) {
return;
}
//Use the message to initiate teleportation
BungeeHelper.handleTeleportMessage(receivedMessage);
}
}

View File

@@ -1,67 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.EntityHelper;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityPortalEvent;
/**
* This listener listens for any relevant events on portal entities
*/
@SuppressWarnings("unused")
public class EntityEventListener implements Listener {
/**
* This event handler prevents sending entities to the normal nether instead of the stargate target
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onPortalEvent(EntityPortalEvent event) {
if (event.isCancelled()) {
return;
}
Entity entity = event.getEntity();
//Cancel normal portal event is near a stargate
if (PortalHandler.getByAdjacentEntrance(event.getFrom(), EntityHelper.getEntityMaxSizeInt(entity)) != null) {
event.setCancelled(true);
}
}
/**
* This method catches any explosion events
*
* <p>If destroyed by explosions is enabled, any portals destroyed by the explosion will be unregistered. If not,
* the explosion will be cancelled.</p>
*
* @param event <p>The triggered explosion event</p>
*/
@EventHandler
public void onEntityExplode(EntityExplodeEvent event) {
if (event.isCancelled()) {
return;
}
for (Block block : event.blockList()) {
Portal portal = PortalHandler.getByBlock(block);
if (portal == null) {
continue;
}
if (Stargate.getGateConfig().destroyedByExplosion()) {
PortalRegistry.unregisterPortal(portal, true);
} else {
event.setCancelled(true);
break;
}
}
}
}

View File

@@ -0,0 +1,96 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateConfig;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
/**
* This listener listens for any plugins/worlds being enabled or disabled
*/
public class EnvironmentChangeListener implements Listener {
private final Stargate stargate;
/**
* Instantiates a new plugin event listener
*
* @param stargate <p>A reference to the stargate plugin to </p>
*/
public EnvironmentChangeListener(@NotNull Stargate stargate) {
this.stargate = stargate;
}
/**
* This event listens for and announces that the vault plugin was detected and enabled
*
* <p>Each time this event is called, the economy handler will try to enable vault</p>
*
* @param ignored <p>The actual event called. This is currently not used</p>
*/
@EventHandler
public void onPluginEnable(@NotNull PluginEnableEvent ignored) {
if (Stargate.getEconomyConfig().setupEconomy(stargate.getServer().getPluginManager())) {
Plugin vault = Stargate.getEconomyConfig().getVault();
if (vault != null) {
String vaultVersion = vault.getDescription().getVersion();
Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOADED).replace("%version%", vaultVersion).toString());
}
}
}
/**
* This event listens for the vault plugin being disabled and notifies the console
*
* @param event <p>The event caused by disabling a plugin</p>
*/
@EventHandler
public void onPluginDisable(@NotNull PluginDisableEvent event) {
if (event.getPlugin().equals(Stargate.getEconomyConfig().getVault())) {
Stargate.logInfo("Vault plugin lost.");
}
}
/**
* This listener listens for the loading of a world and loads all gates from the world if not already loaded
*
* @param event <p>The triggered world load event</p>
*/
@EventHandler
public void onWorldLoad(@NotNull WorldLoadEvent event) {
StargateConfig config = Stargate.getStargateConfig();
if (!config.getManagedWorlds().contains(event.getWorld().getName()) &&
PortalFileHelper.loadAllPortals(event.getWorld())) {
config.addManagedWorld(event.getWorld().getName());
}
}
/**
* This listener listens for the unloading of a world
*
* @param event <p>The triggered world unload event</p>
*/
@EventHandler
public void onWorldUnload(@NotNull WorldUnloadEvent event) {
Stargate.debug("onWorldUnload", "Reloading all Stargates");
World world = event.getWorld();
String worldName = world.getName();
StargateConfig config = Stargate.getStargateConfig();
if (config.getManagedWorlds().contains(worldName)) {
config.removeManagedWorld(worldName);
PortalRegistry.clearPortals(world);
}
}
}

View File

@@ -1,38 +1,34 @@
package net.knarcraft.stargate.listener; package net.knarcraft.stargate.listener;
import net.knarcraft.knarlib.formatting.FormatBuilder;
import net.knarcraft.knarlib.util.UpdateChecker;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.MessageSender; import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalActivator; import net.knarcraft.stargate.portal.PortalActivator;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.knarcraft.stargate.portal.teleporter.Teleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.BungeeHelper; import net.knarcraft.stargate.utility.BungeeHelper;
import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import net.knarcraft.stargate.utility.UUIDMigrationHelper; import net.knarcraft.stargate.utility.UUIDMigrationHelper;
import net.knarcraft.stargate.utility.UpdateChecker;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.data.type.WallSign; import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Event; 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;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -40,7 +36,6 @@ import java.util.Map;
/** /**
* This listener listens to any player-related events related to stargates * This listener listens to any player-related events related to stargates
*/ */
@SuppressWarnings("unused")
public class PlayerEventListener implements Listener { public class PlayerEventListener implements Listener {
private static final Map<Player, Long> previousEventTimes = new HashMap<>(); private static final Map<Player, Long> previousEventTimes = new HashMap<>();
@@ -51,7 +46,7 @@ public class PlayerEventListener implements Listener {
* @param event <p>The event to check for a teleporting player</p> * @param event <p>The event to check for a teleporting player</p>
*/ */
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(@NotNull PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
//Migrate player name to UUID if necessary //Migrate player name to UUID if necessary
UUIDMigrationHelper.migrateUUID(player); UUIDMigrationHelper.migrateUUID(player);
@@ -59,9 +54,9 @@ public class PlayerEventListener implements Listener {
//Notify joining admins about the available update //Notify joining admins about the available update
String availableUpdate = Stargate.getUpdateAvailable(); String availableUpdate = Stargate.getUpdateAvailable();
if (availableUpdate != null && Stargate.getStargateConfig().alertAdminsAboutUpdates() && if (availableUpdate != null && Stargate.getStargateConfig().alertAdminsAboutUpdates() &&
player.hasPermission("stargate.admin")) { PermissionHelper.hasPermission(player, Permission.ADMIN)) {
String updateMessage = UpdateChecker.getUpdateAvailableString(availableUpdate, Stargate.getPluginVersion()); String updateMessage = UpdateChecker.getUpdateAvailableString(availableUpdate, Stargate.getPluginVersion());
Stargate.getMessageSender().sendErrorMessage(player, updateMessage); new SGFormatBuilder(updateMessage).error(player);
} }
if (!Stargate.getGateConfig().enableBungee()) { if (!Stargate.getGateConfig().enableBungee()) {
@@ -71,10 +66,11 @@ public class PlayerEventListener implements Listener {
//Check if the player is waiting to be teleported to a stargate //Check if the player is waiting to be teleported to a stargate
String destination = BungeeHelper.removeFromQueue(player.getUniqueId()); String destination = BungeeHelper.removeFromQueue(player.getUniqueId());
if (destination == null) { if (destination == null) {
Stargate.debug("PlayerJoin", "No bungee request found in queue");
return; return;
} }
Portal portal = PortalHandler.getBungeePortal(destination); Portal portal = PortalUtil.getBungeePortal(destination);
if (portal == null) { if (portal == null) {
Stargate.debug("PlayerJoin", "Error fetching destination portal: " + destination); Stargate.debug("PlayerJoin", "Error fetching destination portal: " + destination);
return; return;
@@ -83,118 +79,13 @@ public class PlayerEventListener implements Listener {
new PlayerTeleporter(portal, player).teleport(portal, null); new PlayerTeleporter(portal, player).teleport(portal, null);
} }
/**
* This event handler detects if a player moves into a portal
*
* @param event <p>The player move event which was triggered</p>
*/
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
BlockLocation fromLocation = new BlockLocation(event.getFrom().getBlock());
BlockLocation toLocation = new BlockLocation(event.getTo().getBlock());
Player player = event.getPlayer();
//Check whether the event needs to be considered
if (!isRelevantMoveEvent(event, player, fromLocation, toLocation)) {
return;
}
Portal entrancePortal = PortalHandler.getByEntrance(toLocation);
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
Entity playerVehicle = player.getVehicle();
//If the player is in a vehicle, but vehicle handling is disabled, just ignore the player
if (playerVehicle == null || (playerVehicle instanceof LivingEntity &&
Stargate.getGateConfig().handleVehicles())) {
teleportPlayer(playerVehicle, player, entrancePortal, destination, event);
}
}
/**
* Teleports a player, also teleports the player's vehicle if it's a living entity
*
* @param playerVehicle <p>The vehicle the player is currently sitting in</p>
* @param player <p>The player which moved</p>
* @param entrancePortal <p>The entrance the player entered</p>
* @param destination <p>The destination of the entrance portal</p>
* @param event <p>The move event causing the teleportation to trigger</p>
*/
private void teleportPlayer(Entity playerVehicle, Player player, Portal entrancePortal, Portal destination,
PlayerMoveEvent event) {
if (playerVehicle instanceof LivingEntity) {
//Make sure any horses are properly tamed
if (playerVehicle instanceof AbstractHorse horse && !horse.isTamed()) {
horse.setTamed(true);
horse.setOwner(player);
}
//Teleport the player's vehicle
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleport(entrancePortal);
} else {
//Just teleport the player like normal
new PlayerTeleporter(destination, player).teleport(entrancePortal, event);
}
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
}
/**
* Checks whether a player move event is relevant for this plugin
*
* @param event <p>The player move event to check</p>
* @param player <p>The player which moved</p>
* @param fromLocation <p>The location the player is moving from</p>
* @param toLocation <p>The location the player is moving to</p>
* @return <p>True if the event is relevant</p>
*/
private boolean isRelevantMoveEvent(PlayerMoveEvent event, Player player, BlockLocation fromLocation,
BlockLocation toLocation) {
//Check to see if the player moved to another block
if (fromLocation.equals(toLocation)) {
return false;
}
//Check if the player moved from a portal
Portal entrancePortal = PortalHandler.getByEntrance(toLocation);
if (entrancePortal == null) {
return false;
}
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
//Catch always open portals without a valid destination to prevent the user for being teleported and denied
if (!entrancePortal.getOptions().isBungee() && destination == null) {
return false;
}
//Decide if the anything stops the player from teleport
if (PermissionHelper.playerCannotTeleport(entrancePortal, destination, player, event)) {
return false;
}
//Decide if the user should be teleported to another bungee server
if (entrancePortal.getOptions().isBungee()) {
if (BungeeHelper.bungeeTeleport(player, entrancePortal, event) && !entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
return false;
}
//Make sure to check if the player has any leashed creatures, even though leashed teleportation is disabled
return Teleporter.noLeashedCreaturesPreventTeleportation(player);
}
/** /**
* This event handler detects if a player clicks a button or a sign * This event handler detects if a player clicks a button or a sign
* *
* @param event <p>The player interact event which was triggered</p> * @param event <p>The player interact event which was triggered</p>
*/ */
@EventHandler @EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
Block block = event.getClickedBlock(); Block block = event.getClickedBlock();
@@ -203,6 +94,10 @@ public class PlayerEventListener implements Listener {
} }
if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (event.getHand() == null) {
return;
}
// Handle right-click of a sign, button or other
handleRightClickBlock(event, player, block, event.getHand()); handleRightClickBlock(event, player, block, event.getHand());
} else if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getBlockData() instanceof WallSign) { } else if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getBlockData() instanceof WallSign) {
//Handle left click of a wall sign //Handle left click of a wall sign
@@ -218,22 +113,16 @@ public class PlayerEventListener implements Listener {
* @param block <p>The block that was clicked</p> * @param block <p>The block that was clicked</p>
* @param leftClick <p>Whether the player performed a left click as opposed to a right click</p> * @param leftClick <p>Whether the player performed a left click as opposed to a right click</p>
*/ */
private void handleSignClick(PlayerInteractEvent event, Player player, Block block, boolean leftClick) { private void handleSignClick(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Block block,
Portal portal = PortalHandler.getByBlock(block); boolean leftClick) {
Portal portal = PortalUtil.getByBlock(block);
if (portal == null) { if (portal == null) {
return; return;
} }
//Allow players with permissions to apply dye to signs //Allow players with permissions to apply dye to signs
EquipmentSlot hand = event.getHand(); if (dyeSign(event, player, portal)) {
if (hand != null && (PermissionHelper.hasPermission(player, "stargate.admin.dye") || return;
portal.isOwner(player))) {
String itemName = player.getInventory().getItem(hand).getType().toString();
if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) {
event.setUseInteractedBlock(Event.Result.ALLOW);
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1);
return;
}
} }
event.setUseInteractedBlock(Event.Result.DENY); event.setUseInteractedBlock(Event.Result.DENY);
@@ -263,6 +152,39 @@ public class PlayerEventListener implements Listener {
} }
} }
/**
* Tries to take care of a sign dye interaction
*
* @param event <p>The triggered player interaction event</p>
* @param player <p>The involved player</p>
* @param portal <p>The involved portal</p>
* @return <p>True if a sign was dyed</p>
*/
private boolean dyeSign(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Portal portal) {
EquipmentSlot hand = event.getHand();
// Check if the player is allowed to dye the sign
if (hand == null || (!PermissionHelper.hasPermission(player, Permission.DYE_SIGN) &&
!portal.isOwner(player))) {
return false;
}
// Check if the player is holding an item
ItemStack item = player.getInventory().getItem(hand);
if (item == null) {
return false;
}
String itemName = item.getType().toString();
// Check if the player's item can be used to dye the sign
if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) {
event.setUseInteractedBlock(Event.Result.ALLOW);
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1);
return true;
} else {
return false;
}
}
/** /**
* Check if a player should be denied from accessing (using) a portal * Check if a player should be denied from accessing (using) a portal
* *
@@ -270,12 +192,12 @@ public class PlayerEventListener implements Listener {
* @param portal <p>The portal the player is trying to use</p> * @param portal <p>The portal the player is trying to use</p>
* @return <p>True if the player should be denied</p> * @return <p>True if the player should be denied</p>
*/ */
private boolean cannotAccessPortal(Player player, Portal portal) { private boolean cannotAccessPortal(@NotNull Player player, @NotNull Portal portal) {
boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getCleanNetwork()); boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getCleanNetwork());
if (PermissionHelper.portalAccessDenied(player, portal, deny)) { if (PermissionHelper.portalAccessDenied(player, portal, deny)) {
if (!portal.getOptions().isSilent()) { if (!portal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
} }
return true; return true;
} }
@@ -290,19 +212,20 @@ public class PlayerEventListener implements Listener {
* @param block <p>The block the player clicked</p> * @param block <p>The block the player clicked</p>
* @param hand <p>The hand the player used to interact with the stargate</p> * @param hand <p>The hand the player used to interact with the stargate</p>
*/ */
private void handleRightClickBlock(PlayerInteractEvent event, Player player, Block block, EquipmentSlot hand) { private void handleRightClickBlock(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Block block,
@NotNull EquipmentSlot hand) {
if (block.getBlockData() instanceof WallSign) { if (block.getBlockData() instanceof WallSign) {
handleSignClick(event, player, block, false); handleSignClick(event, player, block, false);
return; return;
} }
//Prevent a double click caused by a Spigot bug //Prevent a double click caused by a Spigot bug
if (clickIsBug(event.getPlayer(), block)) { if (clickIsBug(event.getPlayer())) {
return; return;
} }
if (MaterialHelper.isButtonCompatible(block.getType())) { if (MaterialHelper.isButtonCompatible(block.getType())) {
Portal portal = PortalHandler.getByBlock(block); Portal portal = PortalUtil.getByBlock(block);
if (portal == null) { if (portal == null) {
return; return;
} }
@@ -323,7 +246,7 @@ public class PlayerEventListener implements Listener {
} else { } else {
//Display information about the portal if it has no sign //Display information about the portal if it has no sign
ItemStack heldItem = player.getInventory().getItem(hand); ItemStack heldItem = player.getInventory().getItem(hand);
if (heldItem.getType().isAir() || !heldItem.getType().isBlock()) { if (heldItem != null && (heldItem.getType().isAir() || !heldItem.getType().isBlock())) {
displayPortalInfo(block, player); displayPortalInfo(block, player);
} }
} }
@@ -332,32 +255,29 @@ public class PlayerEventListener implements Listener {
/** /**
* Displays information about a clicked portal * Displays information about a clicked portal
* *
* <p>This will only display portal info if the portal has no sign and is not silent.</p> * <p>This will only display portal info if the portal has no sign and is not quiet.</p>
* *
* @param block <p>The clicked block</p> * @param block <p>The clicked block</p>
* @param player <p>The player that clicked the block</p> * @param player <p>The player that clicked the block</p>
*/ */
private void displayPortalInfo(Block block, Player player) { private void displayPortalInfo(@NotNull Block block, @NotNull Player player) {
Portal portal = PortalHandler.getByBlock(block); Portal portal = PortalUtil.getByBlock(block);
if (portal == null) { if (portal == null) {
return; return;
} }
//Display portal information as a portal without a sign does not display any //Display portal information as a portal without a sign does not display any
if (portal.getOptions().hasNoSign() && (!portal.getOptions().isSilent() || player.isSneaking())) { if (portal.getOptions().hasNoSign() && (!portal.getOptions().isQuiet() || player.isSneaking())) {
MessageSender sender = Stargate.getMessageSender(); FormatBuilder builder = new SGFormatBuilder();
sender.sendSuccessMessage(player, ChatColor.GOLD + Stargate.getString("portalInfoTitle")); builder.append(ChatColor.GOLD).append(Message.PORTAL_INFO_TITLE).append("\n").
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoName"), append(Message.PORTAL_INFO_NAME).replace("%name%", portal.getName()).append("\n").
"%name%", portal.getName())); append(Message.PORTAL_INFO_DESTINATION).replace("%destination%", portal.getDestinationName()).append("\n");
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoDestination"),
"%destination%", portal.getDestinationName()));
if (portal.getOptions().isBungee()) { if (portal.getOptions().isBungee()) {
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoServer"), builder.append(Message.PORTAL_INFO_SERVER).replace("%server%", portal.getNetwork());
"%server%", portal.getNetwork()));
} else { } else {
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoNetwork"), builder.append(Message.PORTAL_INFO_NETWORK).replace("%network%", portal.getNetwork());
"%network%", portal.getNetwork()));
} }
builder.displayRaw(player);
} }
} }
@@ -369,10 +289,9 @@ public class PlayerEventListener implements Listener {
* clicking once the bug is fixed.</p> * clicking once the bug is fixed.</p>
* *
* @param player <p>The player performing the right-click</p> * @param player <p>The player performing the right-click</p>
* @param block <p>The block to check</p>
* @return <p>True if the click is a bug and should be cancelled</p> * @return <p>True if the click is a bug and should be cancelled</p>
*/ */
private boolean clickIsBug(Player player, Block block) { private boolean clickIsBug(@NotNull Player player) {
Long previousEventTime = previousEventTimes.get(player); Long previousEventTime = previousEventTimes.get(player);
if (previousEventTime != null && previousEventTime + 50 > System.currentTimeMillis()) { if (previousEventTime != null && previousEventTime + 50 > System.currentTimeMillis()) {
previousEventTimes.put(player, null); previousEventTimes.put(player, null);

View File

@@ -1,52 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
/**
* This listener listens for any plugins being enabled or disabled to catch the loading of vault
*/
@SuppressWarnings("unused")
public class PluginEventListener implements Listener {
private final Stargate stargate;
/**
* Instantiates a new plugin event listener
*
* @param stargate <p>A reference to the stargate plugin to </p>
*/
public PluginEventListener(Stargate stargate) {
this.stargate = stargate;
}
/**
* This event listens for and announces that the vault plugin was detected and enabled
*
* <p>Each time this event is called, the economy handler will try to enable vault</p>
*
* @param ignored <p>The actual event called. This is currently not used</p>
*/
@EventHandler
public void onPluginEnable(PluginEnableEvent ignored) {
if (Stargate.getEconomyConfig().setupEconomy(stargate.getServer().getPluginManager())) {
String vaultVersion = Stargate.getEconomyConfig().getVault().getDescription().getVersion();
Stargate.logInfo(Stargate.replaceVars(Stargate.getString("vaultLoaded"), "%version%", vaultVersion));
}
}
/**
* This event listens for the vault plugin being disabled and notifies the console
*
* @param event <p>The event caused by disabling a plugin</p>
*/
@EventHandler
public void onPluginDisable(PluginDisableEvent event) {
if (event.getPlugin().equals(Stargate.getEconomyConfig().getVault())) {
Stargate.logInfo("Vault plugin lost.");
}
}
}

View File

@@ -1,104 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.FromTheEndTeleportation;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPortalEnterEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.world.PortalCreateEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Listens for and cancels relevant portal events
*/
public class PortalEventListener implements Listener {
private static final List<FromTheEndTeleportation> playersFromTheEnd = new ArrayList<>();
/**
* Listens for and aborts vanilla portal creation caused by stargate creation
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onPortalCreation(PortalCreateEvent event) {
if (event.isCancelled()) {
return;
}
//Unnecessary nether portal creation is only triggered by nether pairing
if (event.getReason() == PortalCreateEvent.CreateReason.NETHER_PAIR) {
//If an entity is standing in a Stargate entrance, it can be assumed that the creation is a mistake
Entity entity = event.getEntity();
if (entity != null && PortalHandler.getByAdjacentEntrance(entity.getLocation()) != null) {
Stargate.debug("PortalEventListener::onPortalCreation",
"Cancelled nether portal create event");
event.setCancelled(true);
}
}
}
/**
* Listen for entities entering an artificial end portal
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onEntityPortalEnter(EntityPortalEnterEvent event) {
Location location = event.getLocation();
World world = location.getWorld();
Entity entity = event.getEntity();
//Hijack normal portal teleportation if teleporting from a stargate
if (entity instanceof Player player && location.getBlock().getType() == Material.END_PORTAL && world != null &&
world.getEnvironment() == World.Environment.THE_END) {
Portal portal = PortalHandler.getByAdjacentEntrance(location);
if (portal == null) {
return;
}
//Remove any old player teleportations in case weird things happen
playersFromTheEnd.removeIf((teleportation -> teleportation.getPlayer() == player));
//Decide if the anything stops the player from teleporting
if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(), player, null)) {
//Teleport the player back to the portal they came in, just in case
playersFromTheEnd.add(new FromTheEndTeleportation(player, portal));
}
playersFromTheEnd.add(new FromTheEndTeleportation(player, portal.getPortalActivator().getDestination()));
}
}
/**
* Listen for the respawn event to catch players teleporting from the end in an artificial end portal
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent event) {
Player respawningPlayer = event.getPlayer();
int playerIndex = playersFromTheEnd.indexOf(new FromTheEndTeleportation(respawningPlayer, null));
if (playerIndex == -1) {
return;
}
FromTheEndTeleportation teleportation = playersFromTheEnd.get(playerIndex);
playersFromTheEnd.remove(playerIndex);
Portal exitPortal = teleportation.getExit();
//Overwrite respawn location to respawn in front of the portal
event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit(respawningPlayer,
respawningPlayer.getLocation()));
//Properly close the portal to prevent it from staying in a locked state until it times out
exitPortal.getPortalOpener().closePortal(false);
}
}

View File

@@ -0,0 +1,267 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.Directional;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockFertilizeEvent;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.EntityBlockFormEvent;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.block.SpongeAbsorbEvent;
import org.bukkit.event.block.TNTPrimeEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityBreakDoorEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityPlaceEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.world.PortalCreateEvent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* A listener for any events that might cause a Stargate to be altered or break
*/
public class StargateBreakListener implements Listener {
/**
* Cancels blocks from being placed in the Stargate's entrance
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
if (!Stargate.getGateConfig().protectEntrance()) {
return;
}
Block block = event.getBlock();
Portal portal = PortalUtil.getByEntrance(block);
if (portal != null) {
//Prevent blocks from being placed in the entrance, if protectEntrance is enabled, as breaking the block
// would destroy the portal
event.setCancelled(true);
}
}
/**
* This method catches any explosion events
*
* <p>If destroyed by explosions is enabled, any portals destroyed by the explosion will be unregistered. If not,
* the explosion will be cancelled.</p>
*
* @param event <p>The triggered explosion event</p>
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onEntityExplode(@NotNull EntityExplodeEvent event) {
for (Block block : event.blockList()) {
Portal portal = PortalUtil.getByBlock(block);
if (portal == null) {
continue;
}
if (Stargate.getGateConfig().destroyedByExplosion()) {
PortalRegistry.unregisterPortal(portal, true);
} else {
event.setCancelled(true);
break;
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onCreatureSpawn(@NotNull CreatureSpawnEvent event) {
//Prevent Zombified Piglins and other creatures form spawning at stargates
if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NETHER_PORTAL &&
PortalUtil.getByEntrance(event.getLocation()) != null) {
event.setCancelled(true);
Stargate.debug("EntitySpawnListener", "Prevented creature from spawning at Stargate");
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPistonExtend(BlockPistonExtendEvent event) {
cancelPistonIfNeeded(event.getBlocks(), event.getDirection(), event);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPistonRetract(@NotNull BlockPistonRetractEvent event) {
cancelPistonIfNeeded(event.getBlocks(), event.getDirection(), event);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockExplode(@NotNull BlockExplodeEvent event) {
cancelIfAtEntrance(event, event.blockList());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockFromTo(@NotNull BlockFromToEvent event) {
cancelIfAtEntrance(event, event.getToBlock());
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockFormEvent(@NotNull BlockFormEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockPhysics(@NotNull BlockPhysicsEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockBurn(@NotNull BlockBurnEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockIgnite(@NotNull BlockIgniteEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockFade(@NotNull BlockFadeEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockFertilize(@NotNull BlockFertilizeEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockMultiPlace(@NotNull BlockMultiPlaceEvent event) {
cancelIfAtEntrance(event, getBlocksFromBlockStates(event.getReplacedBlockStates()));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityBlockForm(@NotNull EntityBlockFormEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onLeavesDecay(@NotNull LeavesDecayEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onSpongeAbsorb(@NotNull SpongeAbsorbEvent event) {
cancelIfAtEntrance(event, getBlocksFromBlockStates(event.getBlocks()));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityChangeBlock(@NotNull EntityChangeBlockEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityBreakDoor(@NotNull EntityBreakDoorEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPortalCreate(@NotNull PortalCreateEvent event) {
cancelIfAtEntrance(event, getBlocksFromBlockStates(event.getBlocks()));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityPlace(@NotNull EntityPlaceEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerBucketEmpty(@NotNull PlayerBucketEmptyEvent event) {
cancelIfAtEntrance(event, event.getBlock());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockDispense(@NotNull BlockDispenseEvent event) {
if (event.getBlock().getBlockData() instanceof Directional dispenser) {
cancelIfAtEntrance(event, event.getBlock().getRelative(dispenser.getFacing()));
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onTNTPrime(@NotNull TNTPrimeEvent tntPrimeEvent) {
cancelIfAtEntrance(tntPrimeEvent, tntPrimeEvent.getBlock());
}
/**
* Cancels the given event if it involves a Stargate's entrance
*
* @param event <p>The event to cancel</p>
* @param block <p>The block in question</p>
*/
private boolean cancelIfAtEntrance(@NotNull Cancellable event, @NotNull Block block) {
if (PortalUtil.getByEntrance(block) != null || PortalUtil.getByControl(block) != null ||
PortalUtil.getByBlock(block) != null) {
event.setCancelled(true);
return true;
} else {
return false;
}
}
/**
* Cancels the given event if it involves a Stargate's entrance
*
* @param event <p>The event to cancel</p>
* @param blocks <p>The blocks in question</p>
*/
private void cancelIfAtEntrance(@NotNull Cancellable event, @NotNull List<Block> blocks) {
for (Block block : blocks) {
if (cancelIfAtEntrance(event, block)) {
return;
}
}
}
/**
* Gets a list of blocks from a list of block states
*
* @param blockStates <p>The block states to convert into blocks</p>
* @return <p>The corresponding blocks</p>
*/
@NotNull
private List<Block> getBlocksFromBlockStates(@NotNull List<BlockState> blockStates) {
return blockStates.stream().map(BlockState::getBlock).toList();
}
/**
* Cancels the movement of a piston if it would interfere with a Stargate
*
* @param blocks <p>The blocks involved in the move</p>
* @param blockFace <p>The block face of the piston</p>
* @param event <p>The event to possibly cancel</p>
*/
private void cancelPistonIfNeeded(@NotNull List<Block> blocks, @NotNull BlockFace blockFace,
@NotNull Cancellable event) {
cancelIfAtEntrance(event, blocks);
if (event.isCancelled()) {
return;
}
List<Block> movedBlocks = blocks.stream().map(block -> block.getRelative(blockFace)).toList();
cancelIfAtEntrance(event, movedBlocks);
}
}

View File

@@ -0,0 +1,160 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.event.StargateDestroyEvent;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalCreator;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.jetbrains.annotations.NotNull;
/**
* This class is responsible for listening to relevant block events related to creating and breaking portals
*/
public class StargateCreateDestroyListener implements Listener {
/**
* Detects sign changes to detect if the user is creating a new gate
*
* @param event <p>The triggered event</p>
*/
@EventHandler(ignoreCancelled = true)
public void onSignChange(@NotNull SignChangeEvent event) {
Player player = event.getPlayer();
Block block = event.getBlock();
//Ignore normal signs
if (!(block.getBlockData() instanceof WallSign)) {
return;
}
final Portal portal = new PortalCreator(event, player).createPortal();
//Not creating a gate, just placing a sign
if (portal == null) {
return;
}
//Remove the sign if the no sign option is enabled
if (portal.getOptions().hasNoSign()) {
Material replaceMaterial = PortalFileHelper.decideRemovalMaterial(portal.getLocation().getSignBlock(), portal);
BlockChangeRequest request = new BlockChangeRequest(portal.getLocation().getSignBlock(), replaceMaterial, null);
Stargate.addControlBlockUpdateRequest(request);
}
new SGFormatBuilder(Message.CREATED).success(player);
Stargate.debug("onSignChange", "Initialized stargate: " + portal.getName());
Stargate.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(),
portal::drawSign, 1);
}
/**
* Detects block breaking to detect if the user is destroying a gate
*
* @param event <p>The triggered event</p>
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onBlockBreak(@NotNull BlockBreakEvent event) {
Block block = event.getBlock();
Player player = event.getPlayer();
//Decide if a portal is broken
Portal portal = PortalUtil.getByBlock(block);
if (portal == null && Stargate.getGateConfig().protectEntrance()) {
portal = PortalUtil.getByEntrance(block);
}
if (portal == null) {
return;
}
boolean deny = false;
String denyMessage = "";
// Block breaking the button from breaking the entire Stargate
if (portal.getStructure().getButton() != null && portal.getStructure().getButton().equals(
new BlockLocation(event.getBlock()))) {
event.setCancelled(true);
return;
}
//Decide if the user can destroy the portal
if (!PermissionHelper.canDestroyPortal(player, portal)) {
denyMessage = new SGFormatBuilder(Message.ACCESS_DENIED).toString();
deny = true;
Stargate.logInfo(String.format("%s tried to destroy gate", player.getName()));
}
int cost = Stargate.getEconomyConfig().getDestroyCost(player, portal.getGate());
//Create and call a StarGateDestroyEvent
StargateDestroyEvent destroyEvent = new StargateDestroyEvent(portal, player, deny, denyMessage, cost);
Stargate.getInstance().getServer().getPluginManager().callEvent(destroyEvent);
if (destroyEvent.isCancelled()) {
event.setCancelled(true);
return;
}
//Destroy denied
if (destroyEvent.getDeny()) {
if (!destroyEvent.getDenyReason().trim().isEmpty()) {
new SGFormatBuilder(destroyEvent.getDenyReason()).error(player);
}
event.setCancelled(true);
return;
}
//Take care of payment transactions
if (!handleEconomyPayment(destroyEvent, player, portal, event)) {
return;
}
PortalRegistry.unregisterPortal(portal, true);
new SGFormatBuilder(Message.DESTROYED).success(player);
}
/**
* Handles economy payment for breaking the portal
*
* @param destroyEvent <p>The destroy event</p>
* @param player <p>The player which triggered the event</p>
* @param portal <p>The broken portal</p>
* @param event <p>The break event</p>
* @return <p>True if the payment was successful. False if the event was cancelled</p>
*/
private boolean handleEconomyPayment(@NotNull StargateDestroyEvent destroyEvent, @NotNull Player player,
@NotNull Portal portal, @NotNull BlockBreakEvent event) {
int cost = destroyEvent.getCost();
if (cost != 0) {
String portalName = portal.getName();
//Cannot pay
if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) {
Stargate.debug("onBlockBreak", "Insufficient Funds");
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
event.setCancelled(true);
return false;
}
//Tell the player they've paid or deceived money
if (cost > 0) {
EconomyHelper.sendDeductMessage(portalName, player, cost);
} else {
EconomyHelper.sendRefundMessage(portalName, player, cost);
}
}
return true;
}
}

View File

@@ -0,0 +1,530 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.FromTheEndTeleportation;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.BungeeHelper;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPortalEnterEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import org.bukkit.util.Vector;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.api.GeyserApi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This listener listens for the vehicle move event to teleport vehicles through portals
*/
public class StargateTeleportListener implements Listener {
private static final Map<Player, FromTheEndTeleportation> playersFromTheEnd = new HashMap<>();
private boolean hasGeyser = true;
private boolean hasFloodgate = true;
/**
* This event handler handles some special teleportation events
*
* <p>This event cancels nether portal, end gateway and end portal teleportation if the user teleported from a
* stargate entrance. This prevents the user from just teleporting to the nether or the end with portals using
* the special teleportation blocks.</p>
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onPlayerTeleport(@NotNull PlayerTeleportEvent event) {
PlayerTeleportEvent.TeleportCause cause = event.getCause();
//Block normal portal teleportation if teleporting from a stargate
if (!event.isCancelled() && (cause == PlayerTeleportEvent.TeleportCause.NETHER_PORTAL ||
cause == PlayerTeleportEvent.TeleportCause.END_GATEWAY ||
cause == PlayerTeleportEvent.TeleportCause.END_PORTAL)
&& PortalUtil.getByAdjacentEntrance(event.getFrom()) != null) {
event.setCancelled(true);
}
}
/**
* This event handler prevents sending entities to the normal nether instead of the stargate target
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onPortalEvent(@NotNull EntityPortalEvent event) {
if (event.isCancelled()) {
return;
}
Entity entity = event.getEntity();
//Cancel normal portal event is near a stargate
if (PortalUtil.getByAdjacentEntrance(event.getFrom(), EntityHelper.getEntityMaxSizeInt(entity)) != null) {
event.setCancelled(true);
}
}
/**
* Check for a vehicle moving through a portal
*
* @param event <p>The triggered move event</p>
*/
@EventHandler
public void onVehicleMove(@NotNull VehicleMoveEvent event) {
if (!Stargate.getGateConfig().handleVehicles()) {
return;
}
List<Entity> passengers = event.getVehicle().getPassengers();
Vehicle vehicle = event.getVehicle();
Portal entrancePortal;
int entitySize = EntityHelper.getEntityMaxSizeInt(vehicle);
if (EntityHelper.getEntityMaxSize(vehicle) > 1) {
entrancePortal = PortalUtil.getByAdjacentEntrance(event.getTo(), entitySize - 1);
} else {
entrancePortal = PortalUtil.getByEntrance(event.getTo());
}
//Return if the portal cannot be teleported through
if (entrancePortal == null || !entrancePortal.isOpen() || entrancePortal.getOptions().isBungee()) {
return;
}
teleportVehicle(passengers, entrancePortal, vehicle);
}
/**
* This event handler detects if a player moves into a portal
*
* @param event <p>The player move event which was triggered</p>
*/
@EventHandler
public void onPlayerMove(@NotNull PlayerMoveEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
BlockLocation fromLocation = new BlockLocation(event.getFrom().getBlock());
BlockLocation toLocation = new BlockLocation(event.getTo().getBlock());
Player player = event.getPlayer();
//Check whether the event needs to be considered
if (!isRelevantMoveEvent(event, player, fromLocation, toLocation)) {
return;
}
Portal entrancePortal = PortalUtil.getByEntrance(toLocation);
//Check an additional block away in case the portal is a bungee portal using END_PORTAL
if (entrancePortal == null) {
entrancePortal = PortalUtil.getByAdjacentEntrance(toLocation);
// This should never realistically be null
if (entrancePortal == null) {
return;
}
}
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
if (destination == null) {
return;
}
Entity playerVehicle = player.getVehicle();
//If the player is in a vehicle, but vehicle handling is disabled, just ignore the player
if (playerVehicle == null || (playerVehicle instanceof LivingEntity &&
Stargate.getGateConfig().handleVehicles())) {
teleportPlayer(playerVehicle, player, entrancePortal, destination, event);
}
}
/**
* Listen for entities entering an artificial end portal
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onEntityPortalEnter(@NotNull EntityPortalEnterEvent event) {
Location location = event.getLocation();
World world = location.getWorld();
Entity entity = event.getEntity();
//Hijack normal portal teleportation if teleporting from a stargate, and teleporting from an end portal in the
// end
if (!(entity instanceof Player player) || location.getBlock().getType() != Material.END_PORTAL ||
world == null || world.getEnvironment() != World.Environment.THE_END) {
return;
}
Portal portal = PortalUtil.getByAdjacentEntrance(location);
if (portal == null) {
return;
}
Stargate.debug("PortalEventListener::onEntityPortalEnter",
"Found player " + player + " entering END_PORTAL " + portal);
//Decide if the anything stops the player from teleporting
if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(),
player, null) || portal.getOptions().isBungee()) {
//Teleport the player back to the portal they came in, just in case
playersFromTheEnd.put(player, new FromTheEndTeleportation(portal));
Stargate.debug("PortalEventListener::onEntityPortalEnter",
"Sending player back to the entrance");
} else {
Portal destination = portal.getPortalActivator().getDestination();
if (destination != null) {
playersFromTheEnd.put(player, new FromTheEndTeleportation(destination));
Stargate.debug("PortalEventListener::onEntityPortalEnter",
"Sending player to destination");
}
}
}
/**
* Listen for the respawn event to catch players teleporting from the end in an artificial end portal
*
* @param event <p>The triggered event</p>
*/
@EventHandler
public void onRespawn(@NotNull PlayerRespawnEvent event) {
Player respawningPlayer = event.getPlayer();
FromTheEndTeleportation teleportation = playersFromTheEnd.remove(respawningPlayer);
if (teleportation == null) {
return;
}
Portal exitPortal = teleportation.exitPortal();
//Overwrite respawn location to respawn in front of the portal
PlayerTeleporter teleporter = new PlayerTeleporter(exitPortal, respawningPlayer);
Location respawnLocation = teleporter.getExit();
event.setRespawnLocation(respawnLocation);
//Try and force the player if for some reason the changing of respawn location isn't properly handled
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () ->
respawningPlayer.teleport(respawnLocation), 1);
//Properly close the portal to prevent it from staying in a locked state until it times out
exitPortal.getPortalOpener().closePortal(false);
Stargate.debug("PortalEventListener::onRespawn", "Overwriting respawn for " + respawningPlayer +
" to " + respawnLocation.getWorld() + ":" + respawnLocation);
}
/**
* Teleports a vehicle through a stargate
*
* @param passengers <p>The passengers inside the vehicle</p>
* @param entrancePortal <p>The portal the vehicle is entering</p>
* @param vehicle <p>The vehicle passing through</p>
*/
private static void teleportVehicle(@NotNull List<Entity> passengers, @NotNull Portal entrancePortal,
@NotNull Vehicle vehicle) {
String route = "VehicleEventListener::teleportVehicle";
if (!passengers.isEmpty() && TeleportHelper.containsPlayer(passengers)) {
Stargate.debug(route, "Found passenger vehicle");
teleportPlayerAndVehicle(entrancePortal, vehicle);
} else {
Stargate.debug(route, "Found vehicle without players");
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination();
if (destinationPortal == null) {
Stargate.debug(route, "Unable to find portal destination");
return;
}
Stargate.debug("vehicleTeleport", destinationPortal.getLocation().getWorld() + " " +
destinationPortal.getLocation().getSignBlock());
new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal);
}
}
/**
* Teleports a player and the vehicle the player sits in
*
* @param entrancePortal <p>The portal the minecart entered</p>
* @param vehicle <p>The vehicle to teleport</p>
*/
private static void teleportPlayerAndVehicle(@NotNull Portal entrancePortal, @NotNull Vehicle vehicle) {
Entity rootEntity = vehicle;
while (rootEntity.getVehicle() != null) {
rootEntity = rootEntity.getVehicle();
}
List<Player> players = TeleportHelper.getPlayers(rootEntity.getPassengers());
Portal destinationPortal = getDestinationPortal(players, entrancePortal);
//Cancel the teleport if no players activated the portal, or if any players are denied access
boolean cancelTeleportation = false;
for (Player player : players) {
if (destinationPortal == null) {
cancelTeleportation = true;
if (!entrancePortal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.INVALID_DESTINATION).error(player);
}
} else if (!TeleportHelper.playerCanTeleport(player, entrancePortal, destinationPortal)) {
cancelTeleportation = true;
}
}
if (cancelTeleportation || destinationPortal == null) {
return;
}
//Take payment from all players
if (!takePayment(players, entrancePortal, destinationPortal)) {
return;
}
// Perform the teleportation
teleportPlayerAndVehicle(players, vehicle, entrancePortal, destinationPortal);
}
/**
* Performs the teleportation of one or more players in a vehicle
*
* @param players <p>The players to be teleported</p>
* @param vehicle <p>The vehicle that triggered the teleportation</p>
* @param entrancePortal <p>The portal the player(s) and vehicle entered from</p>
* @param destinationPortal <p>The portal the player(s) and vehicle are teleporting to</p>
*/
private static void teleportPlayerAndVehicle(@NotNull List<Player> players, @NotNull Vehicle vehicle,
@NotNull Portal entrancePortal, @NotNull Portal destinationPortal) {
//Teleport the vehicle and inform the user if the vehicle was teleported
boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal);
if (!teleported) {
return;
}
if (!entrancePortal.getOptions().isQuiet()) {
for (Player player : players) {
new SGFormatBuilder(Message.TELEPORTED).success(player);
}
}
entrancePortal.getPortalOpener().closePortal(false);
}
/**
* Tries to get the destination portal selected by one of the players included in the teleportation
*
* @param players <p>The players to be teleported</p>
* @param entrancePortal <p>The portal the players are entering</p>
* @return <p>The destination portal, or null if not found</p>
*/
@Nullable
private static Portal getDestinationPortal(@NotNull List<Player> players, @NotNull Portal entrancePortal) {
for (Player player : players) {
//The entrance portal must be open for one player for the teleportation to happen
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
continue;
}
//Check if any of the players has selected the destination
Portal possibleDestinationPortal = entrancePortal.getPortalActivator().getDestination(player);
if (possibleDestinationPortal != null) {
return possibleDestinationPortal;
}
}
return null;
}
/**
* Takes payment for the given players
*
* @param players <p>The players to take payment from</p>
* @param entrancePortal <p>The portal the players are travelling from</p>
* @param destinationPortal <p>The portal the players are travelling to</p>
* @return <p>True if payment was successfully taken, false otherwise</p>
*/
private static boolean takePayment(@NotNull List<Player> players, @NotNull Portal entrancePortal,
@NotNull Portal destinationPortal) {
for (Player player : players) {
//To prevent the case where the first passenger pays and then the second passenger is denied, this has to be
// run after it has been confirmed that all passengers are able to pay. Also note that some players might
// not have to pay, and thus the cost check has to be in the loop,
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
if (cost > 0) {
if (EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost)) {
return false;
}
}
}
return true;
}
/**
* Teleports a player, also teleports the player's vehicle if it's a living entity
*
* @param playerVehicle <p>The vehicle the player is currently sitting in</p>
* @param player <p>The player which moved</p>
* @param entrancePortal <p>The entrance the player entered</p>
* @param destination <p>The destination of the entrance portal</p>
* @param event <p>The move event causing the teleportation to trigger</p>
*/
private void teleportPlayer(@Nullable Entity playerVehicle, @NotNull Player player, @NotNull Portal entrancePortal,
@NotNull Portal destination, @NotNull PlayerMoveEvent event) {
if (playerVehicle instanceof LivingEntity) {
//Make sure any horses are properly tamed
if (playerVehicle instanceof AbstractHorse horse && !horse.isTamed()) {
horse.setTamed(true);
horse.setOwner(player);
}
//Teleport the player's vehicle
player.setVelocity(new Vector());
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleportEntity(entrancePortal);
} else {
//Just teleport the player like normal
new PlayerTeleporter(destination, player).teleportPlayer(entrancePortal, event);
}
if (!entrancePortal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.TELEPORTED).success(player);
}
entrancePortal.getPortalOpener().closePortal(false);
}
/**
* Checks whether a player move event is relevant for this plugin
*
* @param event <p>The player move event to check</p>
* @param player <p>The player which moved</p>
* @param fromLocation <p>The location the player is moving from</p>
* @param toLocation <p>The location the player is moving to</p>
* @return <p>True if the event is relevant</p>
*/
private boolean isRelevantMoveEvent(@NotNull PlayerMoveEvent event, Player player,
@NotNull BlockLocation fromLocation, @NotNull BlockLocation toLocation) {
//Check to see if the player moved to another block
if (fromLocation.equals(toLocation)) {
return false;
}
//Get the portal the player entered, if any
Portal entrancePortal = getEnteredPortal(toLocation, player);
if (entrancePortal == null) {
return false;
}
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
//Catch always open portals without a valid destination to prevent the user for being teleported and denied
if (!entrancePortal.getOptions().isBungee() && destination == null) {
return false;
}
//Decide if the anything stops the player from teleport
if (PermissionHelper.playerCannotTeleport(entrancePortal, destination, player, event)) {
return false;
}
//Decide if the user should be teleported to another bungee server
if (entrancePortal.getOptions().isBungee()) {
if (BungeeHelper.bungeeTeleport(player, entrancePortal, event) && !entrancePortal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.TELEPORTED).success(player);
}
return false;
}
//Make sure to check if the player has any leashed creatures, even though leashed teleportation is disabled
return TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
}
/**
* Gets the portal a player entered
*
* @param toLocation <p>The location the player moved to</p>
* @param player <p>The player that moved</p>
* @return <p>The portal the player entered, or null if no portal was entered</p>
*/
private Portal getEnteredPortal(@NotNull BlockLocation toLocation, @NotNull Player player) {
Portal entrancePortal = PortalUtil.getByEntrance(toLocation);
// Return if in an entrance
if (entrancePortal != null) {
return entrancePortal;
}
//Check an additional block away for special cases like BungeeCord portals using END_PORTAL as its material
entrancePortal = PortalUtil.getByAdjacentEntrance(toLocation);
if (entrancePortal == null) {
return null;
}
// If END_GATEWAY and END_PORTAL cannot appear, skip further checks
Set<Material> entranceMaterials = MaterialHelper.specifiersToMaterials(entrancePortal.getGate().getPortalOpenMaterials());
if (!entranceMaterials.contains(Material.END_GATEWAY) && !entranceMaterials.contains(Material.END_PORTAL)) {
return null;
}
// Get the real materials in the entrance, as END_GATEWAY or END_PORTAL may be available, but not chosen
Set<Material> materialsInEntrance = new HashSet<>();
for (BlockLocation location : entrancePortal.getStructure().getEntrances()) {
materialsInEntrance.add(location.getType());
}
// Abort if not a special case
if ((!materialsInEntrance.contains(Material.END_GATEWAY) || !isGeyserPlayer(player)) &&
(!entrancePortal.getOptions().isBungee() || !materialsInEntrance.contains(Material.END_PORTAL))) {
return null;
}
return entrancePortal;
}
/**
* Checks whether the given player is connected through Geyser
*
* @param player <p>The player to check</p>
* @return <p>True if the player is connected through Geyser</p>
*/
private boolean isGeyserPlayer(@NotNull Player player) {
// Prevent unnecessary checking for non-geyser and floodgate servers
if (!hasGeyser && !hasFloodgate) {
return false;
}
// Use Geyser API to get connection status
if (hasGeyser) {
try {
return GeyserApi.api().connectionByUuid(player.getUniqueId()) != null;
} catch (NoClassDefFoundError error1) {
hasGeyser = false;
}
}
// Use Floodgate API to get connection status
if (hasFloodgate) {
try {
return FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId());
} catch (NoClassDefFoundError error2) {
hasFloodgate = false;
}
}
return false;
}
}

View File

@@ -1,36 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.portal.PortalHandler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
/**
* This listener listens to teleportation-related events
*/
@SuppressWarnings("unused")
public class TeleportEventListener implements Listener {
/**
* This event handler handles some special teleportation events
*
* <p>This event cancels nether portal, end gateway and end portal teleportation if the user teleported from a
* stargate entrance. This prevents the user from just teleporting to the nether or the end with portals using
* the special teleportation blocks.</p>
*
* @param event <p>The event to check and possibly cancel</p>
*/
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
PlayerTeleportEvent.TeleportCause cause = event.getCause();
//Block normal portal teleportation if teleporting from a stargate
if (!event.isCancelled() && (cause == PlayerTeleportEvent.TeleportCause.NETHER_PORTAL ||
cause == PlayerTeleportEvent.TeleportCause.END_GATEWAY ||
cause == PlayerTeleportEvent.TeleportCause.END_PORTAL)
&& PortalHandler.getByAdjacentEntrance(event.getFrom()) != null) {
event.setCancelled(true);
}
}
}

View File

@@ -1,180 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.Teleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import java.util.List;
/**
* This listener listens for the vehicle move event to teleport vehicles through portals
*/
@SuppressWarnings("unused")
public class VehicleEventListener implements Listener {
/**
* Check for a vehicle moving through a portal
*
* @param event <p>The triggered move event</p>
*/
@EventHandler
public void onVehicleMove(VehicleMoveEvent event) {
if (!Stargate.getGateConfig().handleVehicles()) {
return;
}
List<Entity> passengers = event.getVehicle().getPassengers();
Vehicle vehicle = event.getVehicle();
Portal entrancePortal;
int entitySize = EntityHelper.getEntityMaxSizeInt(vehicle);
if (EntityHelper.getEntityMaxSize(vehicle) > 1) {
entrancePortal = PortalHandler.getByAdjacentEntrance(event.getTo(), entitySize - 1);
} else {
entrancePortal = PortalHandler.getByEntrance(event.getTo());
}
//Return if the portal cannot be teleported through
if (entrancePortal == null || !entrancePortal.isOpen() || entrancePortal.getOptions().isBungee()) {
return;
}
teleportVehicle(passengers, entrancePortal, vehicle);
}
/**
* Teleports a vehicle through a stargate
*
* @param passengers <p>The passengers inside the vehicle</p>
* @param entrancePortal <p>The portal the vehicle is entering</p>
* @param vehicle <p>The vehicle passing through</p>
*/
private static void teleportVehicle(List<Entity> passengers, Portal entrancePortal, Vehicle vehicle) {
String route = "VehicleEventListener::teleportVehicle";
if (!passengers.isEmpty() && passengers.get(0) instanceof Player) {
Stargate.debug(route, "Found passenger vehicle");
teleportPlayerAndVehicle(entrancePortal, vehicle, passengers);
} else {
Stargate.debug(route, "Found empty vehicle");
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination();
if (destinationPortal == null) {
Stargate.debug(route, "Unable to find portal destination");
return;
}
Stargate.debug("vehicleTeleport", destinationPortal.getWorld() + " " +
destinationPortal.getSignLocation());
new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal);
}
}
/**
* Teleports a player and the vehicle the player sits in
*
* @param entrancePortal <p>The portal the minecart entered</p>
* @param vehicle <p>The vehicle to teleport</p>
* @param passengers <p>Any entities sitting in the minecart</p>
*/
private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle, List<Entity> passengers) {
Player player = (Player) passengers.get(0);
//On the assumption that a non-player cannot sit in the driver's seat and since some portals can only be open
// to one player at a time, we only need to check if the portal is open to the driver.
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
return;
}
//If no destination exists, the teleportation cannot happen
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination(player);
if (destinationPortal == null) {
return;
}
//Make sure all player passengers are allowed to, and can afford to, enter the portal
for (Entity entity : passengers) {
if (entity instanceof Player && !playerCanTeleport((Player) entity, entrancePortal, destinationPortal)) {
return;
}
}
//To prevent the case where the first passenger pays and then the second passenger is denied, this has to be
// run after it has been confirmed that all passengers are able to pay
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
if (cost > 0) {
if (!takePlayerPayment(passengers, entrancePortal, cost)) {
return;
}
}
//Teleport the vehicle and inform the user if the vehicle was teleported
boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal);
if (teleported) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
}
}
/**
* Takes payment from all player passengers
*
* @param passengers <p>All passengers in the teleporting vehicle</p>
* @param entrancePortal <p>The portal the vehicle is entering from</p>
* @param cost <p>The cost each player has to pay</p>
* @return <p>True if all player passengers paid successfully</p>
*/
private static boolean takePlayerPayment(List<Entity> passengers, Portal entrancePortal, int cost) {
for (Entity entity : passengers) {
//If the passenger is a player, make it pay
if (entity instanceof Player && EconomyHelper.cannotPayTeleportFee(entrancePortal, (Player) entity, cost)) {
return false;
}
}
return true;
}
/**
* Checks whether the given player is allowed to and can afford to teleport
*
* @param player <p>The player trying to teleport</p>
* @param entrancePortal <p>The portal the player is entering</p>
* @param destinationPortal <p>The portal the player is to exit from</p>
* @return <p>True if the player is allowed to teleport and is able to pay necessary fees</p>
*/
private static boolean playerCanTeleport(Player player, Portal entrancePortal, Portal destinationPortal) {
//Make sure the user can access the portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destinationPortal)) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Check if the player is able to afford the teleport fee
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
if (!canAffordFee) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds"));
}
return false;
}
return Teleporter.noLeashedCreaturesPreventTeleportation(player);
}
}

View File

@@ -1,49 +0,0 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateConfig;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
/**
* This listener listens for the loading and unloading of worlds to load and unload stargates
*/
@SuppressWarnings("unused")
public class WorldEventListener implements Listener {
/**
* This listener listens for the loading of a world and loads all gates from the world if not already loaded
*
* @param event <p>The triggered world load event</p>
*/
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
StargateConfig config = Stargate.getStargateConfig();
if (!config.getManagedWorlds().contains(event.getWorld().getName()) &&
PortalFileHelper.loadAllPortals(event.getWorld())) {
config.addManagedWorld(event.getWorld().getName());
}
}
/**
* This listener listens for the unloading of a world
*
* @param event <p>The triggered world unload event</p>
*/
@EventHandler
public void onWorldUnload(WorldUnloadEvent event) {
Stargate.debug("onWorldUnload", "Reloading all Stargates");
World world = event.getWorld();
String worldName = world.getName();
StargateConfig config = Stargate.getStargateConfig();
if (config.getManagedWorlds().contains(worldName)) {
config.removeManagedWorld(worldName);
PortalRegistry.clearPortals(world);
}
}
}

View File

@@ -1,16 +1,17 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner; import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.PortalStrings;
import net.knarcraft.stargate.portal.property.PortalStructure; import net.knarcraft.stargate.portal.property.PortalStructure;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.World; import org.bukkit.block.Block;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map; import java.util.Map;
@@ -39,22 +40,21 @@ public class Portal {
* *
* @param portalLocation <p>Object containing locations of all relevant blocks</p> * @param portalLocation <p>Object containing locations of all relevant blocks</p>
* @param button <p>The location of the portal's open button</p> * @param button <p>The location of the portal's open button</p>
* @param destination <p>The destination defined on the sign's destination line. "" for non-fixed gates</p> * @param portalStrings <p>The portal's string values, such as name, network and destination</p>
* @param name <p>The name of the portal defined on the sign's first line</p>
* @param network <p>The network the portal belongs to, defined on the sign's third</p>
* @param gate <p>The gate type to use for this portal</p> * @param gate <p>The gate type to use for this portal</p>
* @param portalOwner <p>The portal's owner</p> * @param portalOwner <p>The portal's owner</p>
* @param options <p>A map containing all possible portal options, with true for the ones enabled</p> * @param options <p>A map containing all possible portal options, with true for the ones enabled</p>
*/ */
public Portal(PortalLocation portalLocation, BlockLocation button, String destination, String name, String network, public Portal(@NotNull PortalLocation portalLocation, @Nullable Block button,
Gate gate, PortalOwner portalOwner, Map<PortalOption, Boolean> options) { @NotNull PortalStrings portalStrings, @NotNull Gate gate, @NotNull PortalOwner portalOwner,
@NotNull Map<PortalOption, Boolean> options) {
this.location = portalLocation; this.location = portalLocation;
this.network = network; this.network = portalStrings.network();
this.name = name; this.name = portalStrings.name();
this.portalOwner = portalOwner; this.portalOwner = portalOwner;
this.options = new PortalOptions(options, destination.length() > 0); this.options = new PortalOptions(options, !portalStrings.destination().isEmpty());
this.signDrawer = new PortalSignDrawer(this); this.signDrawer = new PortalSignDrawer(this);
this.portalOpener = new PortalOpener(this, destination); this.portalOpener = new PortalOpener(this, portalStrings.destination());
this.structure = new PortalStructure(this, gate, button); this.structure = new PortalStructure(this, gate, button);
this.portalActivator = portalOpener.getPortalActivator(); this.portalActivator = portalOpener.getPortalActivator();
this.cleanName = cleanString(name); this.cleanName = cleanString(name);
@@ -84,6 +84,7 @@ public class Portal {
* *
* @return <p>This portal's location data</p> * @return <p>This portal's location data</p>
*/ */
@NotNull
public PortalLocation getLocation() { public PortalLocation getLocation() {
return this.location; return this.location;
} }
@@ -96,6 +97,7 @@ public class Portal {
* *
* @return <p>This portal's structure</p> * @return <p>This portal's structure</p>
*/ */
@NotNull
public PortalStructure getStructure() { public PortalStructure getStructure() {
return this.structure; return this.structure;
} }
@@ -108,6 +110,7 @@ public class Portal {
* *
* @return <p>This portal's activator</p> * @return <p>This portal's activator</p>
*/ */
@NotNull
public PortalActivator getPortalActivator() { public PortalActivator getPortalActivator() {
return this.portalActivator; return this.portalActivator;
} }
@@ -124,6 +127,7 @@ public class Portal {
* *
* @return <p>This portal's portal options</p> * @return <p>This portal's portal options</p>
*/ */
@NotNull
public PortalOptions getOptions() { public PortalOptions getOptions() {
return this.options; return this.options;
} }
@@ -142,6 +146,7 @@ public class Portal {
* *
* @return <p>The player currently using this portal</p> * @return <p>The player currently using this portal</p>
*/ */
@Nullable
public Player getActivePlayer() { public Player getActivePlayer() {
return portalActivator.getActivePlayer(); return portalActivator.getActivePlayer();
} }
@@ -151,6 +156,7 @@ public class Portal {
* *
* @return <p>The network this portal belongs to</p> * @return <p>The network this portal belongs to</p>
*/ */
@NotNull
public String getNetwork() { public String getNetwork() {
return network; return network;
} }
@@ -160,6 +166,7 @@ public class Portal {
* *
* @return <p>The clean network name</p> * @return <p>The clean network name</p>
*/ */
@NotNull
public String getCleanNetwork() { public String getCleanNetwork() {
return cleanNetwork; return cleanNetwork;
} }
@@ -181,6 +188,7 @@ public class Portal {
* *
* @return <p>The name of this portal</p> * @return <p>The name of this portal</p>
*/ */
@NotNull
public String getName() { public String getName() {
return name; return name;
} }
@@ -190,6 +198,7 @@ public class Portal {
* *
* @return <p>The clean name of this portal</p> * @return <p>The clean name of this portal</p>
*/ */
@NotNull
public String getCleanName() { public String getCleanName() {
return cleanName; return cleanName;
} }
@@ -201,6 +210,7 @@ public class Portal {
* *
* @return <p>This portal's portal opener</p> * @return <p>This portal's portal opener</p>
*/ */
@NotNull
public PortalOpener getPortalOpener() { public PortalOpener getPortalOpener() {
return portalOpener; return portalOpener;
} }
@@ -210,6 +220,7 @@ public class Portal {
* *
* @return <p>The name of this portal's destination portal</p> * @return <p>The name of this portal's destination portal</p>
*/ */
@NotNull
public String getDestinationName() { public String getDestinationName() {
return portalOpener.getPortalActivator().getDestinationName(); return portalOpener.getPortalActivator().getDestinationName();
} }
@@ -219,6 +230,7 @@ public class Portal {
* *
* @return <p>The gate type used by this portal</p> * @return <p>The gate type used by this portal</p>
*/ */
@NotNull
public Gate getGate() { public Gate getGate() {
return structure.getGate(); return structure.getGate();
} }
@@ -230,6 +242,7 @@ public class Portal {
* *
* @return <p>This portal's owner</p> * @return <p>This portal's owner</p>
*/ */
@NotNull
public PortalOwner getOwner() { public PortalOwner getOwner() {
return portalOwner; return portalOwner;
} }
@@ -240,7 +253,7 @@ public class Portal {
* @param player <p>The player to check</p> * @param player <p>The player to check</p>
* @return <p>True if the player is the owner of this portal</p> * @return <p>True if the player is the owner of this portal</p>
*/ */
public boolean isOwner(Player player) { public boolean isOwner(@NotNull Player player) {
if (this.portalOwner.getUUID() != null) { if (this.portalOwner.getUUID() != null) {
return player.getUniqueId().compareTo(this.portalOwner.getUUID()) == 0; return player.getUniqueId().compareTo(this.portalOwner.getUUID()) == 0;
} else { } else {
@@ -248,68 +261,22 @@ public class Portal {
} }
} }
/**
* Gets the world this portal belongs to
*
* @return <p>The world this portal belongs to</p>
*/
public World getWorld() {
return location.getWorld();
}
/**
* Gets the location of this portal's sign
*
* @return <p>The location of this portal's sign</p>
*/
public BlockLocation getSignLocation() {
return this.location.getSignLocation();
}
/**
* Gets the rotation (yaw) of this portal
*
* <p>The yaw is used to calculate all kinds of directions. See DirectionHelper to see how the yaw is used to
* calculate to/from other direction types.</p>
*
* @return <p>The rotation (yaw) of this portal</p>
*/
public float getYaw() {
return this.location.getYaw();
}
/**
* Gets the location of the top-left block of the portal
*
* @return <p>The location of the top-left portal block</p>
*/
public BlockLocation getTopLeft() {
return this.location.getTopLeft();
}
/**
* Gets the block at the given location relative to this portal's top-left block
*
* @param vector <p>The relative block vector explaining the position of the block</p>
* @return <p>The block at the given relative position</p>
*/
public BlockLocation getBlockAt(RelativeBlockVector vector) {
return getTopLeft().getRelativeLocation(vector, getYaw());
}
/** /**
* Cleans a string by removing color codes, lower-casing and replacing spaces with underscores * Cleans a string by removing color codes, lower-casing and replacing spaces with underscores
* *
* @param string <p>The string to clean</p> * @param string <p>The string to clean</p>
* @return <p>The clean string</p> * @return <p>The clean string</p>
*/ */
public static String cleanString(String string) { @NotNull
public static String cleanString(@NotNull String string) {
// TODO: Replace special characters such as : and .
return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)).toLowerCase(); return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)).toLowerCase();
} }
@Override @Override
@NotNull
public String toString() { public String toString() {
return String.format("Portal [id=%s, network=%s name=%s, type=%s]", getSignLocation(), network, name, return String.format("Portal [id=%s, network=%s name=%s, type=%s]", this.location.getSignBlock(), network, name,
structure.getGate().getFilename()); structure.getGate().getFilename());
} }
@@ -323,7 +290,7 @@ public class Portal {
} }
@Override @Override
public boolean equals(Object object) { public boolean equals(@Nullable Object object) {
if (this == object) { if (this == object) {
return true; return true;
} }
@@ -345,4 +312,5 @@ public class Portal {
return cleanNetwork.equalsIgnoreCase(other.cleanNetwork); return cleanNetwork.equalsIgnoreCase(other.cleanNetwork);
} }
} }
} }

View File

@@ -1,14 +1,19 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.event.StargateActivateEvent; import net.knarcraft.stargate.event.StargateActivateEvent;
import net.knarcraft.stargate.event.StargateDeactivateEvent; import net.knarcraft.stargate.event.StargateDeactivateEvent;
import net.knarcraft.stargate.utility.ListHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Random;
/** /**
* The portal activator activates/de-activates portals and keeps track of a portal's destinations * The portal activator activates/de-activates portals and keeps track of a portal's destinations
@@ -33,7 +38,7 @@ public class PortalActivator {
* @param portalOpener <p>The portal opener to trigger when the activation causes the portal to open</p> * @param portalOpener <p>The portal opener to trigger when the activation causes the portal to open</p>
* @param destination <p>The fixed destination specified on the portal's sign</p> * @param destination <p>The fixed destination specified on the portal's sign</p>
*/ */
public PortalActivator(Portal portal, PortalOpener portalOpener, String destination) { public PortalActivator(@NotNull Portal portal, @NotNull PortalOpener portalOpener, @NotNull String destination) {
this.portal = portal; this.portal = portal;
this.opener = portalOpener; this.opener = portalOpener;
this.destination = destination; this.destination = destination;
@@ -44,6 +49,7 @@ public class PortalActivator {
* *
* @return <p>The player this activator's portal is currently activated for</p> * @return <p>The player this activator's portal is currently activated for</p>
*/ */
@NotNull
public Player getActivePlayer() { public Player getActivePlayer() {
return activePlayer; return activePlayer;
} }
@@ -53,6 +59,7 @@ public class PortalActivator {
* *
* @return <p>The available portal destinations</p> * @return <p>The available portal destinations</p>
*/ */
@NotNull
public List<String> getDestinations() { public List<String> getDestinations() {
return new ArrayList<>(this.destinations); return new ArrayList<>(this.destinations);
} }
@@ -63,20 +70,21 @@ public class PortalActivator {
* @param player <p>Used for random gates to determine which destinations are available</p> * @param player <p>Used for random gates to determine which destinations are available</p>
* @return <p>The destination portal the player should teleport to</p> * @return <p>The destination portal the player should teleport to</p>
*/ */
public Portal getDestination(Player player) { @Nullable
public Portal getDestination(@Nullable Player player) {
String portalNetwork = portal.getCleanNetwork(); String portalNetwork = portal.getCleanNetwork();
if (portal.getOptions().isRandom()) { if (portal.getOptions().isRandom()) {
//Find possible destinations //Find possible destinations
List<String> destinations = PortalHandler.getDestinations(portal, player, portalNetwork); List<String> destinations = PortalUtil.getDestinations(portal, player, portalNetwork);
if (destinations.size() == 0) { if (destinations.isEmpty()) {
return null; return null;
} }
//Get one random destination //Get one random destination
String destination = destinations.get((new Random()).nextInt(destinations.size())); String randomDestination = ListHelper.getRandom(destinations);
return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork); return PortalUtil.getByName(randomDestination, portalNetwork);
} else { } else {
//Just return the normal fixed destination //Just return the normal fixed destination
return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork); return PortalUtil.getByName(destination, portalNetwork);
} }
} }
@@ -88,6 +96,7 @@ public class PortalActivator {
* *
* @return <p>The portal destination</p> * @return <p>The portal destination</p>
*/ */
@Nullable
public Portal getDestination() { public Portal getDestination() {
return getDestination(null); return getDestination(null);
} }
@@ -97,7 +106,7 @@ public class PortalActivator {
* *
* @param destination <p>The new destination of this portal activator's portal</p> * @param destination <p>The new destination of this portal activator's portal</p>
*/ */
public void setDestination(Portal destination) { public void setDestination(@NotNull Portal destination) {
setDestination(destination.getName()); setDestination(destination.getName());
} }
@@ -106,7 +115,7 @@ public class PortalActivator {
* *
* @param destination <p>The new destination of this portal activator's portal</p> * @param destination <p>The new destination of this portal activator's portal</p>
*/ */
public void setDestination(String destination) { public void setDestination(@NotNull String destination) {
this.destination = destination; this.destination = destination;
} }
@@ -115,6 +124,7 @@ public class PortalActivator {
* *
* @return <p>The name of the selected destination</p> * @return <p>The name of the selected destination</p>
*/ */
@NotNull
public String getDestinationName() { public String getDestinationName() {
return destination; return destination;
} }
@@ -125,7 +135,7 @@ public class PortalActivator {
* @param player <p>The player to activate the portal for</p> * @param player <p>The player to activate the portal for</p>
* @return <p>True if the portal was activated</p> * @return <p>True if the portal was activated</p>
*/ */
boolean activate(Player player) { public boolean activate(@NotNull Player player) {
//Clear previous destination data //Clear previous destination data
this.destination = ""; this.destination = "";
this.destinations.clear(); this.destinations.clear();
@@ -137,7 +147,7 @@ public class PortalActivator {
activePlayer = player; activePlayer = player;
String network = portal.getCleanNetwork(); String network = portal.getCleanNetwork();
destinations = PortalHandler.getDestinations(portal, player, network); destinations = PortalUtil.getDestinations(portal, player, network);
//Sort destinations if enabled //Sort destinations if enabled
if (Stargate.getGateConfig().sortNetworkDestinations()) { if (Stargate.getGateConfig().sortNetworkDestinations()) {
@@ -162,7 +172,7 @@ public class PortalActivator {
* @param player <p>The player trying to activate this activator's portal</p> * @param player <p>The player trying to activate this activator's portal</p>
* @return <p>True if the portal was activated. False otherwise</p> * @return <p>True if the portal was activated. False otherwise</p>
*/ */
private boolean triggerStargateActivationEvent(Player player) { private boolean triggerStargateActivationEvent(@NotNull Player player) {
StargateActivateEvent event = new StargateActivateEvent(portal, player, destinations, destination); StargateActivateEvent event = new StargateActivateEvent(portal, player, destinations, destination);
Stargate.getInstance().getServer().getPluginManager().callEvent(event); Stargate.getInstance().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) { if (event.isCancelled()) {
@@ -209,7 +219,7 @@ public class PortalActivator {
* @return <p>Whether this portal activator's portal is active</p> * @return <p>Whether this portal activator's portal is active</p>
*/ */
public boolean isActive() { public boolean isActive() {
return portal.getOptions().isFixed() || (destinations.size() > 0); return portal.getOptions().isFixed() || (!destinations.isEmpty());
} }
/** /**
@@ -217,7 +227,7 @@ public class PortalActivator {
* *
* @param player <p>The player to cycle the gate for</p> * @param player <p>The player to cycle the gate for</p>
*/ */
public void cycleDestination(Player player) { public void cycleDestination(@NotNull Player player) {
cycleDestination(player, 1); cycleDestination(player, 1);
} }
@@ -227,7 +237,7 @@ public class PortalActivator {
* @param player <p>The player cycling destinations</p> * @param player <p>The player cycling destinations</p>
* @param direction <p>The direction of the cycle (+1 for next, -1 for previous)</p> * @param direction <p>The direction of the cycle (+1 for next, -1 for previous)</p>
*/ */
public void cycleDestination(Player player, int direction) { public void cycleDestination(@NotNull Player player, int direction) {
//Only allow going exactly one step in either direction //Only allow going exactly one step in either direction
if (direction != 1 && direction != -1) { if (direction != 1 && direction != -1) {
throw new IllegalArgumentException("The destination direction must be 1 or -1."); throw new IllegalArgumentException("The destination direction must be 1 or -1.");
@@ -241,15 +251,17 @@ public class PortalActivator {
} }
activate = true; activate = true;
Stargate.debug("cycleDestination", "Network Size: " + List<String> portalsInNetwork = PortalUtil.getNetwork(portal.getCleanNetwork());
PortalHandler.getNetwork(portal.getCleanNetwork()).size()); if (portalsInNetwork != null) {
Stargate.debug("cycleDestination", "Network Size: " + portalsInNetwork.size());
}
Stargate.debug("cycleDestination", "Player has access to: " + destinations.size()); Stargate.debug("cycleDestination", "Player has access to: " + destinations.size());
} }
//If no destinations are available, just tell the player and quit //If no destinations are available, just tell the player and quit
if (destinations.size() == 0) { if (destinations.isEmpty()) {
if (!portal.getOptions().isSilent()) { if (!portal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("destEmpty")); new SGFormatBuilder(Message.NO_DESTINATION).error(player);
} }
return; return;
} }

View File

@@ -1,23 +1,31 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.event.StargateCreateEvent; import net.knarcraft.stargate.event.StargateCreateEvent;
import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner; import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.PortalStrings;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler; import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EconomyHelper; import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.ListHelper;
import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalFileHelper; import net.knarcraft.stargate.utility.PortalFileHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -39,7 +47,7 @@ public class PortalCreator {
* @param event <p>The sign change event which initialized the creation</p> * @param event <p>The sign change event which initialized the creation</p>
* @param player <p>The player creating the portal</p> * @param player <p>The player creating the portal</p>
*/ */
public PortalCreator(SignChangeEvent event, Player player) { public PortalCreator(@NotNull SignChangeEvent event, @NotNull Player player) {
this.event = event; this.event = event;
this.player = player; this.player = player;
} }
@@ -49,53 +57,66 @@ public class PortalCreator {
* *
* @return <p>The created portal</p> * @return <p>The created portal</p>
*/ */
@Nullable
public Portal createPortal() { public Portal createPortal() {
BlockLocation signLocation = new BlockLocation(event.getBlock()); String route = "PortalCreator::createPortal";
Block signControlBlock = signLocation.getParent(); Block signLocation = event.getBlock();
Block signControlBlock = DirectionHelper.getParent(signLocation);
//Return early if the sign is not placed on a block, or the block is not a control block //Return early if the sign is not placed on a block, or the block is not a control block
if (signControlBlock == null || GateHandler.getGatesByControlBlock(signControlBlock).length == 0) { if (signControlBlock == null || GateHandler.getGatesByControlBlock(signControlBlock).isEmpty()) {
Stargate.debug("createPortal", "Control block not registered"); Stargate.debug(route, "Control block not registered");
return null; return null;
} }
//The control block is already part of another portal //The control block is already part of another portal
if (PortalHandler.getByBlock(signControlBlock) != null) { if (PortalUtil.getByBlock(signControlBlock) != null) {
Stargate.debug("createPortal", "idParent belongs to existing stargate"); Stargate.debug(route, "idParent belongs to existing stargate");
return null; return null;
} }
//Get necessary information from the gate's sign //Get necessary information from the gate's sign
String portalName = PortalHandler.filterName(event.getLine(0)); @NotNull String portalName = PortalUtil.filterName(event.getLine(0));
String destinationName = PortalHandler.filterName(event.getLine(1)); @NotNull String destinationName = PortalUtil.filterName(event.getLine(1));
String network = PortalHandler.filterName(event.getLine(2)); @NotNull String network = PortalUtil.filterName(event.getLine(2));
String options = PortalHandler.filterName(event.getLine(3)).toLowerCase(); @NotNull String options = PortalUtil.filterName(event.getLine(3)).toLowerCase();
PortalStrings portalStrings = new PortalStrings(portalName, network, destinationName);
//Get portal options available to the player creating the portal //Get portal options available to the player creating the portal
Map<PortalOption, Boolean> portalOptions = PortalHandler.getPortalOptions(player, destinationName, options); Map<PortalOption, Boolean> portalOptions = PortalUtil.getPortalOptions(player, destinationName, options);
//Get the yaw //Get the yaw
float yaw = DirectionHelper.getYawFromLocationDifference(signControlBlock.getLocation(),
signLocation.getLocation());
//Get the direction the button should be facing
BlockFace buttonFacing = DirectionHelper.getBlockFaceFromYaw(yaw);
PortalLocation portalLocation = new PortalLocation(); BlockFace facing = DirectionHelper.getFacing(signLocation);
portalLocation.setButtonFacing(buttonFacing).setYaw(yaw).setSignLocation(signLocation); if (facing == null) {
facing = DirectionHelper.getBlockFaceFromLocationDifference(signControlBlock.getLocation(),
signLocation.getLocation());
}
Stargate.debug("createPortal", "Finished getting all portal info"); PortalLocation portalLocation = new PortalLocation(signLocation, facing);
Stargate.debug(route, "Finished getting all portal info");
return createPortal(portalStrings, portalOptions, portalLocation);
}
@Nullable
private Portal createPortal(@NotNull PortalStrings portalStrings, @NotNull Map<PortalOption, Boolean> portalOptions,
@NotNull PortalLocation portalLocation) {
String route = "PortalCreator::createPortal";
//Try and find a gate matching the new portal //Try and find a gate matching the new portal
Gate gate = PortalHandler.findMatchingGate(portalLocation, player.getWorld()); Gate gate = PortalUtil.findMatchingGate(portalLocation);
if ((gate == null) || (portalLocation.getButtonVector() == null)) { if ((gate == null) || (portalLocation.getButtonBlock() == null)) {
Stargate.debug("createPortal", "Could not find matching gate layout"); Stargate.debug(route, "Could not find matching gate layout");
return null; return null;
} }
//If the portal is a bungee portal and invalid, abort here //If the portal is a bungee portal and invalid, abort here
if (!PortalHandler.isValidBungeePortal(portalOptions, player, destinationName, network)) { if (!PortalUtil.isValidBungeePortal(portalOptions, player, portalStrings.destination(),
Stargate.debug("createPortal", "Portal is an invalid bungee portal"); portalStrings.network())) {
Stargate.debug(route, "Portal is an invalid bungee portal");
return null; return null;
} }
@@ -104,65 +125,102 @@ public class PortalCreator {
for (PortalOption option : portalOptions.keySet()) { for (PortalOption option : portalOptions.keySet()) {
builder.append(option.getCharacterRepresentation()).append(" = ").append(portalOptions.get(option)).append(" "); builder.append(option.getCharacterRepresentation()).append(" = ").append(portalOptions.get(option)).append(" ");
} }
Stargate.debug("createPortal", builder.toString()); Stargate.debug(route, builder.toString());
//Use default network if a proper alternative is not set
if (!portalOptions.get(PortalOption.BUNGEE) && (network.length() < 1 || network.length() >
getMaxNameNetworkLength())) {
network = Stargate.getDefaultNetwork();
}
boolean deny = false; boolean deny = false;
String denyMessage = ""; String denyMessage = "";
if (!(boolean) portalOptions.get(PortalOption.BUNGEE)) {
String networkName = getNetworkName(portalStrings);
if (networkName == null) {
deny = true;
denyMessage = new SGFormatBuilder(Message.CREATION_NETWORK_DENIED).toString();
} else {
portalStrings = new PortalStrings(portalStrings.name(), networkName, portalStrings.destination());
}
}
// Check whether the player can create a portal with the specified gate in the specified world
if (!deny) {
denyMessage = canCreatePortal(portalOptions.get(PortalOption.BUNGEE), portalStrings.network(), gate,
portalStrings.destination());
if (denyMessage != null) {
deny = true;
} else {
denyMessage = "";
}
}
//Check if a conflict exists
if (conflictsWithExistingPortal(gate, portalLocation, player)) {
return null;
}
PortalOwner owner = new PortalOwner(player);
this.portal = new Portal(portalLocation, null, portalStrings, gate, owner, portalOptions);
return validatePortal(denyMessage, event.getLines(), deny);
}
/**
* Gets the network name to use for the new portal
*
* @param portalStrings <p>The string values for the new portal</p>
* @return <p>The new network name, or null if the player does not have the necessary permission for any networks</p>
*/
@Nullable
private String getNetworkName(@NotNull PortalStrings portalStrings) {
String network = portalStrings.network();
String route = "PortalCreator::getNetworkName";
//Use default network if a proper alternative is not set
if (portalStrings.network().isEmpty() || portalStrings.network().length() > getMaxNameNetworkLength()) {
network = Stargate.getDefaultNetwork();
}
//Check if the player can create portals on this network. If not, create a personal portal //Check if the player can create portals on this network. If not, create a personal portal
if (!portalOptions.get(PortalOption.BUNGEE) && !PermissionHelper.canCreateNetworkGate(player, network)) { if (!PermissionHelper.canCreateNetworkGate(player, network)) {
Stargate.debug("createPortal", "Player doesn't have create permissions on network. Trying personal"); Stargate.debug(route, "Player doesn't have create permissions on network. Trying personal");
if (PermissionHelper.canCreatePersonalPortal(player)) { if (PermissionHelper.canCreatePersonalPortal(player)) {
network = player.getName(); network = player.getName();
if (network.length() > getMaxNameNetworkLength()) { if (network.length() > getMaxNameNetworkLength()) {
network = network.substring(0, getMaxNameNetworkLength()); network = network.substring(0, getMaxNameNetworkLength());
} }
Stargate.debug("createPortal", "Creating personal portal"); Stargate.debug(route, "Creating personal portal");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createPersonal")); new SGFormatBuilder(Message.CREATION_PERSONAL).error(player);
return network;
} else { } else {
Stargate.debug("createPortal", "Player does not have access to network"); Stargate.debug(route, "Player does not have access to network");
deny = true; return null;
denyMessage = Stargate.getString("createNetDeny");
} }
} }
return network;
}
@Nullable
private String canCreatePortal(boolean bungee, @NotNull String network,
@NotNull Gate gate, @NotNull String destinationName) {
//Check if the player can create this gate layout //Check if the player can create this gate layout
String gateName = gate.getFilename(); String gateName = gate.getFilename();
gateName = gateName.substring(0, gateName.indexOf('.')); gateName = gateName.substring(0, gateName.indexOf('.'));
if (!deny && !PermissionHelper.canCreatePortal(player, gateName)) { if (!PermissionHelper.canCreatePortal(player, gateName)) {
Stargate.debug("createPortal", "Player does not have access to gate layout"); Stargate.debug("PortalCreator::canCreatePortal", "Player does not have access to gate layout");
deny = true; return new SGFormatBuilder(Message.CREATION_GATE_DENIED).toString();
denyMessage = Stargate.getString("createGateDeny");
} }
//Check if the user can create portals to this world. //Check if the user can create portals to this world.
if (!portalOptions.get(PortalOption.BUNGEE) && !deny && destinationName.length() > 0) { if (!bungee && !destinationName.isEmpty()) {
Portal portal = PortalHandler.getByName(destinationName, network); Portal destinationPortal = PortalUtil.getByName(destinationName, network);
if (portal != null) { if (destinationPortal != null) {
String world = portal.getWorld().getName(); String world = destinationPortal.getLocation().getWorld().getName();
if (PermissionHelper.cannotAccessWorld(player, world)) { if (PermissionHelper.cannotAccessWorld(player, world)) {
Stargate.debug("canCreateNetworkGate", "Player does not have access to destination world"); Stargate.debug("PortalCreator::canCreatePortal", "Player does not have access to destination world");
deny = true; return new SGFormatBuilder(Message.CREATION_WORLD_DENIED).toString();
denyMessage = Stargate.getString("createWorldDeny");
} }
} }
} }
//Check if a conflict exists return null;
if (conflictsWithExistingPortal(gate, portalLocation.getTopLeft(), yaw, player)) {
return null;
}
PortalOwner owner = new PortalOwner(player);
this.portal = new Portal(portalLocation, null, destinationName, portalName, network, gate, owner,
portalOptions);
return validatePortal(denyMessage, event.getLines(), deny);
} }
/** /**
@@ -173,7 +231,8 @@ public class PortalCreator {
* @param deny <p>Whether the portal creation has already been denied</p> * @param deny <p>Whether the portal creation has already been denied</p>
* @return <p>The portal or null if its creation was denied</p> * @return <p>The portal or null if its creation was denied</p>
*/ */
public Portal validatePortal(String denyMessage, String[] lines, boolean deny) { @Nullable
public Portal validatePortal(@NotNull String denyMessage, @NotNull String[] lines, boolean deny) {
PortalLocation portalLocation = portal.getLocation(); PortalLocation portalLocation = portal.getLocation();
Gate gate = portal.getStructure().getGate(); Gate gate = portal.getStructure().getGate();
PortalOptions portalOptions = portal.getOptions(); PortalOptions portalOptions = portal.getOptions();
@@ -193,7 +252,7 @@ public class PortalCreator {
//Tell the user why it was denied from creating the portal //Tell the user why it was denied from creating the portal
if (stargateCreateEvent.getDeny()) { if (stargateCreateEvent.getDeny()) {
if (!stargateCreateEvent.getDenyReason().trim().isEmpty()) { if (!stargateCreateEvent.getDenyReason().trim().isEmpty()) {
Stargate.getMessageSender().sendErrorMessage(player, stargateCreateEvent.getDenyReason()); new SGFormatBuilder(stargateCreateEvent.getDenyReason()).error(player);
} }
return null; return null;
} }
@@ -207,19 +266,19 @@ public class PortalCreator {
//Add button if the portal is not always on //Add button if the portal is not always on
if (!portalOptions.isAlwaysOn()) { if (!portalOptions.isAlwaysOn()) {
PortalFileHelper.generatePortalButton(portal, portalLocation.getButtonFacing()); PortalFileHelper.generatePortalButton(portal, portalLocation.getFacing());
} }
//Register the new portal //Register the new portal
PortalHandler.registerPortal(portal); PortalUtil.registerPortal(portal);
updateNewPortalOpenState(destinationName); updateNewPortalOpenState(destinationName);
//Update portals pointing at this one if it's not a bungee portal //Update portals pointing at this one if it's not a bungee portal
if (!portal.getOptions().isBungee()) { if (!portal.getOptions().isBungee()) {
PortalHandler.updatePortalsPointingAtNewPortal(portal); PortalUtil.updatePortalsPointingAtNewPortal(portal);
} }
PortalFileHelper.saveAllPortals(portal.getWorld()); PortalFileHelper.saveAllPortals(portal.getLocation().getWorld());
return portal; return portal;
} }
@@ -231,35 +290,37 @@ public class PortalCreator {
* @param portalName <p>The name of the newly created portal</p> * @param portalName <p>The name of the newly created portal</p>
* @return <p>True if the portal is completely valid</p> * @return <p>True if the portal is completely valid</p>
*/ */
private boolean checkIfNewPortalIsValid(int cost, String portalName) { private boolean checkIfNewPortalIsValid(int cost, @NotNull String portalName) {
String route = "PortalCreator::checkIfNewPortalIsValid";
//Check if the portal name can fit on the sign with padding (>name<) //Check if the portal name can fit on the sign with padding (>name<)
if (portal.getCleanName().length() < 1 || portal.getCleanName().length() > getMaxNameNetworkLength()) { if (portal.getCleanName().isEmpty() || portal.getCleanName().length() > getMaxNameNetworkLength()) {
Stargate.debug("createPortal", String.format("Name length error. %s is too long.", Stargate.debug(route, String.format("Name length error. %s is too long.",
portal.getCleanName())); portal.getCleanName()));
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createNameLength")); new SGFormatBuilder(Message.CREATION_NAME_LENGTH).error(player);
return false; return false;
} }
if (portal.getOptions().isBungee()) { if (portal.getOptions().isBungee()) {
//Check if the bungee portal's name has been duplicated //Check if the bungee portal's name has been duplicated
if (PortalHandler.getBungeePortals().get(portal.getCleanName()) != null) { if (PortalRegistry.getBungeePortal(portal.getCleanName()) != null) {
Stargate.debug("createPortal::Bungee", "Gate name duplicate"); Stargate.debug(route, "Gate name duplicate");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists")); new SGFormatBuilder(Message.CREATION_NAME_COLLISION).error(player);
return false; return false;
} }
} else { } else {
//Check if the portal name has been duplicated on the network //Check if the portal name has been duplicated on the network
if (PortalHandler.getByName(portal.getCleanName(), portal.getCleanNetwork()) != null) { if (PortalUtil.getByName(portal.getCleanName(), portal.getCleanNetwork()) != null) {
Stargate.debug("createPortal", "Gate name duplicate"); Stargate.debug(route, "Gate name duplicate");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists")); new SGFormatBuilder(Message.CREATION_NAME_COLLISION).error(player);
return false; return false;
} }
//Check if the number of portals in the network has been surpassed //Check if the number of portals in the network has been surpassed
List<String> networkList = PortalHandler.getAllPortalNetworks().get(portal.getCleanNetwork()); List<String> networkList = PortalUtil.getNetwork(portal.getCleanNetwork());
int maxGates = Stargate.getGateConfig().maxGatesEachNetwork(); int maxGates = Stargate.getGateConfig().maxGatesEachNetwork();
if (maxGates > 0 && networkList != null && networkList.size() >= maxGates) { if (maxGates > 0 && networkList != null && networkList.size() >= maxGates) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createFull")); new SGFormatBuilder(Message.CREATION_NETWORK_FULL).error(player);
return false; return false;
} }
} }
@@ -268,7 +329,7 @@ public class PortalCreator {
//Deduct the required fee from the player //Deduct the required fee from the player
if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) { if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) {
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost); EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
Stargate.debug("createPortal", "Insufficient Funds"); Stargate.debug(route, "Insufficient Funds");
return false; return false;
} else { } else {
EconomyHelper.sendDeductMessage(portalName, player, cost); EconomyHelper.sendDeductMessage(portalName, player, cost);
@@ -282,14 +343,14 @@ public class PortalCreator {
* *
* @param destinationName <p>The name of the destination portal. Only used if set as always on</p> * @param destinationName <p>The name of the destination portal. Only used if set as always on</p>
*/ */
private void updateNewPortalOpenState(String destinationName) { private void updateNewPortalOpenState(@NotNull String destinationName) {
portal.drawSign(); portal.drawSign();
if (portal.getOptions().isRandom() || portal.getOptions().isBungee()) { if (portal.getOptions().isRandom() || portal.getOptions().isBungee()) {
//Open the implicitly always on portal //Open the implicitly always on portal
portal.getPortalOpener().openPortal(true); portal.getPortalOpener().openPortal(true);
} else if (portal.getOptions().isAlwaysOn()) { } else if (portal.getOptions().isAlwaysOn()) {
//For a normal always-on portal, open both the portal and the destination //For a normal always-on portal, open both the portal and the destination
Portal destinationPortal = PortalHandler.getByName(destinationName, portal.getCleanNetwork()); Portal destinationPortal = PortalUtil.getByName(destinationName, portal.getCleanNetwork());
if (destinationPortal != null) { if (destinationPortal != null) {
portal.getPortalOpener().openPortal(true); portal.getPortalOpener().openPortal(true);
destinationPortal.drawSign(); destinationPortal.drawSign();
@@ -297,8 +358,12 @@ public class PortalCreator {
} else { } else {
//Update the block type for the portal's opening to the closed block as the closed block can be anything, //Update the block type for the portal's opening to the closed block as the closed block can be anything,
// not just air or water // not just air or water
for (BlockLocation entrance : portal.getStructure().getEntrances()) { @NotNull List<Material> possibleMaterials = MaterialHelper.specifiersToMaterials(
entrance.setType(portal.getGate().getPortalClosedBlock()); portal.getGate().getPortalClosedMaterials()).stream().toList();
Material closedType = ListHelper.getRandom(possibleMaterials);
for (Block entrance : portal.getStructure().getEntrances()) {
entrance.setType(closedType);
} }
} }
} }
@@ -306,18 +371,19 @@ public class PortalCreator {
/** /**
* Checks whether the new portal conflicts with an existing portal * Checks whether the new portal conflicts with an existing portal
* *
* @param gate <p>The gate type of the new portal</p> * @param gate <p>The gate type of the new portal</p>
* @param topLeft <p>The top-left block of the new portal</p> * @param portalLocation <p>The location of the portal to check</p>
* @param yaw <p>The yaw when looking directly outwards from the portal</p> * @param player <p>The player creating the new portal</p>
* @param player <p>The player creating the new portal</p>
* @return <p>True if a conflict was found. False otherwise</p> * @return <p>True if a conflict was found. False otherwise</p>
*/ */
private static boolean conflictsWithExistingPortal(Gate gate, BlockLocation topLeft, double yaw, Player player) { private static boolean conflictsWithExistingPortal(@NotNull Gate gate, @NotNull PortalLocation portalLocation,
for (RelativeBlockVector borderVector : gate.getLayout().getBorder()) { @NotNull Player player) {
BlockLocation borderBlockLocation = topLeft.getRelativeLocation(borderVector, yaw); for (BlockVector borderVector : gate.getLayout().getBorder()) {
if (PortalHandler.getByBlock(borderBlockLocation.getBlock()) != null) { Block borderBlockLocation = portalLocation.getRelative(borderVector);
Stargate.debug("createPortal", "Gate conflicts with existing gate"); if (PortalUtil.getByBlock(borderBlockLocation) != null) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createConflict")); Stargate.debug("PortalCreator::conflictsWithExistingPortal",
"Gate conflicts with existing gate");
new SGFormatBuilder(Message.CREATION_CONFLICT).error(player);
return true; return true;
} }
} }

View File

@@ -1,15 +1,23 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.material.BukkitTagSpecifier;
import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.event.StargateCloseEvent; import net.knarcraft.stargate.event.StargateCloseEvent;
import net.knarcraft.stargate.event.StargateOpenEvent; import net.knarcraft.stargate.event.StargateOpenEvent;
import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.utility.ListHelper;
import net.knarcraft.stargate.utility.MaterialHelper;
import org.bukkit.Axis; import org.bukkit.Axis;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.data.Orientable; import org.bukkit.block.data.Orientable;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/** /**
* The portal opener is responsible for opening and closing a portal * The portal opener is responsible for opening and closing a portal
@@ -28,7 +36,7 @@ public class PortalOpener {
* @param portal <p>The portal this portal opener should open</p> * @param portal <p>The portal this portal opener should open</p>
* @param destination <p>The fixed destination defined on the portal's sign</p> * @param destination <p>The fixed destination defined on the portal's sign</p>
*/ */
public PortalOpener(Portal portal, String destination) { public PortalOpener(@NotNull Portal portal, @NotNull String destination) {
this.portal = portal; this.portal = portal;
this.portalActivator = new PortalActivator(portal, this, destination); this.portalActivator = new PortalActivator(portal, this, destination);
} }
@@ -56,6 +64,7 @@ public class PortalOpener {
* *
* @return <p>The portal activator belonging to this portal opener</p> * @return <p>The portal activator belonging to this portal opener</p>
*/ */
@NotNull
public PortalActivator getPortalActivator() { public PortalActivator getPortalActivator() {
return this.portalActivator; return this.portalActivator;
} }
@@ -75,7 +84,7 @@ public class PortalOpener {
* @param openFor <p>The player to open the portal for</p> * @param openFor <p>The player to open the portal for</p>
* @param force <p>Whether to force the portal open, even if it's already open for some player</p> * @param force <p>Whether to force the portal open, even if it's already open for some player</p>
*/ */
public void openPortal(Player openFor, boolean force) { public void openPortal(@Nullable Player openFor, boolean force) {
//Call the StargateOpenEvent to allow the opening to be cancelled //Call the StargateOpenEvent to allow the opening to be cancelled
StargateOpenEvent event = new StargateOpenEvent(openFor, portal, force); StargateOpenEvent event = new StargateOpenEvent(openFor, portal, force);
Stargate.getInstance().getServer().getPluginManager().callEvent(event); Stargate.getInstance().getServer().getPluginManager().callEvent(event);
@@ -84,13 +93,16 @@ public class PortalOpener {
} }
//Get the material to change the opening to //Get the material to change the opening to
Material openType = portal.getGate().getPortalOpenBlock(); @NotNull List<Material> possibleMaterials = MaterialHelper.specifiersToMaterials(
portal.getGate().getPortalOpenMaterials()).stream().toList();
Material openType = ListHelper.getRandom(possibleMaterials);
//Adjust orientation if applicable //Adjust orientation if applicable
Axis axis = (openType.createBlockData() instanceof Orientable) ? portal.getLocation().getRotationAxis() : null; Axis axis = (openType.createBlockData() instanceof Orientable) ? portal.getLocation().getVectorOperation().getNormalAxis() : null;
//Change the entrance blocks to the correct type //Change the entrance blocks to the correct type
for (BlockLocation inside : portal.getStructure().getEntrances()) { for (Block inside : portal.getStructure().getEntrances()) {
Stargate.addBlockChangeRequest(new BlockChangeRequest(inside, openType, axis)); Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(inside, openType, axis));
} }
//Update the portal state to make is actually open //Update the portal state to make is actually open
@@ -102,7 +114,7 @@ public class PortalOpener {
* *
* @param openFor <p>The player to open this portal opener's portal for</p> * @param openFor <p>The player to open this portal opener's portal for</p>
*/ */
private void updatePortalOpenState(Player openFor) { private void updatePortalOpenState(@Nullable Player openFor) {
//Update the open state of this portal //Update the open state of this portal
isOpen = true; isOpen = true;
triggeredTime = System.currentTimeMillis() / 1000; triggeredTime = System.currentTimeMillis() / 1000;
@@ -134,8 +146,8 @@ public class PortalOpener {
//Set the destination portal to this opener's portal //Set the destination portal to this opener's portal
destination.getPortalActivator().setDestination(portal); destination.getPortalActivator().setDestination(portal);
//Update the destination's sign if it's verified //Update the destination's sign if it exists
if (destination.getStructure().isVerified()) { if (new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(destination.getLocation().getSignBlock().getType())) {
destination.drawSign(); destination.drawSign();
} }
} }
@@ -165,9 +177,15 @@ public class PortalOpener {
} }
//Close the portal by requesting the opening blocks to change //Close the portal by requesting the opening blocks to change
Material closedType = portal.getGate().getPortalClosedBlock(); @NotNull List<Material> possibleMaterials = MaterialHelper.specifiersToMaterials(
for (BlockLocation entrance : portal.getStructure().getEntrances()) { portal.getGate().getPortalClosedMaterials()).stream().toList();
Stargate.addBlockChangeRequest(new BlockChangeRequest(entrance, closedType, null)); Material closedType = ListHelper.getRandom(possibleMaterials);
//Adjust orientation if applicable
Axis axis = (closedType.createBlockData() instanceof Orientable) ? portal.getLocation().getVectorOperation().getNormalAxis() : null;
for (Block entrance : portal.getStructure().getEntrances()) {
Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(entrance, closedType, axis));
} }
//Update the portal state to make it actually closed //Update the portal state to make it actually closed
@@ -207,7 +225,7 @@ public class PortalOpener {
* @param player <p>The player to check portal state for</p> * @param player <p>The player to check portal state for</p>
* @return <p>True if this portal opener's portal is open to the given player</p> * @return <p>True if this portal opener's portal is open to the given player</p>
*/ */
public boolean isOpenFor(Player player) { public boolean isOpenFor(@Nullable Player player) {
//If closed, it's closed for everyone //If closed, it's closed for everyone
if (!isOpen) { if (!isOpen) {
return false; return false;

View File

@@ -1,9 +1,15 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.config.addons.DynmapManager;
import net.knarcraft.stargate.config.material.BukkitTagSpecifier;
import net.knarcraft.stargate.utility.PortalFileHelper; import net.knarcraft.stargate.utility.PortalFileHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import org.bukkit.Tag;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -15,9 +21,9 @@ import java.util.Map;
*/ */
public class PortalRegistry { public class PortalRegistry {
private static final Map<BlockLocation, Portal> lookupBlocks = new HashMap<>(); private static final Map<Block, Portal> lookupBlocks = new HashMap<>();
private static final Map<BlockLocation, Portal> lookupEntrances = new HashMap<>(); private static final Map<Block, Portal> lookupEntrances = new HashMap<>();
private static final Map<BlockLocation, Portal> lookupControls = new HashMap<>(); private static final Map<Block, Portal> lookupControls = new HashMap<>();
private static final Map<String, Map<String, Portal>> portalLookupByNetwork = new HashMap<>(); private static final Map<String, Map<String, Portal>> portalLookupByNetwork = new HashMap<>();
private static final Map<String, List<String>> allPortalNetworks = new HashMap<>(); private static final Map<String, List<String>> allPortalNetworks = new HashMap<>();
@@ -43,11 +49,11 @@ public class PortalRegistry {
* *
* @param world <p>The world containing the portals to clear</p> * @param world <p>The world containing the portals to clear</p>
*/ */
public static void clearPortals(World world) { public static void clearPortals(@NotNull World world) {
//Storing the portals to clear is necessary to avoid a concurrent modification exception //Storing the portals to clear is necessary to avoid a concurrent modification exception
List<Portal> portalsToRemove = new ArrayList<>(); List<Portal> portalsToRemove = new ArrayList<>();
allPortals.forEach((portal) -> { allPortals.forEach((portal) -> {
if (portal.getWorld().equals(world)) { if (portal.getLocation().getWorld().equals(world)) {
portalsToRemove.add(portal); portalsToRemove.add(portal);
} }
}); });
@@ -60,7 +66,7 @@ public class PortalRegistry {
* *
* @param portalsToRemove <p>A list of portals to remove</p> * @param portalsToRemove <p>A list of portals to remove</p>
*/ */
private static void clearPortals(List<Portal> portalsToRemove) { private static void clearPortals(@NotNull List<Portal> portalsToRemove) {
//Store the names of the portals to remove as some maps require the name, not the object //Store the names of the portals to remove as some maps require the name, not the object
List<String> portalNames = new ArrayList<>(); List<String> portalNames = new ArrayList<>();
portalsToRemove.forEach((portal) -> portalNames.add(portal.getCleanName())); portalsToRemove.forEach((portal) -> portalNames.add(portal.getCleanName()));
@@ -86,44 +92,58 @@ public class PortalRegistry {
* *
* @return <p>A copy of the list of all portals</p> * @return <p>A copy of the list of all portals</p>
*/ */
@NotNull
public static List<Portal> getAllPortals() { public static List<Portal> getAllPortals() {
return new ArrayList<>(allPortals); return new ArrayList<>(allPortals);
} }
/** /**
* Gets a copy of the lookup map for finding a portal by its frame * Gets a portal that the given frame block belongs to
* *
* @return <p>A copy of the frame block lookup map</p> * @param blockLocation <p>The location that might be a frame block</p>
* @return <p>The portal the frame block belongs to, or null</p>
*/ */
public static Map<BlockLocation, Portal> getLookupBlocks() { @Nullable
return new HashMap<>(lookupBlocks); public static Portal getPortalFromFrame(@NotNull Block blockLocation) {
return lookupBlocks.get(blockLocation);
} }
/** /**
* Gets a copy of the lookup map for finding a portal by its control block * Gets the portal that the given control block belongs to
* *
* @return <p>A copy of the control block lookup map</p> * @param blockLocation <p>The location that might be a portal control block</p>
* @return <p>The portal the control block belongs to, or null</p>
*/ */
public static Map<BlockLocation, Portal> getLookupControls() { @Nullable
return new HashMap<>(lookupControls); public static Portal getPortalFromControl(@NotNull Block blockLocation) {
return lookupControls.get(blockLocation);
} }
/** /**
* Gets a copy of the lookup map for finding all portals in a network * Gets the portal identified by the given network name and portal name
* *
* @return <p>A copy of the network portal lookup map</p> * @param networkName <p>The name of the network the portal belongs to</p>
* @param portalName <p>The name of the portal</p>
* @return <p>The portal, or null if no such network and/or portal exists</p>
*/ */
public static Map<String, Map<String, Portal>> getPortalLookupByNetwork() { @Nullable
return new HashMap<>(portalLookupByNetwork); public static Portal getPortalInNetwork(@NotNull String networkName, @NotNull String portalName) {
Map<String, Portal> portalsInNetwork = portalLookupByNetwork.get(Portal.cleanString(networkName));
if (portalsInNetwork == null) {
return null;
}
return portalsInNetwork.get(Portal.cleanString(portalName));
} }
/** /**
* Gets a copy of all portal entrances available for lookup * Gets a portal from the location of a possible entrance
* *
* @return <p>A copy of all entrances to portal mappings</p> * @param blockLocation <p>A location that might be a portal's entrance</p>
* @return <p>A portal, or null</p>
*/ */
public static Map<BlockLocation, Portal> getLookupEntrances() { @Nullable
return new HashMap<>(lookupEntrances); public static Portal getPortalFromEntrance(@NotNull Block blockLocation) {
return lookupEntrances.get(blockLocation);
} }
/** /**
@@ -131,17 +151,20 @@ public class PortalRegistry {
* *
* @return <p>A copy of all portal networks</p> * @return <p>A copy of all portal networks</p>
*/ */
@NotNull
public static Map<String, List<String>> getAllPortalNetworks() { public static Map<String, List<String>> getAllPortalNetworks() {
return new HashMap<>(allPortalNetworks); return new HashMap<>(allPortalNetworks);
} }
/** /**
* Gets a copy of all bungee portals * Gets the BungeeCord portal with the given name
* *
* @return <p>A copy of all bungee portals</p> * @param portalName <p>The name of the portal to get</p>
* @return <p>The portal, or null</p>
*/ */
public static Map<String, Portal> getBungeePortals() { @Nullable
return new HashMap<>(bungeePortals); public static Portal getBungeePortal(@NotNull String portalName) {
return bungeePortals.get(Portal.cleanString(portalName));
} }
/** /**
@@ -150,7 +173,8 @@ public class PortalRegistry {
* @param network <p>The network to get portals from</p> * @param network <p>The network to get portals from</p>
* @return <p>A list of portal names</p> * @return <p>A list of portal names</p>
*/ */
public static List<String> getNetwork(String network) { @Nullable
public static List<String> getNetwork(@NotNull String network) {
return allPortalNetworks.get(network.toLowerCase()); return allPortalNetworks.get(network.toLowerCase());
} }
@@ -160,7 +184,7 @@ public class PortalRegistry {
* @param portal <p>The portal to un-register</p> * @param portal <p>The portal to un-register</p>
* @param removeAll <p>Whether to remove the portal from the list of all portals</p> * @param removeAll <p>Whether to remove the portal from the list of all portals</p>
*/ */
public static void unregisterPortal(Portal portal, boolean removeAll) { public static void unregisterPortal(@NotNull Portal portal, boolean removeAll) {
Stargate.debug("Unregister", "Unregistering gate " + portal.getName()); Stargate.debug("Unregister", "Unregistering gate " + portal.getName());
portal.getPortalActivator().deactivate(); portal.getPortalActivator().deactivate();
portal.getPortalOpener().closePortal(true); portal.getPortalOpener().closePortal(true);
@@ -168,30 +192,7 @@ public class PortalRegistry {
String portalName = portal.getCleanName(); String portalName = portal.getCleanName();
String networkName = portal.getCleanNetwork(); String networkName = portal.getCleanNetwork();
//Remove portal from lookup blocks clearLookupMaps(portal, removeAll);
for (BlockLocation block : portal.getStructure().getFrame()) {
lookupBlocks.remove(block);
}
//Remove registered info about the lookup controls and blocks
lookupBlocks.remove(portal.getSignLocation());
lookupControls.remove(portal.getSignLocation());
BlockLocation button = portal.getStructure().getButton();
if (button != null) {
lookupBlocks.remove(button);
lookupControls.remove(button);
}
//Remove entrances
for (BlockLocation entrance : portal.getStructure().getEntrances()) {
lookupEntrances.remove(entrance);
}
//Remove the portal from the list of all portals
if (removeAll) {
allPortals.remove(portal);
}
if (portal.getOptions().isBungee()) { if (portal.getOptions().isBungee()) {
//Remove the bungee listing //Remove the bungee listing
@@ -203,9 +204,9 @@ public class PortalRegistry {
//Update all portals in the same network with this portal as its destination //Update all portals in the same network with this portal as its destination
for (String originName : allPortalNetworks.get(networkName)) { for (String originName : allPortalNetworks.get(networkName)) {
Portal origin = PortalHandler.getByName(originName, portal.getCleanNetwork()); Portal origin = PortalUtil.getByName(originName, portal.getCleanNetwork());
if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) || if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) ||
!origin.getStructure().isVerified()) { !new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(origin.getLocation().getSignBlock().getType())) {
continue; continue;
} }
//Update the portal's sign //Update the portal's sign
@@ -222,8 +223,42 @@ public class PortalRegistry {
//Mark the portal's sign as unregistered //Mark the portal's sign as unregistered
new PortalSignDrawer(portal).drawUnregisteredSign(); new PortalSignDrawer(portal).drawUnregisteredSign();
PortalFileHelper.saveAllPortals(portal.getWorld()); PortalFileHelper.saveAllPortals(portal.getLocation().getWorld());
portal.setRegistered(false); portal.setRegistered(false);
DynmapManager.removePortalMarker(portal);
}
/**
* Clears the given portal's presence from lookup maps
*
* @param portal <p>The portal to clear</p>
* @param removeAll <p>Whether to remove the portal from the list of all portals</p>
*/
private static void clearLookupMaps(@NotNull Portal portal, boolean removeAll) {
//Remove portal from lookup blocks
for (Block block : portal.getStructure().getFrame()) {
lookupBlocks.remove(block);
}
//Remove registered info about the lookup controls and blocks
lookupBlocks.remove(portal.getLocation().getSignBlock());
lookupControls.remove(portal.getLocation().getSignBlock());
Block button = portal.getStructure().getButton();
if (button != null) {
lookupBlocks.remove(button);
lookupControls.remove(button);
}
//Remove entrances
for (Block entrance : portal.getStructure().getEntrances()) {
lookupEntrances.remove(entrance);
}
//Remove the portal from the list of all portals
if (removeAll) {
allPortals.remove(portal);
}
} }
/** /**
@@ -231,8 +266,8 @@ public class PortalRegistry {
* *
* @param portal <p>The portal to register</p> * @param portal <p>The portal to register</p>
*/ */
static void registerPortal(Portal portal) { public static void registerPortal(@NotNull Portal portal) {
portal.getOptions().setFixed(portal.getDestinationName().length() > 0 || portal.getOptions().isRandom() || portal.getOptions().setFixed(!portal.getDestinationName().isEmpty() || portal.getOptions().isRandom() ||
portal.getOptions().isBungee()); portal.getOptions().isBungee());
String portalName = portal.getCleanName(); String portalName = portal.getCleanName();
@@ -267,28 +302,29 @@ public class PortalRegistry {
} }
//Register all frame blocks to the lookup list //Register all frame blocks to the lookup list
for (BlockLocation block : portal.getStructure().getFrame()) { for (Block block : portal.getStructure().getFrame()) {
lookupBlocks.put(block, portal); lookupBlocks.put(block, portal);
} }
//Register the sign and button to the lookup lists //Register the sign and button to the lookup lists
if (!portal.getOptions().hasNoSign()) { if (!portal.getOptions().hasNoSign()) {
lookupBlocks.put(portal.getSignLocation(), portal); lookupBlocks.put(portal.getLocation().getSignBlock(), portal);
lookupControls.put(portal.getSignLocation(), portal); lookupControls.put(portal.getLocation().getSignBlock(), portal);
} }
BlockLocation button = portal.getStructure().getButton(); Block button = portal.getStructure().getButton();
if (button != null) { if (button != null) {
lookupBlocks.put(button, portal); lookupBlocks.put(button, portal);
lookupControls.put(button, portal); lookupControls.put(button, portal);
} }
//Register entrances to the lookup list //Register entrances to the lookup list
for (BlockLocation entrance : portal.getStructure().getEntrances()) { for (Block entrance : portal.getStructure().getEntrances()) {
lookupEntrances.put(entrance, portal); lookupEntrances.put(entrance, portal);
} }
allPortals.add(portal); allPortals.add(portal);
portal.setRegistered(true); portal.setRegistered(true);
DynmapManager.addPortalMarker(portal);
} }
} }

View File

@@ -1,18 +1,25 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.portal;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.container.SignData; import net.knarcraft.stargate.container.SignData;
import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalUtil;
import net.knarcraft.stargate.utility.SignHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import static net.knarcraft.stargate.utility.ColorHelper.translateAllColorCodes;
/** /**
* The portal sign drawer draws the sing of a given portal * The portal sign drawer draws the sing of a given portal
@@ -32,7 +39,7 @@ public class PortalSignDrawer {
* *
* @param portal <p>The portal whose sign this portal sign drawer is responsible for drawing</p> * @param portal <p>The portal whose sign this portal sign drawer is responsible for drawing</p>
*/ */
public PortalSignDrawer(Portal portal) { public PortalSignDrawer(@NotNull Portal portal) {
this.portal = portal; this.portal = portal;
} }
@@ -43,7 +50,7 @@ public class PortalSignDrawer {
* *
* @param newHighlightColor <p>The new highlight color</p> * @param newHighlightColor <p>The new highlight color</p>
*/ */
public static void setHighlightColor(ChatColor newHighlightColor) { public static void setHighlightColor(@NotNull ChatColor newHighlightColor) {
highlightColor = newHighlightColor; highlightColor = newHighlightColor;
} }
@@ -54,7 +61,7 @@ public class PortalSignDrawer {
* *
* @param newMainColor <p>The new main sign color</p> * @param newMainColor <p>The new main sign color</p>
*/ */
public static void setMainColor(ChatColor newMainColor) { public static void setMainColor(@NotNull ChatColor newMainColor) {
mainColor = newMainColor; mainColor = newMainColor;
} }
@@ -63,7 +70,7 @@ public class PortalSignDrawer {
* *
* @param freeColor <p>The new color to use for marking free stargates</p> * @param freeColor <p>The new color to use for marking free stargates</p>
*/ */
public static void setFreeColor(ChatColor freeColor) { public static void setFreeColor(@NotNull ChatColor freeColor) {
PortalSignDrawer.freeColor = freeColor; PortalSignDrawer.freeColor = freeColor;
} }
@@ -72,7 +79,7 @@ public class PortalSignDrawer {
* *
* @param signMainColors <p>The per-sign main colors</p> * @param signMainColors <p>The per-sign main colors</p>
*/ */
public static void setPerSignMainColors(Map<Material, ChatColor> signMainColors) { public static void setPerSignMainColors(@NotNull Map<Material, ChatColor> signMainColors) {
PortalSignDrawer.perSignMainColors = signMainColors; PortalSignDrawer.perSignMainColors = signMainColors;
} }
@@ -81,7 +88,7 @@ public class PortalSignDrawer {
* *
* @param signHighlightColors <p>The per-sign highlight colors</p> * @param signHighlightColors <p>The per-sign highlight colors</p>
*/ */
public static void setPerSignHighlightColors(Map<Material, ChatColor> signHighlightColors) { public static void setPerSignHighlightColors(@NotNull Map<Material, ChatColor> signHighlightColors) {
PortalSignDrawer.perSignHighlightColors = signHighlightColors; PortalSignDrawer.perSignHighlightColors = signHighlightColors;
} }
@@ -90,6 +97,7 @@ public class PortalSignDrawer {
* *
* @return <p>The currently used main sign color</p> * @return <p>The currently used main sign color</p>
*/ */
@NotNull
public static ChatColor getMainColor() { public static ChatColor getMainColor() {
return mainColor; return mainColor;
} }
@@ -99,6 +107,7 @@ public class PortalSignDrawer {
* *
* @return <p>The currently used highlighting sign color</p> * @return <p>The currently used highlighting sign color</p>
*/ */
@NotNull
public static ChatColor getHighlightColor() { public static ChatColor getHighlightColor() {
return highlightColor; return highlightColor;
} }
@@ -112,8 +121,7 @@ public class PortalSignDrawer {
return; return;
} }
SignData signData = new SignData(sign, getMainColor(sign.getType()), getHighlightColor(sign.getType())); drawSign(new SignData(sign, getMainColor(sign.getType()), getHighlightColor(sign.getType())));
drawSign(signData);
} }
/** /**
@@ -121,8 +129,9 @@ public class PortalSignDrawer {
* *
* @return <p>The sign of this sign drawer's portal</p> * @return <p>The sign of this sign drawer's portal</p>
*/ */
@Nullable
private Sign getSign() { private Sign getSign() {
Block signBlock = portal.getSignLocation().getBlock(); Block signBlock = portal.getLocation().getSignBlock();
BlockState state = signBlock.getState(); BlockState state = signBlock.getState();
if (!(state instanceof Sign sign)) { if (!(state instanceof Sign sign)) {
if (!portal.getOptions().hasNoSign()) { if (!portal.getOptions().hasNoSign()) {
@@ -140,42 +149,63 @@ public class PortalSignDrawer {
* *
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
*/ */
private void drawSign(SignData signData) { private void drawSign(@NotNull SignData signData) {
Sign sign = signData.getSign(); Sign sign = signData.getSign();
ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
//Clear sign
clearSign(sign); // Initialize all lines as empty to prevent null
String[] lines = new String[4];
lines[0] = "";
lines[1] = "";
lines[2] = "";
lines[3] = "";
setLine(signData, 0, highlightColor + "-" + mainColor + translateAllColorCodes(portal.getName()) + setLine(signData, 0, highlightColor + "-" + mainColor + translateAllColorCodes(portal.getName()) +
highlightColor + "-"); highlightColor + "-", lines);
if (!portal.getPortalActivator().isActive()) { if (!portal.getPortalActivator().isActive()) {
//Default sign text //Default sign text
drawInactiveSign(signData); drawInactiveSign(signData, lines);
} else { } else {
if (portal.getOptions().isBungee()) { if (portal.getOptions().isBungee()) {
//Bungee sign //Bungee sign
drawBungeeSign(signData); drawBungeeSign(signData, lines);
} else if (portal.getOptions().isFixed()) { } else if (portal.getOptions().isFixed()) {
//Sign pointing at one other portal //Sign pointing at one other portal
drawFixedSign(signData); drawFixedSign(signData, lines);
} else { } else {
//Networking stuff //Networking stuff
drawNetworkSign(signData); drawNetworkSign(signData, lines);
} }
} }
sign.update(); updateSign(sign, lines);
} }
/** /**
* Clears all lines of a sign, but does not update the sign * Updates a sign, if necessary
* *
* @param sign <p>The sign to clear</p> * @param sign <p>The sign to update</p>
* @param lines <p>The sign's new lines</p>
*/ */
private void clearSign(Sign sign) { private void updateSign(@NotNull Sign sign, @NotNull String[] lines) {
for (int index = 0; index <= 3; index++) { boolean updateNecessary = false;
sign.setLine(index, "");
String[] oldLines = SignHelper.getLines(sign);
for (int i = 0; i < 4; i++) {
if (!oldLines[i].equals(lines[i])) {
updateNecessary = true;
break;
}
}
if (updateNecessary) {
for (int i = 0; i < 4; i++) {
SignHelper.setSignLine(sign, i, lines[i]);
}
sign.update();
} }
} }
@@ -187,8 +217,12 @@ public class PortalSignDrawer {
if (sign == null) { if (sign == null) {
return; return;
} }
clearSign(sign);
sign.setLine(0, translateAllColorCodes(portal.getName())); for (int index = 0; index <= 3; index++) {
SignHelper.setSignLine(sign, index, "");
}
SignHelper.setSignLine(sign, 0, translateAllColorCodes(portal.getName()));
sign.update(); sign.update();
} }
@@ -196,8 +230,9 @@ public class PortalSignDrawer {
* Draws a sign with choose-able network locations * Draws a sign with choose-able network locations
* *
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawNetworkSign(SignData signData) { private void drawNetworkSign(@NotNull SignData signData, @NotNull String[] output) {
PortalActivator destinations = portal.getPortalActivator(); PortalActivator destinations = portal.getPortalActivator();
int maxIndex = destinations.getDestinations().size() - 1; int maxIndex = destinations.getDestinations().size() - 1;
int signLineIndex = 0; int signLineIndex = 0;
@@ -207,21 +242,21 @@ public class PortalSignDrawer {
//Last, and not only entry. Draw the entry two back //Last, and not only entry. Draw the entry two back
if ((destinationIndex == maxIndex) && (maxIndex > 1)) { if ((destinationIndex == maxIndex) && (maxIndex > 1)) {
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 2); drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 2, output);
} }
//Not first entry. Draw the previous entry //Not first entry. Draw the previous entry
if (destinationIndex > 0) { if (destinationIndex > 0) {
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 1); drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 1, output);
} }
//Draw the chosen entry (line 2 or 3) //Draw the chosen entry (line 2 or 3)
drawNetworkSignChosenLine(signData, freeGatesColored, ++signLineIndex); drawNetworkSignChosenLine(signData, freeGatesColored, ++signLineIndex, output);
//Has another entry and space on the sign //Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 1)) { if ((maxIndex >= destinationIndex + 1)) {
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex + 1); drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex + 1, output);
} }
//Has another entry and space on the sign //Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 2) && (++signLineIndex <= 3)) { if ((maxIndex >= destinationIndex + 2) && (++signLineIndex <= 3)) {
drawNetworkSignLine(signData, freeGatesColored, signLineIndex, destinationIndex + 2); drawNetworkSignLine(signData, freeGatesColored, signLineIndex, destinationIndex + 2, output);
} }
} }
@@ -231,19 +266,21 @@ public class PortalSignDrawer {
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param freeGatesColored <p>Whether to display free gates in a different color</p> * @param freeGatesColored <p>Whether to display free gates in a different color</p>
* @param signLineIndex <p>The line to draw on</p> * @param signLineIndex <p>The line to draw on</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawNetworkSignChosenLine(SignData signData, boolean freeGatesColored, int signLineIndex) { private void drawNetworkSignChosenLine(@NotNull SignData signData, boolean freeGatesColored, int signLineIndex,
@NotNull String[] output) {
ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
if (freeGatesColored) { if (freeGatesColored) {
Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork()); Portal destination = PortalUtil.getByName(portal.getDestinationName(), portal.getNetwork());
boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination); boolean free = PermissionHelper.isFree(Objects.requireNonNull(portal.getActivePlayer()), portal, destination);
ChatColor nameColor = (free ? freeColor : highlightColor); ChatColor nameColor = (free ? freeColor : highlightColor);
setLine(signData, signLineIndex, nameColor + ">" + (free ? freeColor : mainColor) + setLine(signData, signLineIndex, nameColor + ">" + (free ? freeColor : mainColor) +
translateAllColorCodes(portal.getDestinationName()) + nameColor + "<"); translateAllColorCodes(portal.getDestinationName()) + nameColor + "<", output);
} else { } else {
setLine(signData, signLineIndex, highlightColor + ">" + mainColor + setLine(signData, signLineIndex, highlightColor + ">" + mainColor +
translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<"); translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<", output);
} }
} }
@@ -253,10 +290,11 @@ public class PortalSignDrawer {
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param index <p>The index of the sign line to change</p> * @param index <p>The index of the sign line to change</p>
* @param text <p>The new text on the sign</p> * @param text <p>The new text on the sign</p>
* @param output <p>The output list to write to</p>
*/ */
public void setLine(SignData signData, int index, String text) { public void setLine(@NotNull SignData signData, int index, @NotNull String text, @NotNull String[] output) {
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
signData.getSign().setLine(index, mainColor + text); output[index] = mainColor + text;
} }
/** /**
@@ -266,17 +304,19 @@ public class PortalSignDrawer {
* @param freeGatesColored <p>Whether to display free gates in a different color</p> * @param freeGatesColored <p>Whether to display free gates in a different color</p>
* @param signLineIndex <p>The line to draw on</p> * @param signLineIndex <p>The line to draw on</p>
* @param destinationIndex <p>The index of the destination to draw</p> * @param destinationIndex <p>The index of the destination to draw</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawNetworkSignLine(SignData signData, boolean freeGatesColored, int signLineIndex, int destinationIndex) { private void drawNetworkSignLine(@NotNull SignData signData, boolean freeGatesColored, int signLineIndex,
int destinationIndex, @NotNull String[] output) {
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
PortalActivator destinations = portal.getPortalActivator(); PortalActivator destinations = portal.getPortalActivator();
String destinationName = destinations.getDestinations().get(destinationIndex); String destinationName = destinations.getDestinations().get(destinationIndex);
if (freeGatesColored) { if (freeGatesColored) {
Portal destination = PortalHandler.getByName(destinationName, portal.getNetwork()); Portal destination = PortalUtil.getByName(destinationName, portal.getNetwork());
boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination); boolean free = PermissionHelper.isFree(Objects.requireNonNull(portal.getActivePlayer()), portal, destination);
setLine(signData, signLineIndex, (free ? freeColor : mainColor) + translateAllColorCodes(destinationName)); setLine(signData, signLineIndex, (free ? freeColor : mainColor) + translateAllColorCodes(destinationName), output);
} else { } else {
setLine(signData, signLineIndex, mainColor + translateAllColorCodes(destinationName)); setLine(signData, signLineIndex, mainColor + translateAllColorCodes(destinationName), output);
} }
} }
@@ -284,15 +324,16 @@ public class PortalSignDrawer {
* Draws the sign of a BungeeCord portal * Draws the sign of a BungeeCord portal
* *
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawBungeeSign(SignData signData) { private void drawBungeeSign(@NotNull SignData signData, @NotNull String[] output) {
ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
setLine(signData, 1, Stargate.getString("bungeeSign")); setLine(signData, 1, new SGFormatBuilder(Message.BUNGEE_SIGN).toString(), output);
setLine(signData, 2, highlightColor + ">" + mainColor + translateAllColorCodes(portal.getDestinationName()) + setLine(signData, 2, highlightColor + ">" + mainColor +
highlightColor + "<"); translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<", output);
setLine(signData, 3, highlightColor + "[" + mainColor + translateAllColorCodes(portal.getNetwork()) + setLine(signData, 3, highlightColor + "[" + mainColor + translateAllColorCodes(portal.getNetwork()) +
highlightColor + "]"); highlightColor + "]", output);
} }
/** /**
@@ -301,17 +342,18 @@ public class PortalSignDrawer {
* <p>The sign for an in-active portal should display the right-click prompt and the network.</p> * <p>The sign for an in-active portal should display the right-click prompt and the network.</p>
* *
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawInactiveSign(SignData signData) { private void drawInactiveSign(@NotNull SignData signData, @NotNull String[] output) {
ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
setLine(signData, 1, Stargate.getString("signRightClick")); setLine(signData, 1, new SGFormatBuilder(Message.SIGN_RIGHT_CLICK).toString(), output);
setLine(signData, 2, Stargate.getString("signToUse")); setLine(signData, 2, new SGFormatBuilder(Message.SIGN_TO_USE).toString(), output);
if (!portal.getOptions().isNoNetwork()) { if (!portal.getOptions().isNoNetwork()) {
setLine(signData, 3, highlightColor + "(" + mainColor + translateAllColorCodes(portal.getNetwork()) + setLine(signData, 3, highlightColor + "(" + mainColor + translateAllColorCodes(portal.getNetwork()) +
highlightColor + ")"); highlightColor + ")", output);
} else { } else {
setLine(signData, 3, ""); setLine(signData, 3, "", output);
} }
} }
@@ -319,29 +361,28 @@ public class PortalSignDrawer {
* Draws a sign pointing to a fixed location * Draws a sign pointing to a fixed location
* *
* @param signData <p>All necessary sign information</p> * @param signData <p>All necessary sign information</p>
* @param output <p>The output list to write to</p>
*/ */
private void drawFixedSign(SignData signData) { private void drawFixedSign(@NotNull SignData signData, @NotNull String[] output) {
ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor(); ChatColor mainColor = signData.getMainSignColor();
Portal destinationPortal = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()), Portal destinationPortal = PortalUtil.getByName(portal.getDestinationName(), portal.getCleanNetwork());
portal.getCleanNetwork()); String destinationName = portal.getOptions().isRandom() ? new SGFormatBuilder(Message.SIGN_RANDOM).toString() :
String destinationName = portal.getOptions().isRandom() ? Stargate.getString("signRandom") :
(destinationPortal != null ? destinationPortal.getName() : portal.getDestinationName()); (destinationPortal != null ? destinationPortal.getName() : portal.getDestinationName());
setLine(signData, 1, highlightColor + ">" + mainColor + translateAllColorCodes(destinationName) + setLine(signData, 1, highlightColor + ">" + mainColor + translateAllColorCodes(destinationName) +
highlightColor + "<"); highlightColor + "<", output);
if (portal.getOptions().isNoNetwork()) { if (portal.getOptions().isNoNetwork()) {
setLine(signData, 2, ""); setLine(signData, 2, "", output);
} else { } else {
setLine(signData, 2, highlightColor + "(" + mainColor + setLine(signData, 2, highlightColor + "(" + mainColor +
translateAllColorCodes(portal.getNetwork()) + highlightColor + ")"); translateAllColorCodes(portal.getNetwork()) + highlightColor + ")", output);
} }
Portal destination = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()), Portal destination = PortalUtil.getByName(portal.getDestinationName(), portal.getNetwork());
portal.getNetwork());
if (destination == null && !portal.getOptions().isRandom()) { if (destination == null && !portal.getOptions().isRandom()) {
setLine(signData, 3, errorColor + Stargate.getString("signDisconnected")); setLine(signData, 3, errorColor + new SGFormatBuilder(Message.SIGN_DISCONNECTED).toString(), output);
} else { } else {
setLine(signData, 3, ""); setLine(signData, 3, "", output);
} }
} }
@@ -352,12 +393,13 @@ public class PortalSignDrawer {
* @param gateName <p>The name of the invalid gate type</p> * @param gateName <p>The name of the invalid gate type</p>
* @param lineIndex <p>The index of the line the invalid portal was found at</p> * @param lineIndex <p>The index of the line the invalid portal was found at</p>
*/ */
public static void markPortalWithInvalidGate(PortalLocation portalLocation, String gateName, int lineIndex) { public static void markPortalWithInvalidGate(@NotNull PortalLocation portalLocation, @NotNull String gateName,
BlockState blockState = portalLocation.getSignLocation().getBlock().getState(); int lineIndex) {
BlockState blockState = portalLocation.getSignBlock().getState();
if (!(blockState instanceof Sign sign)) { if (!(blockState instanceof Sign sign)) {
return; return;
} }
sign.setLine(3, errorColor + Stargate.getString("signInvalidGate")); SignHelper.setSignLine(sign, 3, errorColor + new SGFormatBuilder(Message.SIGN_INVALID).toString());
sign.update(); sign.update();
Stargate.logInfo(String.format("Gate layout on line %d does not exist [%s]", lineIndex, gateName)); Stargate.logInfo(String.format("Gate layout on line %d does not exist [%s]", lineIndex, gateName));
@@ -369,7 +411,8 @@ public class PortalSignDrawer {
* @param signType <p>The sign type to get the main color for</p> * @param signType <p>The sign type to get the main color for</p>
* @return <p>The main color for the given sign type</p> * @return <p>The main color for the given sign type</p>
*/ */
private ChatColor getMainColor(Material signType) { @NotNull
private ChatColor getMainColor(@NotNull Material signType) {
ChatColor signColor = perSignMainColors.get(signType); ChatColor signColor = perSignMainColors.get(signType);
if (signColor == null) { if (signColor == null) {
return mainColor; return mainColor;
@@ -384,7 +427,8 @@ public class PortalSignDrawer {
* @param signType <p>The sign type to get the highlight color for</p> * @param signType <p>The sign type to get the highlight color for</p>
* @return <p>The highlight color for the given sign type</p> * @return <p>The highlight color for the given sign type</p>
*/ */
private ChatColor getHighlightColor(Material signType) { @NotNull
private ChatColor getHighlightColor(@NotNull Material signType) {
ChatColor signColor = perSignHighlightColors.get(signType); ChatColor signColor = perSignHighlightColors.get(signType);
if (signColor == null) { if (signColor == null) {
return highlightColor; return highlightColor;
@@ -393,4 +437,15 @@ public class PortalSignDrawer {
} }
} }
/**
* Translates all normal and RGB color codes in the given input
*
* @param input <p>The input to translate color codes for</p>
* @return <p>The input with color codes converted translated from & to §</p>
*/
@NotNull
private String translateAllColorCodes(@NotNull String input) {
return ColorHelper.translateColorCodes(input, ColorConversion.RGB);
}
} }

View File

@@ -1,10 +1,12 @@
package net.knarcraft.stargate.portal.property; package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.transformation.SimpleVectorOperation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import org.bukkit.Axis;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* Keeps track of location related data for a portal * Keeps track of location related data for a portal
@@ -12,28 +14,61 @@ import org.bukkit.block.BlockFace;
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public class PortalLocation { public class PortalLocation {
private BlockLocation topLeft; private @Nullable Block topLeft;
private float yaw; private @NotNull BlockFace facing;
private BlockLocation signLocation; private @NotNull Block signBlock;
private RelativeBlockVector buttonVector; private @Nullable Block buttonBlock;
private BlockFace buttonFacing; private final @NotNull World world;
private final SimpleVectorOperation vectorOperation;
/**
* Instantiates a new portal location
*
* @param topLeft <p>The top-left block of the portal</p>
* @param facing <p>The direction the portal is facing</p>
* @param signBlock <p>The block the sign is attached to</p>
* @param buttonBlock <p>The block of the portal, or null if the portal doesn't need a button</p>
*/
public PortalLocation(@NotNull Block topLeft, @NotNull BlockFace facing, @NotNull Block signBlock,
@Nullable Block buttonBlock) {
this.world = topLeft.getWorld();
this.topLeft = topLeft;
this.facing = facing;
this.signBlock = signBlock;
this.buttonBlock = buttonBlock;
this.vectorOperation = new SimpleVectorOperation(facing);
}
/**
* Instantiates a new portal location
*
* @param signBlock <p>The block the sign is attached to</p>
* @param facing <p>The direction the portal is facing</p>
*/
public PortalLocation(@NotNull Block signBlock, @NotNull BlockFace facing) {
this.world = signBlock.getWorld();
this.signBlock = signBlock;
this.facing = facing;
this.vectorOperation = new SimpleVectorOperation(facing);
}
/** /**
* Gets the top-left block of the portal * Gets the top-left block of the portal
* *
* @return <p>The top-left block of the portal</p> * @return <p>The top-left block of the portal</p>
*/ */
public BlockLocation getTopLeft() { @Nullable
public Block getTopLeft() {
return topLeft; return topLeft;
} }
/** /**
* Gets the yaw for looking outwards from the portal * Gets the block face the portal is facing towards
* *
* @return <p>The portal's yaw</p> * @return <p>The portal's facing direction</p>
*/ */
public float getYaw() { public BlockFace getFacing() {
return yaw; return facing;
} }
/** /**
@@ -41,8 +76,9 @@ public class PortalLocation {
* *
* @return <p>The location of the portal's sign</p> * @return <p>The location of the portal's sign</p>
*/ */
public BlockLocation getSignLocation() { @NotNull
return signLocation; public Block getSignBlock() {
return signBlock;
} }
/** /**
@@ -50,27 +86,9 @@ public class PortalLocation {
* *
* @return <p>The relative location of the portal's button</p> * @return <p>The relative location of the portal's button</p>
*/ */
public RelativeBlockVector getButtonVector() { @Nullable
return buttonVector; public Block getButtonBlock() {
} return buttonBlock;
/**
* Gets the block face determining the button's direction
*
* @return <p>The button's block face</p>
*/
public BlockFace getButtonFacing() {
return buttonFacing;
}
/**
* Gets the rotation axis, which is the axis along which the gate is placed
* <p>The portal's rotation axis is the cross axis of the button's axis</p>
*
* @return <p>The portal's rotation axis</p>
*/
public Axis getRotationAxis() {
return getYaw() == 0.0F || getYaw() == 180.0F ? Axis.X : Axis.Z;
} }
/** /**
@@ -78,8 +96,19 @@ public class PortalLocation {
* *
* @return <p>The world this portal resides in</p> * @return <p>The world this portal resides in</p>
*/ */
@NotNull
public World getWorld() { public World getWorld() {
return topLeft.getWorld(); return this.world;
}
/**
* Gets the vector operation to use in order to convert this portal's relative vectors to the real space
*
* @return <p>The vector operation</p>
*/
@NotNull
public SimpleVectorOperation getVectorOperation() {
return this.vectorOperation;
} }
/** /**
@@ -91,55 +120,63 @@ public class PortalLocation {
* @param topLeft <p>The new top-left block of the portal's square structure</p> * @param topLeft <p>The new top-left block of the portal's square structure</p>
* @return <p>The portal location Object</p> * @return <p>The portal location Object</p>
*/ */
public PortalLocation setTopLeft(BlockLocation topLeft) { @NotNull
public PortalLocation setTopLeft(@NotNull Block topLeft) {
this.topLeft = topLeft; this.topLeft = topLeft;
return this; return this;
} }
/** /**
* Sets the portal's yaw * Sets the portal's facing direction
* *
* <p>The portal's yaw is the yaw a player would get when looking directly out from the portal</p> * <p>The portal's facing direction is the direction a player would get when looking directly out from the portal</p>
* *
* @param yaw <p>The portal's new yaw</p> * @param facing <p>The portal's new facing direction</p>
* @return <p>The portal location Object</p> * @return <p>The portal location Object</p>
*/ */
public PortalLocation setYaw(float yaw) { @NotNull
this.yaw = yaw; public PortalLocation setFacing(@NotNull BlockFace facing) {
this.facing = facing;
return this; return this;
} }
/** /**
* Sets the location of the portal's sign * Sets the location of the portal's sign
* *
* @param signLocation <p>The new sign location</p> * @param signBlock <p>The new sign location</p>
* @return <p>The portal location Object</p> * @return <p>The portal location Object</p>
*/ */
public PortalLocation setSignLocation(BlockLocation signLocation) { @NotNull
this.signLocation = signLocation; public PortalLocation setSignBlock(@NotNull Block signBlock) {
this.signBlock = signBlock;
return this; return this;
} }
/** /**
* Sets the relative location of the portal's button * Sets the relative location of the portal's button
* *
* @param buttonVector <p>The new relative button location</p> * @param buttonBlock <p>The new relative button location</p>
* @return <p>The portal location Object</p> * @return <p>The portal location Object</p>
*/ */
public PortalLocation setButtonVector(RelativeBlockVector buttonVector) { @NotNull
this.buttonVector = buttonVector; public PortalLocation setButtonBlock(@Nullable Block buttonBlock) {
this.buttonBlock = buttonBlock;
return this; return this;
} }
/** /**
* Sets the block face for the direction the portal button is facing * Gets a block relative to this portal's top-left block
* *
* @param buttonFacing <p>The new block face of the portal's button</p> * @param vector <p>The relative vector pointing at the block</p>
* @return <p>The portal location Object</p> * @return <p>The block</p>
* @throws IllegalStateException <p>If the top-left location is not set</p>
*/ */
public PortalLocation setButtonFacing(BlockFace buttonFacing) { @NotNull
this.buttonFacing = buttonFacing; public Block getRelative(@NotNull BlockVector vector) throws IllegalStateException {
return this; if (this.topLeft == null) {
throw new IllegalStateException("Top-left is not set");
}
return this.vectorOperation.getRealLocationBlock(this.topLeft, vector);
} }
} }

View File

@@ -1,5 +1,8 @@
package net.knarcraft.stargate.portal.property; package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.config.Permission;
import org.jetbrains.annotations.NotNull;
/** /**
* Each enum value represents one option a portal can have/use * Each enum value represents one option a portal can have/use
*/ */
@@ -8,71 +11,71 @@ public enum PortalOption {
/** /**
* This option allows a portal to be hidden from others * This option allows a portal to be hidden from others
*/ */
HIDDEN('h', "stargate.option.hidden", 11), HIDDEN('h', Permission.OPTION_HIDDEN, 11),
/** /**
* This option allows a portal that's always on and does not need to be activated or opened each time * This option allows a portal that's always on and does not need to be activated or opened each time
*/ */
ALWAYS_ON('a', "stargate.option.alwayson", 12), ALWAYS_ON('a', Permission.OPTION_ALWAYS_ON, 12),
/** /**
* This option allows a portal that's private to the stargate's owner * This option allows a portal that's private to the stargate's owner
*/ */
PRIVATE('p', "stargate.option.private", 13), PRIVATE('p', Permission.OPTIONS_PRIVATE, 13),
/** /**
* This option allows a portal that's free even if stargates usually are not * This option allows a portal that's free even if stargates usually are not
*/ */
FREE('f', "stargate.option.free", 15), FREE('f', Permission.OPTIONS_FREE, 15),
/** /**
* This option allows a portal where players exit through the back of the portal * This option allows a portal where players exit through the back of the portal
*/ */
BACKWARDS('b', "stargate.option.backwards", 16), BACKWARDS('b', Permission.OPTIONS_BACKWARDS, 16),
/** /**
* This option shows the gate in the network list even if it's always on * This option shows the gate in the network list even if it's always on
*/ */
SHOW('s', "stargate.option.show", 17), SHOW('s', Permission.OPTIONS_SHOW, 17),
/** /**
* This option hides the network name on the sign * This option hides the network name on the sign
*/ */
NO_NETWORK('n', "stargate.option.nonetwork", 18), NO_NETWORK('n', Permission.OPTIONS_NO_NETWORK, 18),
/** /**
* This option allows a portal where players teleport to a random exit portal in the network * This option allows a portal where players teleport to a random exit portal in the network
*/ */
RANDOM('r', "stargate.option.random", 19), RANDOM('r', Permission.OPTIONS_RANDOM, 19),
/** /**
* This option allows a portal to teleport to another server connected through BungeeCord * This option allows a portal to teleport to another server connected through BungeeCord
*/ */
BUNGEE('u', "stargate.admin.bungee", 20), BUNGEE('u', Permission.OPTIONS_BUNGEE, 20),
/** /**
* This option allows a portal which does not display a teleportation message, for better immersion * This option allows a portal which does not display a teleportation message, for better immersion
*/ */
SILENT('i', "stargate.option.silent", 21), QUIET('q', Permission.OPTIONS_QUIET, 21),
/** /**
* This option causes a fixed portal's sign to be removed after creation * This option causes a fixed portal's sign to be removed after creation
*/ */
NO_SIGN('e', "stargate.option.nosign", 22); INVISIBLE('v', Permission.OPTIONS_INVISIBLE, 22);
private final char characterRepresentation; private final char characterRepresentation;
private final String permissionString; private final Permission permission;
private final int saveIndex; private final int saveIndex;
/** /**
* Instantiates a new portal options * Instantiates a new portal options
* *
* @param characterRepresentation <p>The character representation used on the sign to allow this option</p> * @param characterRepresentation <p>The character representation used on the sign to allow this option</p>
* @param permissionString <p>The permission necessary to use this option</p> * @param permission <p>The permission necessary to use this option</p>
*/ */
PortalOption(final char characterRepresentation, String permissionString, int saveIndex) { PortalOption(final char characterRepresentation, @NotNull Permission permission, int saveIndex) {
this.characterRepresentation = characterRepresentation; this.characterRepresentation = characterRepresentation;
this.permissionString = permissionString; this.permission = permission;
this.saveIndex = saveIndex; this.saveIndex = saveIndex;
} }
@@ -90,8 +93,9 @@ public enum PortalOption {
* *
* @return <p>The permission necessary for this option</p> * @return <p>The permission necessary for this option</p>
*/ */
public String getPermissionString() { @NotNull
return this.permissionString; public Permission getPermission() {
return this.permission;
} }
/** /**

View File

@@ -1,6 +1,7 @@
package net.knarcraft.stargate.portal.property; package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import org.jetbrains.annotations.NotNull;
import java.util.Map; import java.util.Map;
@@ -18,7 +19,7 @@ public class PortalOptions {
* @param options <p>All options to keep track of</p> * @param options <p>All options to keep track of</p>
* @param hasDestination <p>Whether the portal has a fixed destination</p> * @param hasDestination <p>Whether the portal has a fixed destination</p>
*/ */
public PortalOptions(Map<PortalOption, Boolean> options, boolean hasDestination) { public PortalOptions(@NotNull Map<PortalOption, Boolean> options, boolean hasDestination) {
this.options = options; this.options = options;
isFixed = hasDestination || this.isRandom() || this.isBungee(); isFixed = hasDestination || this.isRandom() || this.isBungee();
@@ -34,7 +35,7 @@ public class PortalOptions {
} }
if (this.hasNoSign() && !this.isFixed) { if (this.hasNoSign() && !this.isFixed) {
this.options.put(PortalOption.NO_SIGN, false); this.options.put(PortalOption.INVISIBLE, false);
Stargate.debug("PortalOptions", "Gate marked with no sign, but not fixed. Setting NoSign = false"); Stargate.debug("PortalOptions", "Gate marked with no sign, but not fixed. Setting NoSign = false");
} }
} }
@@ -171,15 +172,15 @@ public class PortalOptions {
} }
/** /**
* Gets whether this portal is silent * Gets whether this portal is QUIET
* *
* <p>A silent portal does not output anything to the chat when teleporting. This option is mainly useful to keep * <p>A quiet portal does not output anything to the chat when teleporting. This option is mainly useful to keep
* the immersion during teleportation (for role-playing servers or similar).</p> * the immersion during teleportation (for role-playing servers or similar).</p>
* *
* @return <p>Whether this portal is silent</p> * @return <p>Whether this portal is quiet</p>
*/ */
public boolean isSilent() { public boolean isQuiet() {
return this.options.get(PortalOption.SILENT); return this.options.get(PortalOption.QUIET);
} }
/** /**
@@ -190,7 +191,17 @@ public class PortalOptions {
* @return <p>Whether this portal has no sign</p> * @return <p>Whether this portal has no sign</p>
*/ */
public boolean hasNoSign() { public boolean hasNoSign() {
return this.options.get(PortalOption.NO_SIGN); return this.options.get(PortalOption.INVISIBLE);
}
/**
* Checks whether the given portal option is true
*
* @param option <p>The option to check</p>
* @return <p>True if enabled for the portal</p>
*/
public boolean checkOption(@NotNull PortalOption option) {
return this.options.get(option);
} }
} }

View File

@@ -3,7 +3,8 @@ package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
@@ -20,7 +21,7 @@ public class PortalOwner {
* *
* @param ownerIdentifier <p>A UUID, or a username for legacy support</p> * @param ownerIdentifier <p>A UUID, or a username for legacy support</p>
*/ */
public PortalOwner(String ownerIdentifier) { public PortalOwner(@NotNull String ownerIdentifier) {
parseIdentifier(ownerIdentifier); parseIdentifier(ownerIdentifier);
} }
@@ -29,7 +30,7 @@ public class PortalOwner {
* *
* @param player <p>The player which is the owner of the portal</p> * @param player <p>The player which is the owner of the portal</p>
*/ */
public PortalOwner(Player player) { public PortalOwner(@NotNull OfflinePlayer player) {
this.ownerUUID = player.getUniqueId(); this.ownerUUID = player.getUniqueId();
this.ownerName = player.getName(); this.ownerName = player.getName();
} }
@@ -39,6 +40,7 @@ public class PortalOwner {
* *
* @return <p>The UUID of this owner, or null if a UUID is not available</p> * @return <p>The UUID of this owner, or null if a UUID is not available</p>
*/ */
@Nullable
public UUID getUUID() { public UUID getUUID() {
return ownerUUID; return ownerUUID;
} }
@@ -51,7 +53,7 @@ public class PortalOwner {
* *
* @param uniqueId <p>The new unique id for the portal owner</p> * @param uniqueId <p>The new unique id for the portal owner</p>
*/ */
public void setUUID(UUID uniqueId) { public void setUUID(@NotNull UUID uniqueId) {
if (ownerUUID == null) { if (ownerUUID == null) {
ownerUUID = uniqueId; ownerUUID = uniqueId;
} else { } else {
@@ -64,7 +66,14 @@ public class PortalOwner {
* *
* @return <p>The name of this owner</p> * @return <p>The name of this owner</p>
*/ */
@NotNull
public String getName() { public String getName() {
if (this.ownerUUID != null && this.ownerName == null) {
this.ownerName = fetchName();
if (this.ownerName == null) {
this.ownerName = "UNKNOWN!";
}
}
return ownerName; return ownerName;
} }
@@ -76,6 +85,7 @@ public class PortalOwner {
* *
* @return <p>The owner's identifier</p> * @return <p>The owner's identifier</p>
*/ */
@NotNull
public String getIdentifier() { public String getIdentifier() {
if (ownerUUID != null) { if (ownerUUID != null) {
return ownerUUID.toString(); return ownerUUID.toString();
@@ -93,16 +103,14 @@ public class PortalOwner {
* *
* @param ownerIdentifier <p>The identifier for a portal's owner</p> * @param ownerIdentifier <p>The identifier for a portal's owner</p>
*/ */
private void parseIdentifier(String ownerIdentifier) { private void parseIdentifier(@NotNull String ownerIdentifier) {
UUID ownerUUID = null; UUID ownerUUID = null;
String ownerName; String ownerName = null;
if (ownerIdentifier.length() > 16) { if (ownerIdentifier.length() > 16) {
//If more than 16 characters, the string cannot be a username, so it's probably a UUID //If more than 16 characters, the string cannot be a username, so it's probably a UUID
try { try {
ownerUUID = UUID.fromString(ownerIdentifier); ownerUUID = UUID.fromString(ownerIdentifier);
OfflinePlayer offlineOwner = Bukkit.getServer().getOfflinePlayer(ownerUUID); } catch (IllegalArgumentException exception) {
ownerName = offlineOwner.getName();
} catch (IllegalArgumentException ex) {
//Invalid as UUID and username, so just keep it as owner name and hope the server owner fixes it //Invalid as UUID and username, so just keep it as owner name and hope the server owner fixes it
ownerName = ownerIdentifier; ownerName = ownerIdentifier;
Stargate.debug("loadAllPortals", "Invalid stargate owner string: " + ownerIdentifier); Stargate.debug("loadAllPortals", "Invalid stargate owner string: " + ownerIdentifier);
@@ -115,4 +123,14 @@ public class PortalOwner {
this.ownerUUID = ownerUUID; this.ownerUUID = ownerUUID;
} }
/**
* Gets the name of a player
*
* @return <p>The player to get the name of</p>
*/
@Nullable
private String fetchName() {
return Bukkit.getServer().getOfflinePlayer(ownerUUID).getName();
}
} }

View File

@@ -0,0 +1,13 @@
package net.knarcraft.stargate.portal.property;
import org.jetbrains.annotations.NotNull;
/**
* A record of a portal's string values
*
* @param name <p>The name of the portal</p>
* @param network <p>The name of the network the portal belongs to</p>
* @param destination <p>The name of the portal's destination</p>
*/
public record PortalStrings(@NotNull String name, @NotNull String network, @NotNull String destination) {
}

View File

@@ -1,10 +1,12 @@
package net.knarcraft.stargate.portal.property; package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import org.bukkit.block.Block;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* The portal structure is responsible for the physical properties of a portal * The portal structure is responsible for the physical properties of a portal
@@ -16,10 +18,9 @@ public class PortalStructure {
private final Portal portal; private final Portal portal;
private final Gate gate; private final Gate gate;
private BlockLocation button; private Block button;
private BlockLocation[] frame; private Block[] frame;
private BlockLocation[] entrances; private Block[] entrances;
private boolean verified;
/** /**
* Instantiates a new portal structure * Instantiates a new portal structure
@@ -28,10 +29,9 @@ public class PortalStructure {
* @param gate <p>The gate type used by this portal structure</p> * @param gate <p>The gate type used by this portal structure</p>
* @param button <p>The real location of the portal's button</p> * @param button <p>The real location of the portal's button</p>
*/ */
public PortalStructure(Portal portal, Gate gate, BlockLocation button) { public PortalStructure(@NotNull Portal portal, @NotNull Gate gate, @Nullable Block button) {
this.portal = portal; this.portal = portal;
this.gate = gate; this.gate = gate;
this.verified = false;
this.button = button; this.button = button;
} }
@@ -40,6 +40,7 @@ public class PortalStructure {
* *
* @return <p>The gate used by this portal structure</p> * @return <p>The gate used by this portal structure</p>
*/ */
@NotNull
public Gate getGate() { public Gate getGate() {
return gate; return gate;
} }
@@ -49,7 +50,8 @@ public class PortalStructure {
* *
* @return <p>The location of this portal's button</p> * @return <p>The location of this portal's button</p>
*/ */
public BlockLocation getButton() { @Nullable
public Block getButton() {
return button; return button;
} }
@@ -58,39 +60,10 @@ public class PortalStructure {
* *
* @param button <p>The location of this portal's button</p> * @param button <p>The location of this portal's button</p>
*/ */
public void setButton(BlockLocation button) { public void setButton(@NotNull Block button) {
this.button = button; this.button = button;
} }
/**
* Verifies that all control blocks in this portal follows its gate template
*
* @return <p>True if all control blocks were verified</p>
*/
public boolean isVerified() {
boolean verified = true;
if (!Stargate.getGateConfig().verifyPortals()) {
return true;
}
for (RelativeBlockVector control : gate.getLayout().getControls()) {
verified = verified && portal.getBlockAt(control).getBlock().getType().equals(gate.getControlBlock());
}
this.verified = verified;
return verified;
}
/**
* Gets the result of the last portal verification
*
* @return <p>True if this portal was verified</p>
*/
public boolean wasVerified() {
if (!Stargate.getGateConfig().verifyPortals()) {
return true;
}
return verified;
}
/** /**
* Checks if all blocks in a gate matches the gate template * Checks if all blocks in a gate matches the gate template
* *
@@ -98,7 +71,7 @@ public class PortalStructure {
*/ */
public boolean checkIntegrity() { public boolean checkIntegrity() {
if (Stargate.getGateConfig().verifyPortals()) { if (Stargate.getGateConfig().verifyPortals()) {
return gate.matches(portal.getTopLeft(), portal.getYaw()); return gate.matches(portal.getLocation().getTopLeft(), portal.getLocation().getFacing());
} else { } else {
return true; return true;
} }
@@ -113,10 +86,11 @@ public class PortalStructure {
* @param vectors <p>The relative block vectors to convert</p> * @param vectors <p>The relative block vectors to convert</p>
* @return <p>A list of block locations</p> * @return <p>A list of block locations</p>
*/ */
private BlockLocation[] relativeBlockVectorsToBlockLocations(RelativeBlockVector[] vectors) { @NotNull
BlockLocation[] locations = new BlockLocation[vectors.length]; private Block[] relativeBlockVectorsToBlockLocations(@NotNull BlockVector[] vectors) {
Block[] locations = new Block[vectors.length];
for (int i = 0; i < vectors.length; i++) { for (int i = 0; i < vectors.length; i++) {
locations[i] = portal.getBlockAt(vectors[i]); locations[i] = portal.getLocation().getRelative(vectors[i]);
} }
return locations; return locations;
} }
@@ -126,7 +100,8 @@ public class PortalStructure {
* *
* @return <p>The locations of this portal's entrances</p> * @return <p>The locations of this portal's entrances</p>
*/ */
public BlockLocation[] getEntrances() { @NotNull
public Block[] getEntrances() {
if (entrances == null) { if (entrances == null) {
//Get the locations of the entrances once, and only if necessary as it's an expensive operation //Get the locations of the entrances once, and only if necessary as it's an expensive operation
entrances = relativeBlockVectorsToBlockLocations(gate.getLayout().getEntrances()); entrances = relativeBlockVectorsToBlockLocations(gate.getLayout().getEntrances());
@@ -139,7 +114,8 @@ public class PortalStructure {
* *
* @return <p>The locations of this portal's frame</p> * @return <p>The locations of this portal's frame</p>
*/ */
public BlockLocation[] getFrame() { @NotNull
public Block[] getFrame() {
if (frame == null) { if (frame == null) {
//Get the locations of the frame blocks once, and only if necessary as it's an expensive operation //Get the locations of the frame blocks once, and only if necessary as it's an expensive operation
frame = relativeBlockVectorsToBlockLocations(gate.getLayout().getBorder()); frame = relativeBlockVectorsToBlockLocations(gate.getLayout().getBorder());

View File

@@ -1,14 +1,23 @@
package net.knarcraft.stargate.portal.property.gate; package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.config.material.MaterialSpecifier;
import net.knarcraft.stargate.transformation.SimpleVectorOperation;
import net.knarcraft.stargate.utility.MaterialHelper;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@@ -20,11 +29,13 @@ public class Gate {
private final String filename; private final String filename;
private final GateLayout layout; private final GateLayout layout;
private final Map<Character, Material> characterMaterialMap; private final Map<Character, List<MaterialSpecifier>> characterMaterialMap;
//Gate materials //Gate materials
private final Material portalOpenBlock; private final List<MaterialSpecifier> portalOpenMaterials;
private final Material portalClosedBlock; private final List<MaterialSpecifier> portalClosedMaterials;
private final Material portalButton; private final List<MaterialSpecifier> portalButtonMaterials;
//Economy information //Economy information
private final int useCost; private final int useCost;
private final int createCost; private final int createCost;
@@ -34,30 +45,29 @@ public class Gate {
/** /**
* Instantiates a new gate * Instantiates a new gate
* *
* @param filename <p>The name of the gate file, including extension</p> * @param filename <p>The name of the gate file, including extension</p>
* @param layout <p>The gate layout defined in the gate file</p> * @param layout <p>The gate layout defined in the gate file</p>
* @param characterMaterialMap <p>The material types the different layout characters represent</p> * @param characterMaterialsMap <p>The material types the different layout characters represent</p>
* @param portalOpenBlock <p>The material to set the opening to when the portal is open</p> * @param portalOpenMaterials <p>The material to set the opening to when the portal is open</p>
* @param portalClosedBlock <p>The material to set the opening to when the portal is closed</p> * @param portalClosedMaterials <p>The material to set the opening to when the portal is closed</p>
* @param portalButton <p>The material to use for the portal button</p> * @param portalButtonMaterials <p>The material to use for the portal button</p>
* @param useCost <p>The cost of using a portal with this gate layout (-1 to disable)</p> * @param gateCosts <p>The costs and other economy information for the gate</p>
* @param createCost <p>The cost of creating a portal with this gate layout (-1 to disable)</p>
* @param destroyCost <p>The cost of destroying a portal with this gate layout (-1 to disable)</p>
* @param toOwner <p>Whether any payment should go to the owner of the gate, as opposed to just disappearing</p>
*/ */
public Gate(String filename, GateLayout layout, Map<Character, Material> characterMaterialMap, Material portalOpenBlock, public Gate(@NotNull String filename, @NotNull GateLayout layout,
Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost, @NotNull Map<Character, List<MaterialSpecifier>> characterMaterialsMap,
boolean toOwner) { @NotNull List<MaterialSpecifier> portalOpenMaterials,
@NotNull List<MaterialSpecifier> portalClosedMaterials,
@NotNull List<MaterialSpecifier> portalButtonMaterials, @NotNull GateCosts gateCosts) {
this.filename = filename; this.filename = filename;
this.layout = layout; this.layout = layout;
this.characterMaterialMap = characterMaterialMap; this.characterMaterialMap = characterMaterialsMap;
this.portalOpenBlock = portalOpenBlock; this.portalOpenMaterials = portalOpenMaterials;
this.portalClosedBlock = portalClosedBlock; this.portalClosedMaterials = portalClosedMaterials;
this.portalButton = portalButton; this.portalButtonMaterials = portalButtonMaterials;
this.useCost = useCost; this.useCost = gateCosts.useCost();
this.createCost = createCost; this.createCost = gateCosts.createCost();
this.destroyCost = destroyCost; this.destroyCost = gateCosts.destroyCost();
this.toOwner = toOwner; this.toOwner = gateCosts.toOwner();
} }
/** /**
@@ -65,6 +75,7 @@ public class Gate {
* *
* @return <p>This gate's layout</p> * @return <p>This gate's layout</p>
*/ */
@NotNull
public GateLayout getLayout() { public GateLayout getLayout() {
return layout; return layout;
} }
@@ -74,7 +85,8 @@ public class Gate {
* *
* @return <p>The character to material map</p> * @return <p>The character to material map</p>
*/ */
public Map<Character, Material> getCharacterMaterialMap() { @NotNull
public Map<Character, List<MaterialSpecifier>> getCharacterMaterialMap() {
return new HashMap<>(characterMaterialMap); return new HashMap<>(characterMaterialMap);
} }
@@ -83,7 +95,8 @@ public class Gate {
* *
* @return <p>The material type used for control blocks</p> * @return <p>The material type used for control blocks</p>
*/ */
public Material getControlBlock() { @NotNull
public List<MaterialSpecifier> getControlBlockMaterials() {
return characterMaterialMap.get(GateHandler.getControlBlockCharacter()); return characterMaterialMap.get(GateHandler.getControlBlockCharacter());
} }
@@ -92,6 +105,7 @@ public class Gate {
* *
* @return <p>The filename of this gate's file</p> * @return <p>The filename of this gate's file</p>
*/ */
@NotNull
public String getFilename() { public String getFilename() {
return filename; return filename;
} }
@@ -101,8 +115,9 @@ public class Gate {
* *
* @return <p>The block type to use for the opening when open</p> * @return <p>The block type to use for the opening when open</p>
*/ */
public Material getPortalOpenBlock() { @NotNull
return portalOpenBlock; public List<MaterialSpecifier> getPortalOpenMaterials() {
return portalOpenMaterials;
} }
/** /**
@@ -110,8 +125,9 @@ public class Gate {
* *
* @return <p>The block type to use for the opening when closed</p> * @return <p>The block type to use for the opening when closed</p>
*/ */
public Material getPortalClosedBlock() { @NotNull
return portalClosedBlock; public List<MaterialSpecifier> getPortalClosedMaterials() {
return portalClosedMaterials;
} }
/** /**
@@ -119,8 +135,9 @@ public class Gate {
* *
* @return <p>The material to use for a portal's button if using this gate type</p> * @return <p>The material to use for a portal's button if using this gate type</p>
*/ */
public Material getPortalButton() { @NotNull
return portalButton; public List<MaterialSpecifier> getPortalButtonMaterials() {
return portalButtonMaterials;
} }
/** /**
@@ -137,6 +154,7 @@ public class Gate {
* *
* @return <p>The cost of creating a portal with this gate</p> * @return <p>The cost of creating a portal with this gate</p>
*/ */
@NotNull
public Integer getCreateCost() { public Integer getCreateCost() {
return createCost < 0 ? Stargate.getEconomyConfig().getDefaultCreateCost() : createCost; return createCost < 0 ? Stargate.getEconomyConfig().getDefaultCreateCost() : createCost;
} }
@@ -146,6 +164,7 @@ public class Gate {
* *
* @return <p>The cost of destroying a portal with this gate</p> * @return <p>The cost of destroying a portal with this gate</p>
*/ */
@NotNull
public Integer getDestroyCost() { public Integer getDestroyCost() {
return destroyCost < 0 ? Stargate.getEconomyConfig().getDefaultDestroyCost() : destroyCost; return destroyCost < 0 ? Stargate.getEconomyConfig().getDefaultDestroyCost() : destroyCost;
} }
@@ -155,6 +174,7 @@ public class Gate {
* *
* @return <p>Whether portal payments go to the owner</p> * @return <p>Whether portal payments go to the owner</p>
*/ */
@NotNull
public Boolean getToOwner() { public Boolean getToOwner() {
return toOwner; return toOwner;
} }
@@ -163,11 +183,11 @@ public class Gate {
* Checks whether a portal's gate matches this gate type * Checks whether a portal's gate matches this gate type
* *
* @param topLeft <p>The top-left block of the portal's gate</p> * @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards</p> * @param facing <p>The direction the portal is facing</p>
* @return <p>True if this gate matches the portal</p> * @return <p>True if this gate matches the portal</p>
*/ */
public boolean matches(BlockLocation topLeft, double yaw) { public boolean matches(@NotNull Block topLeft, @NotNull BlockFace facing) {
return matches(topLeft, yaw, false); return matches(topLeft, facing, false);
} }
/** /**
@@ -178,43 +198,44 @@ public class Gate {
* containing AIR or WATER will cause the gate to not match.</p> * containing AIR or WATER will cause the gate to not match.</p>
* *
* @param topLeft <p>The top-left block of the portal's gate</p> * @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards</p> * @param facing <p>The direction the portal is facing</p>
* @param onCreate <p>Whether this is used in the context of creating a new gate</p> * @param onCreate <p>Whether this is used in the context of creating a new gate</p>
* @return <p>True if this gate matches the portal</p> * @return <p>True if this gate matches the portal</p>
*/ */
public boolean matches(BlockLocation topLeft, double yaw, boolean onCreate) { public boolean matches(@NotNull Block topLeft, @NotNull BlockFace facing, boolean onCreate) {
return verifyGateEntrancesMatch(topLeft, yaw, onCreate) && verifyGateBorderMatches(topLeft, yaw); return verifyGateEntrancesMatch(topLeft, facing, onCreate) && verifyGateBorderMatches(topLeft, facing);
} }
/** /**
* Verifies that all border blocks of a portal matches this gate type * Verifies that all border blocks of a portal matches this gate type
* *
* @param topLeft <p>The top-left block of the portal</p> * @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards from the portal</p> * @param facing <p>The direction the portal is facing</p>
* @return <p>True if all border blocks of the gate match the layout</p> * @return <p>True if all border blocks of the gate match the layout</p>
*/ */
private boolean verifyGateBorderMatches(BlockLocation topLeft, double yaw) { private boolean verifyGateBorderMatches(@NotNull Block topLeft, @NotNull BlockFace facing) {
Map<Character, Material> characterMaterialMap = new HashMap<>(this.characterMaterialMap); for (BlockVector borderVector : layout.getBorder()) {
for (RelativeBlockVector borderVector : layout.getBorder()) { int rowIndex = borderVector.getBlockX();
int rowIndex = borderVector.getRight(); int lineIndex = borderVector.getBlockY();
int lineIndex = borderVector.getDown();
Character key = layout.getLayout()[lineIndex][rowIndex]; Character key = layout.getLayout()[lineIndex][rowIndex];
Material materialInLayout = characterMaterialMap.get(key); List<MaterialSpecifier> materialInLayout = characterMaterialMap.get(key);
Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType(); Material type = new SimpleVectorOperation(facing).getRealLocationBlock(topLeft, borderVector).getType();
if (materialInLayout == null) { if (materialInLayout != null) {
if (!MaterialHelper.specifiersToMaterials(materialInLayout).contains(type)) {
Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
type, MaterialHelper.specifiersToMaterials(materialInLayout)));
return false;
}
} else {
/* This generally should not happen with proper checking, but just in case a material character is not /* This generally should not happen with proper checking, but just in case a material character is not
* recognized, but still allowed in previous checks, verify the gate as long as all such instances of * recognized, but still allowed in previous checks, verify the gate as long as all such instances of
* the character correspond to the same material in the physical gate. All subsequent gates will also * the character correspond to the same material in the physical gate. All subsequent gates will also
* need to match the first verified gate. */ * need to match the first verified gate. */
characterMaterialMap.put(key, materialAtLocation); this.characterMaterialMap.put(key, List.of(new BukkitMaterialSpecifier(type)));
Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" + Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" +
" physical portal.", getFilename(), materialAtLocation)); " physical portal.", getFilename(), type));
} else if (materialAtLocation != materialInLayout) {
Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
materialAtLocation, materialInLayout));
return false;
} }
} }
return true; return true;
@@ -223,23 +244,24 @@ public class Gate {
/** /**
* Verifies that all entrances of a portal gate matches this gate type * Verifies that all entrances of a portal gate matches this gate type
* *
* @param topLeft <p>The top-left block of this portal</p> * @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards</p> * @param facing <p>The direction the portal is facing</p>
* @param onCreate <p>Whether this is used in the context of creating a new gate</p> * @param onCreate <p>Whether this is used in the context of creating a new gate</p>
* @return <p>Whether this is used in the context of creating a new gate</p> * @return <p>Whether this is used in the context of creating a new gate</p>
*/ */
private boolean verifyGateEntrancesMatch(BlockLocation topLeft, double yaw, boolean onCreate) { private boolean verifyGateEntrancesMatch(@NotNull Block topLeft, @NotNull BlockFace facing, boolean onCreate) {
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(topLeft)); Stargate.debug("verifyGateEntrancesMatch", String.valueOf(topLeft));
for (RelativeBlockVector entranceVector : layout.getEntrances()) { for (BlockVector entranceVector : layout.getEntrances()) {
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(entranceVector)); Stargate.debug("verifyGateEntrancesMatch", String.valueOf(entranceVector));
Material type = topLeft.getRelativeLocation(entranceVector, yaw).getType(); Material type = new SimpleVectorOperation(facing).getRealLocationBlock(topLeft, entranceVector).getType();
//Ignore entrance if it's air or water, and we're creating a new gate //Ignore entrance if it's air or water, and we're creating a new gate
if (onCreate && (type.isAir() || type == Material.WATER)) { if (onCreate && (type.isAir() || type == Material.WATER)) {
continue; continue;
} }
if (type != portalClosedBlock && type != portalOpenBlock) { if (!MaterialHelper.specifiersToMaterials(portalClosedMaterials).contains(type) &&
!MaterialHelper.specifiersToMaterials(portalOpenMaterials).contains(type)) {
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type); Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
return false; return false;
} }
@@ -254,20 +276,20 @@ public class Gate {
* *
* @param gateFolder <p>The folder to save the gate file in</p> * @param gateFolder <p>The folder to save the gate file in</p>
*/ */
public void save(String gateFolder) { public void save(@NotNull String gateFolder) {
try { try {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(gateFolder + filename)); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(gateFolder, filename)));
//Save main material names //Save main material names
writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name()); writeConfig(bufferedWriter, "portal-open", MaterialHelper.specifiersToString(portalOpenMaterials));
writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name()); writeConfig(bufferedWriter, "portal-closed", MaterialHelper.specifiersToString(portalClosedMaterials));
writeConfig(bufferedWriter, "button", portalButton.name()); writeConfig(bufferedWriter, "button", MaterialHelper.specifiersToString(portalButtonMaterials));
//Save the values necessary for economy //Save the values necessary for economy
saveEconomyValues(bufferedWriter); saveEconomyValues(bufferedWriter);
//Store material types to use for frame blocks //Store material types to use for frame blocks
saveFrameBlockTypes(bufferedWriter); saveFrameBlockType(bufferedWriter);
bufferedWriter.newLine(); bufferedWriter.newLine();
@@ -275,8 +297,8 @@ public class Gate {
layout.saveLayout(bufferedWriter); layout.saveLayout(bufferedWriter);
bufferedWriter.close(); bufferedWriter.close();
} catch (IOException ex) { } catch (IOException exception) {
Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, ex.getMessage())); Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, exception.getMessage()));
} }
} }
@@ -286,7 +308,7 @@ public class Gate {
* @param bufferedWriter <p>The buffered writer to write to</p> * @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p> * @throws IOException <p>If unable to write to the buffered writer</p>
*/ */
private void saveEconomyValues(BufferedWriter bufferedWriter) throws IOException { private void saveEconomyValues(@NotNull BufferedWriter bufferedWriter) throws IOException {
//Write use cost if not disabled //Write use cost if not disabled
if (useCost != -1) { if (useCost != -1) {
writeConfig(bufferedWriter, "usecost", useCost); writeConfig(bufferedWriter, "usecost", useCost);
@@ -308,26 +330,37 @@ public class Gate {
* @param bufferedWriter <p>The buffered writer to write to</p> * @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p> * @throws IOException <p>If unable to write to the buffered writer</p>
*/ */
private void saveFrameBlockTypes(BufferedWriter bufferedWriter) throws IOException { private void saveFrameBlockType(@NotNull BufferedWriter bufferedWriter) throws IOException {
for (Map.Entry<Character, Material> entry : characterMaterialMap.entrySet()) { for (Map.Entry<Character, List<MaterialSpecifier>> entry : this.characterMaterialMap.entrySet()) {
Character type = entry.getKey(); Character key = entry.getKey();
Material value = entry.getValue();
//Skip characters not part of the frame //Skip characters not part of the frame
if (type.equals(GateHandler.getAnythingCharacter()) || if (key.equals(GateHandler.getAnythingCharacter()) ||
type.equals(GateHandler.getEntranceCharacter()) || key.equals(GateHandler.getEntranceCharacter()) ||
type.equals(GateHandler.getExitCharacter())) { key.equals(GateHandler.getExitCharacter())) {
continue; continue;
} }
saveFrameBlockType(key, MaterialHelper.specifiersToString(entry.getValue()), bufferedWriter);
bufferedWriter.append(type);
bufferedWriter.append('=');
if (value != null) {
bufferedWriter.append(value.toString());
}
bufferedWriter.newLine();
} }
} }
/**
* Saves a type of block used for the gate frame/border using a buffered writer
*
* @param key <p>The character key to store</p>
* @param value <p>The string value to store</p>
* @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
private void saveFrameBlockType(@NotNull Character key, @Nullable String value,
@NotNull BufferedWriter bufferedWriter) throws IOException {
bufferedWriter.append(key.toString());
bufferedWriter.append('=');
if (value != null) {
bufferedWriter.append(value);
}
bufferedWriter.newLine();
}
/** /**
* Writes a formatted string to a buffered writer * Writes a formatted string to a buffered writer
* *
@@ -336,7 +369,8 @@ public class Gate {
* @param value <p>The config value to save</p> * @param value <p>The config value to save</p>
* @throws IOException <p>If unable to write to the buffered writer</p> * @throws IOException <p>If unable to write to the buffered writer</p>
*/ */
private void writeConfig(BufferedWriter bufferedWriter, String key, Object value) throws IOException { private void writeConfig(@NotNull BufferedWriter bufferedWriter, @NotNull String key,
@NotNull Object value) throws IOException {
//Figure out the correct formatting to use //Figure out the correct formatting to use
String format = "%s="; String format = "%s=";
if (value instanceof Boolean) { if (value instanceof Boolean) {

View File

@@ -0,0 +1,12 @@
package net.knarcraft.stargate.portal.property.gate;
/**
* The costs assigned to a gate
*
* @param useCost <p>The cost for using (entering) the gate</p>
* @param createCost <p>The cost for creating a portal with the gate type</p>
* @param destroyCost <p>The cost for destroying a portal with the gate type</p>
* @param toOwner <p>Whether the use cost is paid to the gate's owner</p>
*/
public record GateCosts(int useCost, int createCost, int destroyCost, boolean toOwner) {
}

View File

@@ -1,20 +1,26 @@
package net.knarcraft.stargate.portal.property.gate; package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.GateReader; import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
import net.knarcraft.stargate.config.material.MaterialSpecifier;
import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.MaterialHelper;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix; import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix;
import static net.knarcraft.stargate.utility.GateReader.readGateConfig; import static net.knarcraft.stargate.utility.GateReader.readGateConfig;
@@ -34,8 +40,9 @@ public class GateHandler {
private static final Material defaultPortalBlockClosed = Material.AIR; private static final Material defaultPortalBlockClosed = Material.AIR;
private static final Material defaultButton = Material.STONE_BUTTON; private static final Material defaultButton = Material.STONE_BUTTON;
private static final HashMap<String, Gate> gates = new HashMap<>(); private static final Map<String, Gate> gates = new HashMap<>();
private static final HashMap<Material, List<Gate>> controlBlocks = new HashMap<>(); private static final Map<Material, List<Gate>> controlBlocks = new HashMap<>();
private static final Map<String, List<Gate>> controlBlockTags = new HashMap<>();
private GateHandler() { private GateHandler() {
@@ -46,6 +53,7 @@ public class GateHandler {
* *
* @return <p>The character used for blocks that are not part of the gate</p> * @return <p>The character used for blocks that are not part of the gate</p>
*/ */
@NotNull
public static Character getAnythingCharacter() { public static Character getAnythingCharacter() {
return ANYTHING; return ANYTHING;
} }
@@ -55,6 +63,7 @@ public class GateHandler {
* *
* @return <p>The character used for defining the entrance</p> * @return <p>The character used for defining the entrance</p>
*/ */
@NotNull
public static Character getEntranceCharacter() { public static Character getEntranceCharacter() {
return ENTRANCE; return ENTRANCE;
} }
@@ -64,6 +73,7 @@ public class GateHandler {
* *
* @return <p>The character used for defining the exit</p> * @return <p>The character used for defining the exit</p>
*/ */
@NotNull
public static Character getExitCharacter() { public static Character getExitCharacter() {
return EXIT; return EXIT;
} }
@@ -74,6 +84,7 @@ public class GateHandler {
* *
* @return <p>The character used for defining control blocks</p> * @return <p>The character used for defining control blocks</p>
*/ */
@NotNull
public static Character getControlBlockCharacter() { public static Character getControlBlockCharacter() {
return CONTROL_BLOCK; return CONTROL_BLOCK;
} }
@@ -83,16 +94,16 @@ public class GateHandler {
* *
* @param gate <p>The gate to register</p> * @param gate <p>The gate to register</p>
*/ */
private static void registerGate(Gate gate) { private static void registerGate(@NotNull Gate gate) {
gates.put(gate.getFilename(), gate); gates.put(gate.getFilename(), gate);
Material blockID = gate.getControlBlock(); Set<Material> blockTypes = MaterialHelper.specifiersToMaterials(gate.getControlBlockMaterials());
for (Material material : blockTypes) {
if (!controlBlocks.containsKey(blockID)) { if (!controlBlocks.containsKey(material)) {
controlBlocks.put(blockID, new ArrayList<>()); controlBlocks.put(material, new ArrayList<>());
}
controlBlocks.get(material).add(gate);
} }
controlBlocks.get(blockID).add(gate);
} }
/** /**
@@ -101,7 +112,8 @@ public class GateHandler {
* @param file <p>The file containing the gate data</p> * @param file <p>The file containing the gate data</p>
* @return <p>The loaded gate, or null if unable to load the gate</p> * @return <p>The loaded gate, or null if unable to load the gate</p>
*/ */
private static Gate loadGate(File file) { @Nullable
private static Gate loadGate(@NotNull File file) {
try (Scanner scanner = new Scanner(file)) { try (Scanner scanner = new Scanner(file)) {
return loadGate(file.getName(), file.getParent(), scanner); return loadGate(file.getName(), file.getParent(), scanner);
} catch (Exception exception) { } catch (Exception exception) {
@@ -118,19 +130,20 @@ public class GateHandler {
* @param scanner <p>The scanner to use for reading the gate data</p> * @param scanner <p>The scanner to use for reading the gate data</p>
* @return <p>The loaded gate or null if unable to load the gate</p> * @return <p>The loaded gate or null if unable to load the gate</p>
*/ */
private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) { @Nullable
private static Gate loadGate(@NotNull String fileName, @NotNull String parentFolder,
@NotNull Scanner scanner) {
List<List<Character>> design = new ArrayList<>(); List<List<Character>> design = new ArrayList<>();
Map<Character, Material> characterMaterialMap = new HashMap<>(); Map<Character, List<MaterialSpecifier>> characterMaterialMap = new HashMap<>();
Map<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
Set<Material> frameTypes = new HashSet<>();
//Initialize character to material map //Initialize character to material map
characterMaterialMap.put(ENTRANCE, Material.AIR); characterMaterialMap.put(ENTRANCE, List.of(new BukkitMaterialSpecifier(Material.AIR)));
characterMaterialMap.put(EXIT, Material.AIR); characterMaterialMap.put(EXIT, List.of(new BukkitMaterialSpecifier(Material.AIR)));
characterMaterialMap.put(ANYTHING, Material.AIR); characterMaterialMap.put(ANYTHING, List.of(new BukkitMaterialSpecifier(Material.AIR)));
//Read the file into appropriate lists and maps //Read the file into appropriate lists and maps
int columns = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config); int columns = readGateFile(scanner, characterMaterialMap, fileName, design, config);
if (columns < 0) { if (columns < 0) {
return null; return null;
} }
@@ -143,7 +156,7 @@ public class GateHandler {
} }
//Update gate file in case the format has changed between versions //Update gate file in case the format has changed between versions
gate.save(parentFolder + "/"); gate.save(parentFolder);
return gate; return gate;
} }
@@ -156,23 +169,26 @@ public class GateHandler {
* @param characterMaterialMap <p>A map between layout characters and the material to use</p> * @param characterMaterialMap <p>A map between layout characters and the material to use</p>
* @return <p>A new gate, or null if the config is invalid</p> * @return <p>A new gate, or null if the config is invalid</p>
*/ */
private static Gate createGate(Map<String, String> config, String fileName, Character[][] layout, @Nullable
Map<Character, Material> characterMaterialMap) { private static Gate createGate(@NotNull Map<String, String> config, @NotNull String fileName,
@NotNull Character[][] layout,
@NotNull Map<Character, List<MaterialSpecifier>> characterMaterialMap) {
//Read relevant material types //Read relevant material types
Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen); List<MaterialSpecifier> portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); List<MaterialSpecifier> portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
Material portalButton = readGateConfig(config, fileName, "button", defaultButton); List<MaterialSpecifier> portalButton = readGateConfig(config, fileName, "button", defaultButton);
//Read economy values //Read economy values
int useCost = GateReader.readGateConfig(config, fileName, "usecost"); int useCost = readGateConfig(config, fileName, "usecost");
int createCost = GateReader.readGateConfig(config, fileName, "createcost"); int createCost = readGateConfig(config, fileName, "createcost");
int destroyCost = GateReader.readGateConfig(config, fileName, "destroycost"); int destroyCost = readGateConfig(config, fileName, "destroycost");
boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) : boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) :
Stargate.getEconomyConfig().sendPaymentToOwner()); Stargate.getEconomyConfig().sendPaymentToOwner());
GateCosts gateCosts = new GateCosts(useCost, createCost, destroyCost, toOwner);
//Create the new gate //Create the new gate
Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock, Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock,
portalButton, useCost, createCost, destroyCost, toOwner); portalButton, gateCosts);
if (!validateGate(gate, fileName)) { if (!validateGate(gate, fileName)) {
return null; return null;
@@ -187,7 +203,7 @@ public class GateHandler {
* @param fileName <p>The filename of the loaded gate file</p> * @param fileName <p>The filename of the loaded gate file</p>
* @return <p>True if the gate is valid. False otherwise</p> * @return <p>True if the gate is valid. False otherwise</p>
*/ */
private static boolean validateGate(Gate gate, String fileName) { private static boolean validateGate(@NotNull Gate gate, @NotNull String fileName) {
String failString = String.format("Could not load Gate %s", fileName) + " - %s"; String failString = String.format("Could not load Gate %s", fileName) + " - %s";
if (gate.getLayout().getControls().length != 2) { if (gate.getLayout().getControls().length != 2) {
@@ -195,23 +211,28 @@ public class GateHandler {
return false; return false;
} }
if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) { if (gate.getLayout().getExit() == null) {
Stargate.logSevere(String.format(failString, "Gates must have one specified exit point"));
return false;
}
if (checkMaterialPredicateFail(gate.getPortalButtonMaterials(), MaterialHelper::isButtonCompatible)) {
Stargate.logSevere(String.format(failString, "Gate button must be a type of button.")); Stargate.logSevere(String.format(failString, "Gate button must be a type of button."));
return false; return false;
} }
if (!gate.getPortalOpenBlock().isBlock()) { if (checkMaterialPredicateFail(gate.getPortalOpenMaterials(), Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Gate open block must be a type of block.")); Stargate.logSevere(String.format(failString, "Gate open block must be a type of block."));
return false; return false;
} }
if (!gate.getPortalClosedBlock().isBlock()) { if (checkMaterialPredicateFail(gate.getPortalClosedMaterials(), Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Gate closed block must be a type of block.")); Stargate.logSevere(String.format(failString, "Gate closed block must be a type of block."));
return false; return false;
} }
for (Material material : gate.getCharacterMaterialMap().values()) { for (List<MaterialSpecifier> materialSpecifiers : gate.getCharacterMaterialMap().values()) {
if (!material.isBlock()) { if (checkMaterialPredicateFail(materialSpecifiers, Material::isBlock)) {
Stargate.logSevere(String.format(failString, "Every gate border block must be a type of block.")); Stargate.logSevere(String.format(failString, "Every gate border block must be a type of block."));
return false; return false;
} }
@@ -220,12 +241,31 @@ public class GateHandler {
return true; return true;
} }
/**
* Checks whether a predicate is true for a list of material specifiers
*
* @param materialSpecifiers <p>The material specifiers to test</p>
* @param predicate <p>The predicate to test</p>
* @return <p>True if the predicate failed for any specified materials</p>
*/
private static boolean checkMaterialPredicateFail(@NotNull List<MaterialSpecifier> materialSpecifiers,
@NotNull Predicate<Material> predicate) {
Set<Material> closedMaterials = MaterialHelper.specifiersToMaterials(materialSpecifiers);
for (Material material : closedMaterials) {
if (!predicate.test(material)) {
return true;
}
}
return false;
}
/** /**
* Loads all gates inside the given folder * Loads all gates inside the given folder
* *
* @param gateFolder <p>The folder containing the gates</p> * @param gateFolder <p>The folder containing the gates</p>
*/ */
public static void loadGates(String gateFolder) { public static void loadGates(@NotNull String gateFolder) {
File directory = new File(gateFolder); File directory = new File(gateFolder);
File[] files; File[] files;
@@ -258,11 +298,12 @@ public class GateHandler {
* *
* @param gateFolder <p>The folder containing gate config files</p> * @param gateFolder <p>The folder containing gate config files</p>
*/ */
public static void writeDefaultGatesToFolder(String gateFolder) { public static void writeDefaultGatesToFolder(@NotNull String gateFolder) {
loadGateFromJar("nethergate.gate", gateFolder); loadGateFromJar("nethergate.gate", gateFolder);
loadGateFromJar("watergate.gate", gateFolder); loadGateFromJar("watergate.gate", gateFolder);
loadGateFromJar("endgate.gate", gateFolder); loadGateFromJar("endgate.gate", gateFolder);
loadGateFromJar("squarenetherglowstonegate.gate", gateFolder); loadGateFromJar("squarenetherglowstonegate.gate", gateFolder);
loadGateFromJar("wool.gate", gateFolder);
} }
/** /**
@@ -271,7 +312,7 @@ public class GateHandler {
* @param gateFile <p>The name of the gate file</p> * @param gateFile <p>The name of the gate file</p>
* @param gateFolder <p>The folder containing gates</p> * @param gateFolder <p>The folder containing gates</p>
*/ */
private static void loadGateFromJar(String gateFile, String gateFolder) { private static void loadGateFromJar(@NotNull String gateFile, @NotNull String gateFolder) {
//Get an input stream for the internal file //Get an input stream for the internal file
InputStream gateFileStream = Gate.class.getResourceAsStream("/gates/" + gateFile); InputStream gateFileStream = Gate.class.getResourceAsStream("/gates/" + gateFile);
if (gateFileStream != null) { if (gateFileStream != null) {
@@ -293,7 +334,8 @@ public class GateHandler {
* @param block <p>The control block to check</p> * @param block <p>The control block to check</p>
* @return <p>A list of gates using the given control block</p> * @return <p>A list of gates using the given control block</p>
*/ */
public static Gate[] getGatesByControlBlock(Block block) { @NotNull
public static List<Gate> getGatesByControlBlock(@NotNull Block block) {
return getGatesByControlBlock(block.getType()); return getGatesByControlBlock(block.getType());
} }
@@ -306,12 +348,24 @@ public class GateHandler {
* @param type <p>The type of the control block to check</p> * @param type <p>The type of the control block to check</p>
* @return <p>A list of gates using the given material for control block</p> * @return <p>A list of gates using the given material for control block</p>
*/ */
public static Gate[] getGatesByControlBlock(Material type) { @NotNull
Gate[] result = new Gate[0]; public static List<Gate> getGatesByControlBlock(@NotNull Material type) {
List<Gate> lookup = controlBlocks.get(type); List<Gate> result = new ArrayList<>();
List<Gate> fromId = controlBlocks.get(type);
List<Gate> fromTag = null;
for (String tagString : controlBlockTags.keySet()) {
Tag<Material> tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString.replaceFirst(
"minecraft:", "")), Material.class);
if (tag != null && tag.isTagged(type)) {
fromTag = controlBlockTags.get(tag.getKey().toString());
}
}
if (lookup != null) { if (fromId != null) {
result = lookup.toArray(result); result.addAll(fromId);
}
if (fromTag != null) {
result.addAll(fromTag);
} }
return result; return result;
@@ -323,7 +377,8 @@ public class GateHandler {
* @param fileName <p>The filename of the gate to get</p> * @param fileName <p>The filename of the gate to get</p>
* @return <p>The gate with the given filename</p> * @return <p>The gate with the given filename</p>
*/ */
public static Gate getGateByName(String fileName) { @Nullable
public static Gate getGateByName(@NotNull String fileName) {
return gates.get(fileName); return gates.get(fileName);
} }
@@ -342,6 +397,7 @@ public class GateHandler {
public static void clearGates() { public static void clearGates() {
gates.clear(); gates.clear();
controlBlocks.clear(); controlBlocks.clear();
controlBlockTags.clear();
} }
} }

View File

@@ -1,6 +1,8 @@
package net.knarcraft.stargate.portal.property.gate; package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.container.RelativeBlockVector; import org.bukkit.util.BlockVector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
@@ -17,18 +19,18 @@ import java.util.List;
public class GateLayout { public class GateLayout {
private final Character[][] layout; private final Character[][] layout;
private final List<RelativeBlockVector> exits = new ArrayList<>(); private final List<BlockVector> exits = new ArrayList<>();
private RelativeBlockVector[] entrances = new RelativeBlockVector[0]; private BlockVector[] entrances = new BlockVector[0];
private RelativeBlockVector[] border = new RelativeBlockVector[0]; private BlockVector[] border = new BlockVector[0];
private RelativeBlockVector[] controls = new RelativeBlockVector[0]; private BlockVector[] controls = new BlockVector[0];
private RelativeBlockVector exitBlock = null; private BlockVector exitBlock = null;
/** /**
* Instantiates a new gate layout * Instantiates a new gate layout
* *
* @param layout <p>A character matrix describing the layout</p> * @param layout <p>A character matrix describing the layout</p>
*/ */
public GateLayout(Character[][] layout) { public GateLayout(@NotNull Character[][] layout) {
this.layout = layout; this.layout = layout;
readLayout(); readLayout();
} }
@@ -38,6 +40,7 @@ public class GateLayout {
* *
* @return <p>The character array describing this layout</p> * @return <p>The character array describing this layout</p>
*/ */
@NotNull
public Character[][] getLayout() { public Character[][] getLayout() {
return this.layout; return this.layout;
} }
@@ -49,7 +52,8 @@ public class GateLayout {
* *
* @return <p>The locations of entrances for this gate</p> * @return <p>The locations of entrances for this gate</p>
*/ */
public RelativeBlockVector[] getEntrances() { @NotNull
public BlockVector[] getEntrances() {
return entrances; return entrances;
} }
@@ -61,7 +65,8 @@ public class GateLayout {
* *
* @return <p>The locations of border blocks for this gate</p> * @return <p>The locations of border blocks for this gate</p>
*/ */
public RelativeBlockVector[] getBorder() { @NotNull
public BlockVector[] getBorder() {
return border; return border;
} }
@@ -70,7 +75,8 @@ public class GateLayout {
* *
* @return <p>The exit block defined in the layout</p> * @return <p>The exit block defined in the layout</p>
*/ */
public RelativeBlockVector getExit() { @Nullable
public BlockVector getExit() {
return exitBlock; return exitBlock;
} }
@@ -82,7 +88,8 @@ public class GateLayout {
* *
* @return <p>All possible exits</p> * @return <p>All possible exits</p>
*/ */
public List<RelativeBlockVector> getExits() { @NotNull
public List<BlockVector> getExits() {
return exits; return exits;
} }
@@ -94,7 +101,8 @@ public class GateLayout {
* *
* @return <p>The locations of the control blocks for this gate</p> * @return <p>The locations of the control blocks for this gate</p>
*/ */
public RelativeBlockVector[] getControls() { @NotNull
public BlockVector[] getControls() {
return controls; return controls;
} }
@@ -104,7 +112,7 @@ public class GateLayout {
* @param bufferedWriter <p>The buffered writer to write to</p> * @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p> * @throws IOException <p>If unable to write to the buffered writer</p>
*/ */
public void saveLayout(BufferedWriter bufferedWriter) throws IOException { public void saveLayout(@NotNull BufferedWriter bufferedWriter) throws IOException {
for (Character[] line : this.layout) { for (Character[] line : this.layout) {
for (Character character : line) { for (Character character : line) {
bufferedWriter.append(character); bufferedWriter.append(character);
@@ -119,9 +127,9 @@ public class GateLayout {
* <p>This methods reads the layout and stores exits, entrances, border blocks and control blocks.</p> * <p>This methods reads the layout and stores exits, entrances, border blocks and control blocks.</p>
*/ */
private void readLayout() { private void readLayout() {
List<RelativeBlockVector> entranceList = new ArrayList<>(); List<BlockVector> entranceList = new ArrayList<>();
List<RelativeBlockVector> borderList = new ArrayList<>(); List<BlockVector> borderList = new ArrayList<>();
List<RelativeBlockVector> controlList = new ArrayList<>(); List<BlockVector> controlList = new ArrayList<>();
readLayout(controlList, entranceList, borderList); readLayout(controlList, entranceList, borderList);
@@ -137,8 +145,9 @@ public class GateLayout {
* @param entranceList <p>The list of entrances to save to</p> * @param entranceList <p>The list of entrances to save to</p>
* @param borderList <p>The list of border blocks to save to</p> * @param borderList <p>The list of border blocks to save to</p>
*/ */
private void readLayout(List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList, private void readLayout(@NotNull List<BlockVector> controlList,
List<RelativeBlockVector> borderList) { @NotNull List<BlockVector> entranceList,
@NotNull List<BlockVector> borderList) {
//Store the lowest opening for each column //Store the lowest opening for each column
int[] exitDepths = new int[layout[0].length]; int[] exitDepths = new int[layout[0].length];
@@ -157,7 +166,7 @@ public class GateLayout {
for (int x = 0; x < exitDepths.length; x++) { for (int x = 0; x < exitDepths.length; x++) {
//Ignore invalid exits //Ignore invalid exits
if (exitDepths[x] > 0) { if (exitDepths[x] > 0) {
this.exits.add(new RelativeBlockVector(x, exitDepths[x], 0)); this.exits.add(new BlockVector(x, exitDepths[x], 0));
} }
} }
} }
@@ -173,26 +182,27 @@ public class GateLayout {
* @param entranceList <p>The list of entrances to save to</p> * @param entranceList <p>The list of entrances to save to</p>
* @param borderList <p>The list of border blocks to save to</p> * @param borderList <p>The list of border blocks to save to</p>
*/ */
private void parseLayoutCharacter(Character key, int columnIndex, int rowIndex, int[] exitDepths, private void parseLayoutCharacter(@NotNull Character key, int columnIndex, int rowIndex, int[] exitDepths,
List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList, @NotNull List<BlockVector> controlList,
List<RelativeBlockVector> borderList) { @NotNull List<BlockVector> entranceList,
@NotNull List<BlockVector> borderList) {
//Add control blocks to the control block list //Add control blocks to the control block list
if (key.equals(GateHandler.getControlBlockCharacter())) { if (key.equals(GateHandler.getControlBlockCharacter())) {
controlList.add(new RelativeBlockVector(columnIndex, rowIndex, 0)); controlList.add(new BlockVector(columnIndex, rowIndex, 0));
} }
if (isOpening(key)) { if (isOpening(key)) {
//Register entrance //Register entrance
entranceList.add(new RelativeBlockVector(columnIndex, rowIndex, 0)); entranceList.add(new BlockVector(columnIndex, rowIndex, 0));
//Overwrite the lowest exit location for this column/x-coordinate //Overwrite the lowest exit location for this column/x-coordinate
exitDepths[columnIndex] = rowIndex; exitDepths[columnIndex] = rowIndex;
//Register exit if found //Register exit if found
if (key.equals(GateHandler.getExitCharacter())) { if (key.equals(GateHandler.getExitCharacter())) {
this.exitBlock = new RelativeBlockVector(columnIndex, rowIndex, 0); this.exitBlock = new BlockVector(columnIndex, rowIndex, 0);
} }
} else if (!key.equals(GateHandler.getAnythingCharacter())) { } else if (!key.equals(GateHandler.getAnythingCharacter())) {
//Register border block //Register border block
borderList.add(new RelativeBlockVector(columnIndex, rowIndex, 0)); borderList.add(new BlockVector(columnIndex, rowIndex, 0));
} }
} }
@@ -202,7 +212,7 @@ public class GateLayout {
* @param character <p>The character to check</p> * @param character <p>The character to check</p>
* @return <p>True if the character represents an opening</p> * @return <p>True if the character represents an opening</p>
*/ */
private boolean isOpening(Character character) { private boolean isOpening(@NotNull Character character) {
return character.equals(GateHandler.getEntranceCharacter()) || character.equals(GateHandler.getExitCharacter()); return character.equals(GateHandler.getEntranceCharacter()) || character.equals(GateHandler.getExitCharacter());
} }

View File

@@ -1,10 +1,9 @@
package net.knarcraft.stargate.portal.teleporter; package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateEntityPortalEvent; import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
/** /**
* The portal teleporter takes care of the actual portal teleportation for any entities * The portal teleporter takes care of the actual portal teleportation for any entities
@@ -16,10 +15,10 @@ public class EntityTeleporter extends Teleporter {
/** /**
* Instantiates a new portal teleporter * Instantiates a new portal teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The portal which is the target of the teleportation</p>
*/ */
public EntityTeleporter(Portal portal, Entity teleportingEntity) { public EntityTeleporter(@NotNull Portal targetPortal, @NotNull Entity teleportingEntity) {
super(portal); super(targetPortal, teleportingEntity);
this.teleportingEntity = teleportingEntity; this.teleportingEntity = teleportingEntity;
} }
@@ -29,44 +28,8 @@ public class EntityTeleporter extends Teleporter {
* @param origin <p>The portal the entity is teleporting from</p> * @param origin <p>The portal the entity is teleporting from</p>
* @return <p>True if the entity was teleported. False otherwise</p> * @return <p>True if the entity was teleported. False otherwise</p>
*/ */
public boolean teleport(Portal origin) { public boolean teleportEntity(@NotNull Portal origin) {
Location traveller = teleportingEntity.getLocation(); return teleport(origin, new StargateEntityPortalEvent(teleportingEntity, origin, portal, exit));
Location exit = getExit(teleportingEntity, traveller);
//Rotate the entity to face out from the portal
adjustRotation(exit);
//Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
exit = triggerEntityPortalEvent(origin, exit);
if (exit == null) {
return false;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
teleportingEntity.teleport(exit);
return true;
} }
/**
* Triggers the entity portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param exit <p>The exit location to teleport the entity to</p>
* @return <p>The location the entity should be teleported to, or null if the event was cancelled</p>
*/
protected Location triggerEntityPortalEvent(Portal origin, Location exit) {
StargateEntityPortalEvent stargateEntityPortalEvent = new StargateEntityPortalEvent(teleportingEntity, origin,
portal, exit);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargateEntityPortalEvent);
//Teleport is cancelled. Teleport the entity back to where it came from just for sanity's sake
if (stargateEntityPortalEvent.isCancelled()) {
new EntityTeleporter(origin, teleportingEntity).teleport(origin);
return null;
}
return stargateEntityPortalEvent.getExit();
}
} }

View File

@@ -4,11 +4,16 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargatePlayerPortalEvent; import net.knarcraft.stargate.event.StargatePlayerPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/** /**
* The portal teleporter takes care of the actual portal teleportation for any players * The portal teleporter takes care of the actual portal teleportation for any players
@@ -20,11 +25,11 @@ public class PlayerTeleporter extends Teleporter {
/** /**
* Instantiates a new player teleporter * Instantiates a new player teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The portal which is the target of the teleportation</p>
* @param player <p>The teleporting player</p> * @param player <p>The teleporting player</p>
*/ */
public PlayerTeleporter(Portal portal, Player player) { public PlayerTeleporter(@NotNull Portal targetPortal, @NotNull Player player) {
super(portal); super(targetPortal, player);
this.player = player; this.player = player;
} }
@@ -34,27 +39,31 @@ public class PlayerTeleporter extends Teleporter {
* @param origin <p>The portal the player teleports from</p> * @param origin <p>The portal the player teleports from</p>
* @param event <p>The player move event triggering the event</p> * @param event <p>The player move event triggering the event</p>
*/ */
public void teleport(Portal origin, PlayerMoveEvent event) { public void teleportPlayer(@NotNull Portal origin, @Nullable PlayerMoveEvent event) {
double velocity = player.getVelocity().length(); double velocity = player.getVelocity().length();
Location traveller = player.getLocation(); List<Entity> passengers = player.getPassengers();
Location exit = getExit(player, traveller);
//Rotate the player to face out from the portal
adjustRotation(exit);
//Call the StargatePlayerPortalEvent to allow plugins to change destination //Call the StargatePlayerPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) { if (!origin.equals(portal)) {
exit = triggerPlayerPortalEvent(origin, exit, event); exit = triggerPortalEvent(origin, new StargatePlayerPortalEvent(player, origin, portal, exit));
if (exit == null) { if (exit == null) {
return; return;
} }
} }
//Calculate the exit velocity of the player
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getLocation().getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity * Stargate.getGateConfig().getExitVelocity());
//Load chunks to make sure not to teleport to the void //Load chunks to make sure not to teleport to the void
loadChunks(); loadChunks();
//Teleport any creatures leashed by the player in a 15-block range //Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin); TeleportHelper.teleportLeashedCreatures(player, origin, portal);
if (player.eject()) {
TeleportHelper.handleEntityPassengers(passengers, player, origin, portal, exit.getDirection(), newVelocity);
}
//If no event is passed in, assume it's a teleport, and act as such //If no event is passed in, assume it's a teleport, and act as such
if (event == null) { if (event == null) {
@@ -65,30 +74,7 @@ public class PlayerTeleporter extends Teleporter {
} }
//Set the velocity of the teleported player after the teleportation is finished //Set the velocity of the teleported player after the teleportation is finished
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> { Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> player.setVelocity(newVelocity), 1);
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity * Stargate.getGateConfig().getExitVelocity());
player.setVelocity(newVelocity);
}, 1);
}
/**
* Triggers the player portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param exit <p>The exit location to teleport the player to</p>
* @param event <p>The player move event which triggered the teleportation</p>
* @return <p>The location the player should be teleported to, or null if the event was cancelled</p>
*/
private Location triggerPlayerPortalEvent(Portal origin, Location exit, PlayerMoveEvent event) {
StargatePlayerPortalEvent stargatePlayerPortalEvent = new StargatePlayerPortalEvent(player, origin, portal, exit);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargatePlayerPortalEvent);
//Teleport is cancelled. Teleport the player back to where it came from
if (stargatePlayerPortalEvent.isCancelled()) {
new PlayerTeleporter(origin, player).teleport(origin, event);
return null;
}
return stargatePlayerPortalEvent.getExit();
} }
} }

View File

@@ -1,23 +1,30 @@
package net.knarcraft.stargate.portal.teleporter; package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.event.StargateTeleportEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.transformation.SimpleVectorOperation;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EntityHelper; import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Bisected; import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Slab; import org.bukkit.block.data.type.Slab;
import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.event.Event;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
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;
@@ -31,71 +38,117 @@ public abstract class Teleporter {
* The portal the entity is teleporting to * The portal the entity is teleporting to
*/ */
protected final Portal portal; protected final Portal portal;
/** /**
* The scheduler to use for delaying tasks * The scheduler to use for delaying tasks
*/ */
protected final BukkitScheduler scheduler; protected final BukkitScheduler scheduler;
/**
* The exit location any entities will be teleported to
*/
protected Location exit;
/**
* The entity being teleported by this teleporter
*/
protected final Entity teleportedEntity;
/** /**
* Instantiates a new portal teleporter * Instantiates a new portal teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param portal <p>The portal which is the target of the teleportation</p>
* @param teleportedEntity <p>The entity teleported by this teleporter</p>
*/ */
public Teleporter(Portal portal) { protected Teleporter(@NotNull Portal portal, @NotNull Entity teleportedEntity) {
this.portal = portal; this.portal = portal;
this.scheduler = Stargate.getInstance().getServer().getScheduler(); this.scheduler = Stargate.getInstance().getServer().getScheduler();
this.teleportedEntity = teleportedEntity;
this.exit = getExit(teleportedEntity);
} }
/**
* Teleports an entity
*
* @param origin <p>The portal the entity teleported from</p>
* @param stargateTeleportEvent <p>The event to call to make sure the teleportation is valid</p>
* @return <p>True if the teleportation was successfully performed</p>
*/
public boolean teleport(@NotNull Portal origin, @Nullable StargateTeleportEvent stargateTeleportEvent) {
List<Entity> passengers = teleportedEntity.getPassengers();
//Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal) && stargateTeleportEvent != null) {
exit = triggerPortalEvent(origin, stargateTeleportEvent);
if (exit == null) {
return false;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
if (teleportedEntity.eject()) {
TeleportHelper.handleEntityPassengers(passengers, teleportedEntity, origin, portal, exit.getDirection(),
new Vector());
}
teleportedEntity.teleport(exit);
return true;
}
/**
* Gets the exit location of this teleporter
*
* @return <p>The exit location of this teleporter</p>
*/
@NotNull
public Location getExit() {
return exit.clone();
}
/**
* Triggers the entity portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param stargateTeleportEvent <p>The exit location to teleport the entity to</p>
* @return <p>The location the entity should be teleported to, or null if the event was cancelled</p>
*/
@Nullable
protected Location triggerPortalEvent(@NotNull Portal origin,
@NotNull StargateTeleportEvent stargateTeleportEvent) {
Stargate.getInstance().getServer().getPluginManager().callEvent((Event) stargateTeleportEvent);
//Teleport is cancelled. Teleport the entity back to where it came from just for sanity's sake
if (stargateTeleportEvent.isCancelled()) {
new EntityTeleporter(origin, teleportedEntity).teleportEntity(origin);
return null;
}
return stargateTeleportEvent.getExit();
}
/** /**
* Adjusts the rotation of the exit to make the teleporting entity face directly out from the portal * Adjusts the rotation of the exit to make the teleporting entity face directly out from the portal
* *
* @param exit <p>The location the entity will exit from</p> * @param exit <p>The location the entity will exit from</p>
*/ */
protected void adjustRotation(Location exit) { protected void adjustExitLocationRotation(@NotNull Location exit) {
int adjust = 0; int adjust = 0;
if (portal.getOptions().isBackwards()) { if (portal.getOptions().isBackwards()) {
adjust = 180; adjust = 180;
} }
float newYaw = (portal.getYaw() + adjust) % 360; float newYaw = (portal.getLocation().getYaw() + adjust) % 360;
Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw); Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw);
exit.setDirection(DirectionHelper.getDirectionVectorFromYaw(newYaw)); exit.setDirection(DirectionHelper.getDirectionVectorFromYaw(newYaw));
} }
/** /**
* Gets the exit location for a given entity and current location * Loads the chunks outside the portal's entrance
*
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
* @param traveller <p>The location of the entity travelling</p>
* @return <p>The location the entity should be teleported to.</p>
*/ */
public Location getExit(Entity entity, Location traveller) { protected void loadChunks() {
Location exitLocation = null; for (Chunk chunk : getChunksToLoad()) {
RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit(); chunk.addPluginChunkTicket(Stargate.getInstance());
if (relativeExit != null) { //Allow the chunk to unload after 10 seconds
BlockLocation exit = portal.getBlockAt(relativeExit); Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L));
//Move one block out to prevent exiting inside the portal
float portalYaw = portal.getYaw();
if (portal.getOptions().isBackwards()) {
portalYaw += 180;
}
exitLocation = exit.getRelativeLocation(0D, 0D, 1, portalYaw);
if (entity != null) {
double entitySize = EntityHelper.getEntityMaxSize(entity);
//Prevent exit suffocation for players riding horses or similar
if (entitySize > 1) {
exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity);
}
}
} else {
Stargate.logWarning(String.format("Missing destination point in .gate file %s",
portal.getGate().getFilename()));
} }
//Adjust pitch and height
return adjustExitLocation(traveller, exitLocation);
} }
/** /**
@@ -106,7 +159,9 @@ public abstract class Teleporter {
* @param entity <p>The travelling entity</p> * @param entity <p>The travelling entity</p>
* @return <p>A location which won't suffocate the entity inside the portal</p> * @return <p>A location which won't suffocate the entity inside the portal</p>
*/ */
private Location preventExitSuffocation(RelativeBlockVector relativeExit, Location exitLocation, Entity entity) { @NotNull
private Location preventExitSuffocation(@NotNull RelativeBlockVector relativeExit,
@NotNull Location exitLocation, @NotNull Entity entity) {
//Go left to find start of opening //Go left to find start of opening
RelativeBlockVector openingLeft = getPortalExitEdge(relativeExit, -1); RelativeBlockVector openingLeft = getPortalExitEdge(relativeExit, -1);
@@ -114,15 +169,15 @@ public abstract class Teleporter {
RelativeBlockVector openingRight = getPortalExitEdge(relativeExit, 1); RelativeBlockVector openingRight = getPortalExitEdge(relativeExit, 1);
//Get the width to check if the entity fits //Get the width to check if the entity fits
int openingWidth = openingRight.getRight() - openingLeft.getRight() + 1; int openingWidth = openingRight.right() - openingLeft.right() + 1;
int existingOffset = relativeExit.getRight() - openingLeft.getRight(); int existingOffset = relativeExit.right() - openingLeft.right();
double newOffset = (openingWidth - existingOffset) / 2D; double newOffset = (openingWidth - existingOffset) / 2D;
//Remove the half offset for better centering //Remove the half offset for better centering
if (openingWidth > 1) { if (openingWidth > 1) {
newOffset -= 0.5; newOffset -= 0.5;
} }
exitLocation = DirectionHelper.moveLocation(exitLocation, newOffset, 0, 0, portal.getYaw()); exitLocation = DirectionHelper.moveLocation(exitLocation, newOffset, 0, 0, portal.getLocation().getYaw());
//Move large entities further from the portal //Move large entities further from the portal
return moveExitLocationOutwards(exitLocation, entity); return moveExitLocationOutwards(exitLocation, entity);
@@ -135,7 +190,8 @@ public abstract class Teleporter {
* @param entity <p>The entity to adjust the exit location for</p> * @param entity <p>The entity to adjust the exit location for</p>
* @return <p>The adjusted exit location</p> * @return <p>The adjusted exit location</p>
*/ */
private Location moveExitLocationOutwards(Location exitLocation, Entity entity) { @NotNull
private Location moveExitLocationOutwards(@NotNull Location exitLocation, @NotNull Entity entity) {
double entitySize = EntityHelper.getEntityMaxSize(entity); double entitySize = EntityHelper.getEntityMaxSize(entity);
int entityBoxSize = EntityHelper.getEntityMaxSizeInt(entity); int entityBoxSize = EntityHelper.getEntityMaxSizeInt(entity);
if (entitySize > 1) { if (entitySize > 1) {
@@ -150,7 +206,7 @@ public abstract class Teleporter {
if (entity instanceof AbstractHorse) { if (entity instanceof AbstractHorse) {
entityOffset += 1; entityOffset += 1;
} }
exitLocation = DirectionHelper.moveLocation(exitLocation, 0, 0, entityOffset, portal.getYaw()); exitLocation = DirectionHelper.moveLocation(exitLocation, 0, 0, entityOffset, portal.getLocation().getYaw());
} }
return exitLocation; return exitLocation;
} }
@@ -162,12 +218,13 @@ public abstract class Teleporter {
* @param direction <p>The direction to move (+1 for right, -1 for left)</p> * @param direction <p>The direction to move (+1 for right, -1 for left)</p>
* @return <p>The right or left edge of the opening</p> * @return <p>The right or left edge of the opening</p>
*/ */
private RelativeBlockVector getPortalExitEdge(RelativeBlockVector relativeExit, int direction) { @NotNull
private RelativeBlockVector getPortalExitEdge(@NotNull RelativeBlockVector relativeExit, int direction) {
RelativeBlockVector openingEdge = relativeExit; RelativeBlockVector openingEdge = relativeExit;
do { do {
RelativeBlockVector possibleOpening = new RelativeBlockVector(openingEdge.getRight() + direction, RelativeBlockVector possibleOpening = new RelativeBlockVector(openingEdge.right() + direction,
openingEdge.getDown(), openingEdge.getOut()); openingEdge.down(), openingEdge.out());
if (portal.getGate().getLayout().getExits().contains(possibleOpening)) { if (portal.getGate().getLayout().getExits().contains(possibleOpening)) {
openingEdge = possibleOpening; openingEdge = possibleOpening;
} else { } else {
@@ -185,41 +242,64 @@ public abstract class Teleporter {
* slab check is necessary to prevent the player from clipping through the slab and spawning beneath it. The water * slab check is necessary to prevent the player from clipping through the slab and spawning beneath it. The water
* check is necessary when teleporting boats to prevent it from becoming a submarine.</p> * check is necessary when teleporting boats to prevent it from becoming a submarine.</p>
* *
* @param traveller <p>The location of the travelling entity</p> * @param entity <p>The travelling entity</p>
* @param exitLocation <p>The exit location generated</p> * @param exitLocation <p>The exit location generated</p>
* @return <p>The location the travelling entity should be teleported to</p> * @return <p>The location the travelling entity should be teleported to</p>
*/ */
private Location adjustExitLocation(Location traveller, Location exitLocation) { @NotNull
private Location adjustExitLocationHeight(@NotNull Entity entity, @Nullable Location exitLocation) {
if (exitLocation != null) { if (exitLocation != null) {
BlockData blockData = exitLocation.getBlock().getBlockData(); BlockData blockData = exitLocation.getBlock().getBlockData();
if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) || if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM)) { (blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM) ||
//Prevent traveller from spawning inside a slab blockData.getMaterial() == Material.WATER) {
Stargate.debug("adjustExitLocation", "Added a block to get above a slab"); //Prevent traveller from spawning inside a slab, or a boat from spawning inside water
exitLocation.add(0, 1, 0); Stargate.debug("adjustExitLocation", "Added a block to get above a slab or a block of water");
} else if (blockData.getMaterial() == Material.WATER) {
//If there's water outside, go one up to allow for boat teleportation
Stargate.debug("adjustExitLocation", "Added a block to get above a block of water");
exitLocation.add(0, 1, 0); exitLocation.add(0, 1, 0);
} }
exitLocation.setPitch(traveller.getPitch());
return exitLocation; return exitLocation;
} else { } else {
Stargate.logWarning("Unable to generate exit location"); Stargate.logWarning("Unable to generate exit location");
return traveller; return entity.getLocation();
} }
} }
/** /**
* Loads the chunks outside the portal's entrance * Gets the exit location for a given entity and current location
*
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
* @return <p>The location the entity should be teleported to.</p>
*/ */
protected void loadChunks() { @NotNull
for (Chunk chunk : getChunksToLoad()) { private Location getExit(@NotNull Entity entity) {
chunk.addPluginChunkTicket(Stargate.getInstance()); Block exitLocation = null;
//Allow the chunk to unload after 10 seconds BlockVector relativeExit = portal.getGate().getLayout().getExit();
Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L)); if (relativeExit != null) {
Block exit = portal.getBlockAt(relativeExit);
//Move one block out to prevent exiting inside the portal
BlockFace facing = portal.getLocation().getFacing();
if (portal.getOptions().isBackwards()) {
facing = facing.getOppositeFace();
}
SimpleVectorOperation operation = new SimpleVectorOperation(facing);
exitLocation = operation.getRealLocationBlock(portal.getLocation().getTopLeft(),
new BlockVector(0, 0, 1));
double entitySize = EntityHelper.getEntityMaxSize(entity);
//Prevent exit suffocation for players riding horses or similar
if (entitySize > 1) {
exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity);
}
} else {
Stargate.logWarning(String.format("Missing destination point in .gate file %s",
portal.getGate().getFilename()));
} }
//Adjust height and rotation
Location adjusted = adjustExitLocationHeight(entity, exitLocation);
adjustExitLocationRotation(adjusted);
return adjusted;
} }
/** /**
@@ -227,10 +307,11 @@ public abstract class Teleporter {
* *
* @return <p>A list of chunks to load</p> * @return <p>A list of chunks to load</p>
*/ */
@NotNull
private List<Chunk> getChunksToLoad() { private List<Chunk> getChunksToLoad() {
List<Chunk> chunksToLoad = new ArrayList<>(); List<Chunk> chunksToLoad = new ArrayList<>();
for (RelativeBlockVector vector : portal.getGate().getLayout().getEntrances()) { for (BlockVector vector : portal.getGate().getLayout().getEntrances()) {
BlockLocation entranceLocation = portal.getBlockAt(vector); Block entranceLocation = portal.getBlockAt(vector);
Chunk chunk = entranceLocation.getChunk(); Chunk chunk = entranceLocation.getChunk();
//Make sure not to load chunks twice //Make sure not to load chunks twice
if (!chunksToLoad.contains(chunk)) { if (!chunksToLoad.contains(chunk)) {
@@ -239,8 +320,7 @@ public abstract class Teleporter {
//Get the chunk in front of the gate entrance //Get the chunk in front of the gate entrance
int blockOffset = portal.getOptions().isBackwards() ? -5 : 5; int blockOffset = portal.getOptions().isBackwards() ? -5 : 5;
Location fiveBlocksForward = DirectionHelper.moveLocation(entranceLocation, 0, 0, blockOffset, Block fiveBlocksForward = entranceLocation.getRelative(portal.getLocation().getFacing(), blockOffset);
portal.getYaw());
//Load the chunk five blocks forward to make sure the teleported entity will never spawn in unloaded chunks //Load the chunk five blocks forward to make sure the teleported entity will never spawn in unloaded chunks
Chunk forwardChunk = fiveBlocksForward.getChunk(); Chunk forwardChunk = fiveBlocksForward.getChunk();
if (!chunksToLoad.contains(forwardChunk)) { if (!chunksToLoad.contains(forwardChunk)) {
@@ -250,76 +330,4 @@ public abstract class Teleporter {
return chunksToLoad; return chunksToLoad;
} }
/**
* Checks whether a player has leashed creatures that block the teleportation
*
* @param player <p>The player trying to teleport</p>
* @return <p>False if the player has leashed any creatures that cannot go through the portal</p>
*/
public static boolean noLeashedCreaturesPreventTeleportation(Player player) {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyCreatures = getLeashedCreatures(player);
//Disallow creatures with passengers to prevent smuggling
for (Creature creature : nearbyCreatures) {
if (!creature.getPassengers().isEmpty()) {
return false;
}
}
//If it's enabled, there is no problem
if (Stargate.getGateConfig().handleLeashedCreatures()) {
return true;
} else {
return nearbyCreatures.isEmpty();
}
}
/**
* Teleports any creatures leashed by the player
*
* <p>Will return false if the teleportation should be aborted because the player has leashed creatures that
* aren't allowed to be teleported with the player.</p>
*
* @param player <p>The player which is teleported</p>
* @param origin <p>The portal the player is teleporting from</p>
*/
protected void teleportLeashedCreatures(Player player, Portal origin) {
//If this feature is disabled, just return
if (!Stargate.getGateConfig().handleLeashedCreatures()) {
return;
}
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyEntities = getLeashedCreatures(player);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Creature creature : nearbyEntities) {
creature.setLeashHolder(null);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
new EntityTeleporter(portal, creature).teleport(origin);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player), 6);
}, 2);
}
}
/**
* Gets all creatures leashed by a player within the given range
*
* @param player <p>The player to check</p>
* @return <p>A list of all creatures the player is holding in a leash (lead)</p>
*/
protected static List<Creature> getLeashedCreatures(Player player) {
List<Creature> leashedCreatures = new ArrayList<>();
//Find any nearby leashed entities to teleport with the player
List<Entity> nearbyEntities = player.getNearbyEntities(15, 15, 15);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Entity entity : nearbyEntities) {
if (entity instanceof Creature creature && creature.isLeashed() && creature.getLeashHolder() == player) {
leashedCreatures.add(creature);
}
}
return leashedCreatures;
}
} }

View File

@@ -2,17 +2,22 @@ package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateGateConfig; import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle; import org.bukkit.entity.Vehicle;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* The portal teleporter takes care of the actual portal teleportation for any vehicles * The portal teleporter takes care of the actual portal teleportation for any vehicles
@@ -24,11 +29,11 @@ public class VehicleTeleporter extends EntityTeleporter {
/** /**
* Instantiates a new vehicle teleporter * Instantiates a new vehicle teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The targetPortal which is the target of the teleportation</p>
* @param teleportingVehicle <p>The teleporting vehicle</p> * @param teleportingVehicle <p>The teleporting vehicle</p>
*/ */
public VehicleTeleporter(Portal portal, Vehicle teleportingVehicle) { public VehicleTeleporter(@NotNull Portal targetPortal, @NotNull Vehicle teleportingVehicle) {
super(portal, teleportingVehicle); super(targetPortal, teleportingVehicle);
this.teleportingVehicle = teleportingVehicle; this.teleportingVehicle = teleportingVehicle;
} }
@@ -42,28 +47,22 @@ public class VehicleTeleporter extends EntityTeleporter {
* @return <p>True if the vehicle was teleported. False otherwise</p> * @return <p>True if the vehicle was teleported. False otherwise</p>
*/ */
@Override @Override
public boolean teleport(Portal origin) { public boolean teleportEntity(@NotNull Portal origin) {
Location traveller = teleportingVehicle.getLocation(); Stargate.debug("VehicleTeleporter::teleport", "Preparing to teleport: " + teleportingVehicle);
Location exit = getExit(teleportingVehicle, traveller);
double velocity = teleportingVehicle.getVelocity().length(); double velocity = teleportingVehicle.getVelocity().length();
//Stop and teleport //Stop the vehicle before teleporting
teleportingVehicle.setVelocity(new Vector()); teleportingVehicle.setVelocity(new Vector());
//Get new velocity //Get new velocity
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw()); Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getLocation().getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity); Vector newVelocity = newVelocityDirection.multiply(velocity);
//Make sure the vehicle points out from the portal
adjustRotation(exit);
//Call the StargateEntityPortalEvent to allow plugins to change destination //Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) { exit = triggerPortalEvent(origin, new StargateEntityPortalEvent(teleportingVehicle, origin, portal, exit));
exit = triggerEntityPortalEvent(origin, exit); if (exit == null) {
if (exit == null) { return false;
return false;
}
} }
//Teleport the vehicle //Teleport the vehicle
@@ -78,7 +77,7 @@ public class VehicleTeleporter extends EntityTeleporter {
* @param origin <p>The portal the vehicle teleported from</p> * @param origin <p>The portal the vehicle teleported from</p>
* @return <p>True if the vehicle was teleported. False otherwise</p> * @return <p>True if the vehicle was teleported. False otherwise</p>
*/ */
private boolean teleportVehicle(Location exit, Vector newVelocity, Portal origin) { private boolean teleportVehicle(@NotNull Location exit, @NotNull Vector newVelocity, @NotNull Portal origin) {
//Load chunks to make sure not to teleport to the void //Load chunks to make sure not to teleport to the void
loadChunks(); loadChunks();
@@ -89,12 +88,13 @@ public class VehicleTeleporter extends EntityTeleporter {
return false; return false;
} }
if (!(teleportingVehicle instanceof LivingEntity)) { if (!(teleportingVehicle instanceof LivingEntity) &&
Stargate.getGateConfig().enableCraftBookRemoveOnEjectFix()) {
//Teleport a normal vehicle with passengers (minecart or boat) //Teleport a normal vehicle with passengers (minecart or boat)
putPassengersInNewVehicle(passengers, exit, newVelocity, origin); putPassengersInNewVehicle(passengers, exit, newVelocity, origin);
} else { } else {
//Teleport a living vehicle with passengers (pig, horse, donkey, strider) //Teleport a living vehicle with passengers (pig, horse, donkey, strider)
teleportLivingVehicle(exit, passengers, origin); teleportVehicle(passengers, exit, newVelocity, origin);
} }
} else { } else {
//Check if teleportation of empty vehicles is enabled //Check if teleportation of empty vehicles is enabled
@@ -115,57 +115,39 @@ public class VehicleTeleporter extends EntityTeleporter {
* @param passengers <p>The passengers to teleport</p> * @param passengers <p>The passengers to teleport</p>
* @return <p>True if the passengers are allowed to teleport</p> * @return <p>True if the passengers are allowed to teleport</p>
*/ */
private boolean vehiclePassengersAllowed(List<Entity> passengers) { private boolean vehiclePassengersAllowed(@NotNull List<Entity> passengers) {
StargateGateConfig config = Stargate.getGateConfig(); StargateGateConfig config = Stargate.getGateConfig();
//Don't teleport if the vehicle contains a creature and creature transportation is disabled //Don't teleport if the vehicle contains a creature and creature transportation is disabled
if (containsNonPlayer(passengers) && !config.handleCreatureTransportation()) { if (TeleportHelper.containsNonPlayer(passengers) && !config.handleCreatureTransportation()) {
return false; return false;
} }
//Don't teleport if the player does not contain a player and non-player vehicles is disabled //Don't teleport if the player does not contain a player and non-player vehicles is disabled
return containsPlayer(passengers) || config.handleNonPlayerVehicles(); return TeleportHelper.containsPlayer(passengers) || config.handleNonPlayerVehicles();
}
/**
* Checks whether a list of entities contains any non-players
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one entity is not a player</p>
*/
private boolean containsNonPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (!(entity instanceof Player)) {
return true;
}
}
return false;
}
/**
* Checks whether a list of entities contains at least one player
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one player is present among the passengers</p>
*/
private boolean containsPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (entity instanceof Player) {
return true;
}
}
return false;
} }
/** /**
* Teleport a vehicle which is not a minecart or a boat * Teleport a vehicle which is not a minecart or a boat
* *
* @param exit <p>The location the vehicle will exit</p> * @param passengers <p>The passengers of the vehicle</p>
* @param passengers <p>The passengers of the vehicle</p> * @param exit <p>The location the vehicle will exit</p>
* @param origin <p>The portal the vehicle teleported from</p> * @param newVelocity <p>The new velocity of the teleported vehicle</p>
* @param origin <p>The portal the vehicle teleported from</p>
*/ */
private void teleportLivingVehicle(Location exit, List<Entity> passengers, Portal origin) { private void teleportVehicle(@NotNull List<Entity> passengers, @NotNull Location exit, @NotNull Vector newVelocity,
teleportingVehicle.eject(); @NotNull Portal origin) {
teleportingVehicle.teleport(exit); if (teleportingVehicle.eject()) {
handleVehiclePassengers(passengers, teleportingVehicle, 2, origin, exit.getDirection()); TeleportHelper.handleEntityPassengers(passengers, teleportingVehicle, origin, portal, exit.getDirection(),
newVelocity);
}
Stargate.debug("VehicleTeleporter::teleportVehicle", "Teleporting " + teleportingVehicle +
" to final location " + exit + " with direction " + exit.getDirection());
teleportingVehicle.teleport(exit, PlayerTeleportEvent.TeleportCause.PLUGIN);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(),
() -> {
Stargate.debug("VehicleTeleporter::teleportVehicle", "Setting velocity " + newVelocity +
" for vehicle " + teleportingVehicle);
teleportingVehicle.setVelocity(newVelocity);
}, 1);
} }
/** /**
@@ -180,63 +162,26 @@ public class VehicleTeleporter extends EntityTeleporter {
* @param newVelocity <p>The new velocity of the new vehicle</p> * @param newVelocity <p>The new velocity of the new vehicle</p>
* @param origin <p>The portal the vehicle teleported from</p> * @param origin <p>The portal the vehicle teleported from</p>
*/ */
private void putPassengersInNewVehicle(List<Entity> passengers, Location exit, private void putPassengersInNewVehicle(@NotNull List<Entity> passengers, @NotNull Location exit,
Vector newVelocity, Portal origin) { @NotNull Vector newVelocity, Portal origin) {
World vehicleWorld = exit.getWorld(); World vehicleWorld = exit.getWorld();
if (vehicleWorld == null) { if (vehicleWorld == null) {
Stargate.logWarning("Unable to get the world to teleport the vehicle to"); Stargate.logWarning("Unable to get the world to teleport the vehicle to");
return; return;
} }
//Spawn a new vehicle //Spawn a new vehicle
Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass()); Vehicle newVehicle = (Vehicle) vehicleWorld.spawn(exit,
Objects.requireNonNull(teleportingVehicle.getType().getEntityClass()));
if (teleportingVehicle instanceof Boat boat) {
boat.setBoatType(boat.getBoatType());
}
//Remove the old vehicle //Remove the old vehicle
teleportingVehicle.eject(); if (teleportingVehicle.eject()) {
TeleportHelper.handleEntityPassengers(passengers, newVehicle, origin, portal, exit.getDirection(),
newVelocity);
}
teleportingVehicle.remove(); teleportingVehicle.remove();
//Set rotation, add passengers and restore velocity
newVehicle.setRotation(exit.getYaw(), exit.getPitch());
handleVehiclePassengers(passengers, newVehicle, 1, origin, exit.getDirection());
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1); scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1);
} }
/**
* Ejects, teleports and adds all passengers to the target vehicle
*
* @param passengers <p>The passengers to handle</p>
* @param vehicle <p>The vehicle the passengers should be put into</p>
* @param delay <p>The amount of milliseconds to wait before adding the vehicle passengers</p>
* @param origin <p>The portal the vehicle teleported from</p>
* @param exitRotation <p>The rotation of any passengers exiting the stargate</p>
*/
private void handleVehiclePassengers(List<Entity> passengers, Vehicle vehicle, long delay, Portal origin, Vector exitRotation) {
for (Entity passenger : passengers) {
passenger.eject();
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
if (passenger instanceof Player player) {
//Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin);
}
teleportAndAddPassenger(vehicle, passenger, exitRotation);
}, delay);
}
}
/**
* Teleports and adds a passenger to a vehicle
*
* <p>Teleportation of living vehicles is really buggy if you wait between the teleportation and passenger adding,
* but there needs to be a delay between teleporting the vehicle and teleporting and adding the passenger.</p>
*
* @param targetVehicle <p>The vehicle to add the passenger to</p>
* @param passenger <p>The passenger to teleport and add</p>
* @param exitDirection <p>The direction of any passengers exiting the stargate</p>
*/
private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger, Vector exitDirection) {
if (!passenger.teleport(targetVehicle.getLocation().clone().setDirection(exitDirection))) {
Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger");
}
if (!targetVehicle.addPassenger(passenger)) {
Stargate.debug("handleVehiclePassengers", "Failed to add passenger");
}
}
} }

View File

@@ -8,11 +8,12 @@ import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.EndGateway; import org.bukkit.block.EndGateway;
import org.bukkit.block.data.Orientable; import org.bukkit.block.data.Orientable;
import org.jetbrains.annotations.NotNull;
/** /**
* This thread changes gate blocks to display a gate as open or closed * This thread changes gate blocks to display a gate as open or closed
* *
* <p>This thread fetches some entries from blockPopulateQueue each time it's called.</p> * <p>This thread fetches some entries from blockChangeRequestQueue each time it's called.</p>
*/ */
public class BlockChangeThread implements Runnable { public class BlockChangeThread implements Runnable {
@@ -21,18 +22,22 @@ public class BlockChangeThread implements Runnable {
long sTime = System.nanoTime(); long sTime = System.nanoTime();
//Repeat for at most 0.025 seconds //Repeat for at most 0.025 seconds
while (System.nanoTime() - sTime < 25000000) { while (System.nanoTime() - sTime < 25000000) {
pollQueue(); if (pollQueue()) {
break;
}
} }
} }
/** /**
* Polls the block change request queue for any waiting requests * Polls the block change request queue for any waiting requests
*
* @return <p>True if the queue is empty and it's safe to quit</p>
*/ */
public static void pollQueue() { public static boolean pollQueue() {
//Abort if there's no work to be done //Abort if there's no work to be done
BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll(); BlockChangeRequest blockChangeRequest = Stargate.getControlBlockUpdateRequestQueue().poll();
if (blockChangeRequest == null) { if (blockChangeRequest == null) {
return; return true;
} }
//Change the material of the pulled block //Change the material of the pulled block
@@ -46,6 +51,7 @@ public class BlockChangeThread implements Runnable {
//If orientation is relevant, adjust the block's orientation //If orientation is relevant, adjust the block's orientation
orientBlock(block, blockChangeRequest.getAxis()); orientBlock(block, blockChangeRequest.getAxis());
} }
return false;
} }
/** /**
@@ -53,7 +59,7 @@ public class BlockChangeThread implements Runnable {
* *
* @param block <p>The block to fix</p> * @param block <p>The block to fix</p>
*/ */
private static void fixEndGatewayGate(Block block) { private static void fixEndGatewayGate(@NotNull Block block) {
EndGateway gateway = (EndGateway) block.getState(); EndGateway gateway = (EndGateway) block.getState();
gateway.setAge(Long.MIN_VALUE); gateway.setAge(Long.MIN_VALUE);
if (block.getWorld().getEnvironment() == World.Environment.THE_END) { if (block.getWorld().getEnvironment() == World.Environment.THE_END) {
@@ -69,7 +75,7 @@ public class BlockChangeThread implements Runnable {
* @param block <p>The block to orient</p> * @param block <p>The block to orient</p>
* @param axis <p>The axis to use for orienting the block</p> * @param axis <p>The axis to use for orienting the block</p>
*/ */
private static void orientBlock(Block block, Axis axis) { private static void orientBlock(@NotNull Block block, @NotNull Axis axis) {
Orientable orientable = (Orientable) block.getBlockData(); Orientable orientable = (Orientable) block.getBlockData();
orientable.setAxis(axis); orientable.setAxis(axis);
block.setBlockData(orientable); block.setBlockData(orientable);

View File

@@ -0,0 +1,49 @@
package net.knarcraft.stargate.thread;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.ControlBlockUpdateRequest;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.Material;
import org.bukkit.block.Block;
/**
* This thread updates the signs and buttons of Stargates, if deemed necessary
*/
public class ControlBlocksUpdateThread implements Runnable {
@Override
public void run() {
//Abort if there's no work to be done
ControlBlockUpdateRequest controlBlockUpdateRequest = Stargate.getButtonUpdateRequestQueue().poll();
if (controlBlockUpdateRequest == null) {
return;
}
Portal portal = controlBlockUpdateRequest.portal();
portal.drawSign();
Block buttonLocation = portal.getLocation().getButtonBlock();
if (buttonLocation == null) {
return;
}
Stargate.debug("ControlBlocksUpdateThread", "Updating control blocks for portal " + portal);
if (portal.getOptions().isAlwaysOn()) {
//Clear button if it exists
if (MaterialHelper.isButtonCompatible(buttonLocation.getType())) {
Material newMaterial = PortalFileHelper.decideRemovalMaterial(buttonLocation, portal);
Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(buttonLocation, newMaterial, null));
}
} else {
//Replace button if the material is not a button
if (!MaterialHelper.isButtonCompatible(buttonLocation.getType())) {
PortalFileHelper.generatePortalButton(portal, portal.getLocation().getFacing());
}
}
}
}

View File

@@ -0,0 +1,208 @@
package net.knarcraft.stargate.transformation;
import org.bukkit.Axis;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* A class for performing rotational operations on vectors
*
* @author Kristian Knarvik
*/
@SuppressWarnings("unused")
public class SimpleVectorOperation {
private static final Map<BlockFace, Double> rotationAngles = new HashMap<>();
private static final Map<BlockFace, Vector> rotationAxes = new HashMap<>();
private static final Map<BlockFace, Axis> normalAxes = new HashMap<>();
private static final BlockFace defaultDirection = BlockFace.SOUTH;
private static final Axis defaultVerticalAxis = Axis.Y;
private final Axis normalAxis;
private boolean flipXAxis = false;
private final BlockFace facing;
/**
* Instantiates a vector operation to rotate vectors in the direction of a sign face
*
* @param signFace <p>The sign face of a gate's sign</p>
*/
public SimpleVectorOperation(@NotNull BlockFace signFace) {
if (normalAxes.isEmpty()) {
initializeIrisNormalAxes();
initializeOperations();
}
this.facing = signFace;
this.normalAxis = normalAxes.get(signFace);
}
/**
* Gets the block face of a sign given upon instantiation
*
* @return <p>The block face of a sign given upon instantiation</p>
*/
@NotNull
public BlockFace getFacing() {
return facing;
}
/**
* Gets the normal axis orthogonal to the opening plane
*
* <p>Said another way, get the axis going directly towards or away from a stargate's entrance.</p>
*
* @return <p>The normal axis orthogonal to the opening plane</p>
*/
@NotNull
public Axis getNormalAxis() {
return normalAxis;
}
/**
* Sets whether to flip the X- axis
*
* @param flipXAxis <p>Whether to flip the X-axis</p>
*/
public void setFlipXAxis(boolean flipXAxis) {
this.flipXAxis = flipXAxis;
}
/**
* Performs an operation from the real space to the vector space
*
* @param vector <p>The vector to perform the operation on</p>
* @return vector <p>A new vector with the operation applied</p>
*/
@NotNull
public Vector performToAbstractSpaceOperation(@NotNull Vector vector) {
Vector clone = vector.clone();
clone.rotateAroundAxis(rotationAxes.get(facing), rotationAngles.get(facing));
if (flipXAxis) {
clone.setX(-clone.getX());
}
return clone;
}
/**
* Performs an operation from the vector space to the real space
*
* @param vector <p>The vector to perform the inverse operation on</p>
* @return vector <p>A new vector with the operation applied</p>
*/
@NotNull
public Vector performToRealSpaceOperation(@NotNull Vector vector) {
Vector clone = vector.clone();
if (flipXAxis) {
clone.setX(-clone.getX());
}
return clone.rotateAroundAxis(rotationAxes.get(facing), -rotationAngles.get(facing));
}
/**
* Performs an operation from the vector space to the real space
*
* @param vector <p>The vector to perform the inverse operation on</p>
* @return vector <p>A new vector with the operation applied</p>
*/
@NotNull
public BlockVector performToRealSpaceOperation(@NotNull BlockVector vector) {
return performToRealSpaceOperation((Vector) vector).toBlockVector();
}
/**
* Gets the block vector pointing to the real location of the relative block vector
*
* @param topLeft <p>The vector of the top-left portal block</p>
* @param relative <p>The relative vector of a portal block</p>
* @return <p>The vector for the true location of the block in the real space</p>
*/
public BlockVector getRealLocationVector(@NotNull BlockVector topLeft, @NotNull BlockVector relative) {
return topLeft.clone().add(this.performToRealSpaceOperation(relative)).toBlockVector();
}
/**
* Gets the block pointing to the real location of the relative block vector
*
* @param topLeft <p>The top-left portal block</p>
* @param relative <p>The relative vector of a portal block</p>
* @return <p>The corresponding block</p>
*/
public Block getRealLocationBlock(@NotNull Block topLeft, @NotNull BlockVector relative) {
return topLeft.getLocation().clone().add(this.performToRealSpaceOperation(relative)).getBlock();
}
/**
* Initializes the operations used for rotating to each block-face
*/
private static void initializeOperations() {
Map<Axis, Vector> axisVectors = new HashMap<>();
axisVectors.put(Axis.Y, new Vector(0, 1, 0));
axisVectors.put(Axis.X, new Vector(1, 0, 0));
axisVectors.put(Axis.Z, new Vector(0, 0, 1));
//Use the cross product to find the correct axis
for (BlockFace face : normalAxes.keySet()) {
Vector crossProduct = face.getDirection().crossProduct(defaultDirection.getDirection());
if (face == defaultDirection || face == defaultDirection.getOppositeFace()) {
rotationAxes.put(face, axisVectors.get(defaultVerticalAxis));
} else if (Math.abs(crossProduct.getZ()) > 0) {
rotationAxes.put(face, axisVectors.get(Axis.Z));
} else if (Math.abs(crossProduct.getY()) > 0) {
rotationAxes.put(face, axisVectors.get(Axis.Y));
} else {
rotationAxes.put(face, axisVectors.get(Axis.X));
}
}
calculateRotations();
}
/**
* Calculates the required rotations based on the default rotation
*/
private static void calculateRotations() {
double halfRotation = Math.PI;
double quarterRotation = halfRotation / 2;
Vector defaultDirectionVector = defaultDirection.getDirection();
boolean defaultDirectionPositive = defaultDirectionVector.getX() + defaultDirectionVector.getY() +
defaultDirectionVector.getZ() > 0;
for (BlockFace blockFace : normalAxes.keySet()) {
if (defaultDirection == blockFace) {
//The default direction requires no rotation
rotationAngles.put(blockFace, 0d);
} else if (defaultDirection.getOppositeFace() == blockFace) {
//The opposite direction requires a half rotation
rotationAngles.put(blockFace, halfRotation);
} else {
//All the other used directions require a quarter rotation
Vector faceDirectionVector = blockFace.getDirection();
boolean faceDirectionPositive = faceDirectionVector.getX() + faceDirectionVector.getY() +
faceDirectionVector.getZ() > 0;
double rotation = defaultDirectionPositive && faceDirectionPositive ? quarterRotation : -quarterRotation;
rotationAngles.put(blockFace, rotation);
}
}
}
/**
* Initializes the iris normal axes corresponding to each block face
*/
private static void initializeIrisNormalAxes() {
normalAxes.put(BlockFace.EAST, Axis.Z);
normalAxes.put(BlockFace.WEST, Axis.Z);
normalAxes.put(BlockFace.NORTH, Axis.X);
normalAxes.put(BlockFace.SOUTH, Axis.X);
normalAxes.put(BlockFace.UP, Axis.Y);
normalAxes.put(BlockFace.DOWN, Axis.Y);
}
}

View File

@@ -0,0 +1,54 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.ConfigOption;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
/**
* A helper for dealing with BStats
*/
public final class BStatsHelper {
private static boolean hasBeenInitialized = false;
private BStatsHelper() {
}
/**
* Initializes BStats
*
* @param plugin <p>The plugin to initialize BStats for</p>
*/
public static void initialize(@NotNull JavaPlugin plugin) {
if (hasBeenInitialized) {
throw new IllegalArgumentException("BStats initialized twice");
} else {
hasBeenInitialized = true;
}
int pluginId = 10451;
Metrics metrics = new Metrics(plugin, pluginId);
Map<ConfigOption, Object> configValues = Stargate.getStargateConfig().getConfigOptions();
Map<String, List<String>> portalNetworks = PortalUtil.getAllPortalNetworks();
int totalPortals = 0;
for (List<String> portals : portalNetworks.values()) {
totalPortals += portals.size();
}
metrics.addCustomChart(new SimplePie("language", () -> (String) configValues.get(ConfigOption.LANGUAGE)));
metrics.addCustomChart(new SimplePie("gateformats", () -> String.valueOf(GateHandler.getGateCount())));
int finalTotalPortals = totalPortals;
metrics.addCustomChart(new SingleLineChart("gatesv3", () -> finalTotalPortals));
}
}

View File

@@ -1,12 +1,14 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -36,6 +38,7 @@ public final class BungeeHelper {
* *
* @return <p>The bungee plugin channel</p> * @return <p>The bungee plugin channel</p>
*/ */
@NotNull
public static String getBungeeChannel() { public static String getBungeeChannel() {
return bungeeChannel; return bungeeChannel;
} }
@@ -48,9 +51,10 @@ public final class BungeeHelper {
* queue and teleported to the destination.</p> * queue and teleported to the destination.</p>
* *
* @param playerUUID <p>The UUID of the player to remove</p> * @param playerUUID <p>The UUID of the player to remove</p>
* @return <p>The name of the destination portal the player should be teleported to</p> * @return <p>The name of the destination portal the player should be teleported to, or null if not queued</p>
*/ */
public static String removeFromQueue(UUID playerUUID) { @Nullable
public static String removeFromQueue(@NotNull UUID playerUUID) {
return bungeeQueue.remove(playerUUID); return bungeeQueue.remove(playerUUID);
} }
@@ -61,7 +65,7 @@ public final class BungeeHelper {
* @param entrancePortal <p>The portal the player is teleporting from</p> * @param entrancePortal <p>The portal the player is teleporting from</p>
* @return <p>True if the message was successfully sent</p> * @return <p>True if the message was successfully sent</p>
*/ */
public static boolean sendTeleportationMessage(Player player, Portal entrancePortal) { public static boolean sendTeleportationMessage(@NotNull Player player, @NotNull Portal entrancePortal) {
try { try {
//Build the teleportation message, format is <player identifier>delimiter<destination> //Build the teleportation message, format is <player identifier>delimiter<destination>
String message = player.getUniqueId() + teleportMessageDelimiter + entrancePortal.getDestinationName(); String message = player.getUniqueId() + teleportMessageDelimiter + entrancePortal.getDestinationName();
@@ -72,7 +76,7 @@ public final class BungeeHelper {
//Build the message data and send it over the SGBungee BungeeCord channel //Build the message data and send it over the SGBungee BungeeCord channel
dataOutputStream.writeUTF("Forward"); dataOutputStream.writeUTF("Forward");
//Send the message to the server defined in the entrance portal's network line //Send the message to the server defined in the entrance portal's network line
dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); dataOutputStream.writeUTF(Portal.cleanString(entrancePortal.getNetwork()));
//Specify the sub-channel/tag to make it recognizable on arrival //Specify the sub-channel/tag to make it recognizable on arrival
dataOutputStream.writeUTF(bungeeSubChannel); dataOutputStream.writeUTF(bungeeSubChannel);
//Write the length of the message //Write the length of the message
@@ -81,9 +85,8 @@ public final class BungeeHelper {
dataOutputStream.writeBytes(message); dataOutputStream.writeBytes(message);
//Send the plugin message //Send the plugin message
player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray());
} catch (IOException ex) { } catch (IOException exception) {
Stargate.logSevere("Error sending BungeeCord teleport packet"); Stargate.logSevere("Error sending BungeeCord teleport packet! Message: " + exception.getMessage());
ex.printStackTrace();
return false; return false;
} }
return true; return true;
@@ -96,20 +99,19 @@ public final class BungeeHelper {
* @param entrancePortal <p>The bungee portal the player is teleporting from</p> * @param entrancePortal <p>The bungee portal the player is teleporting from</p>
* @return <p>True if the plugin message was sent successfully</p> * @return <p>True if the plugin message was sent successfully</p>
*/ */
public static boolean changeServer(Player player, Portal entrancePortal) { public static boolean changeServer(@NotNull Player player, @NotNull Portal entrancePortal) {
try { try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
//Send a connect-message to connect the player to the server defined in the entrance portal's network line //Send a connect-message to connect the player to the server defined in the entrance portal's network line
dataOutputStream.writeUTF("Connect"); dataOutputStream.writeUTF("Connect");
dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); dataOutputStream.writeUTF(Portal.cleanString(entrancePortal.getNetwork()));
//Send the plugin message //Send the plugin message
player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray());
} catch (IOException ex) { } catch (IOException exception) {
Stargate.logSevere("Error sending BungeeCord connect packet"); Stargate.logSevere("Error sending BungeeCord connect packet! Message: " + exception.getMessage());
ex.printStackTrace();
return false; return false;
} }
return true; return true;
@@ -121,6 +123,7 @@ public final class BungeeHelper {
* @param message <p>The byte array to read</p> * @param message <p>The byte array to read</p>
* @return <p>The message contained in the byte array, or null on failure</p> * @return <p>The message contained in the byte array, or null on failure</p>
*/ */
@Nullable
public static String readPluginMessage(byte[] message) { public static String readPluginMessage(byte[] message) {
byte[] data; byte[] data;
try { try {
@@ -137,9 +140,8 @@ public final class BungeeHelper {
data = new byte[dataLength]; data = new byte[dataLength];
//Read the message to the prepared array //Read the message to the prepared array
dataInputStream.readFully(data); dataInputStream.readFully(data);
} catch (IOException ex) { } catch (IOException exception) {
Stargate.logSevere("Error receiving BungeeCord message"); Stargate.logSevere("Error receiving BungeeCord message. Message: " + exception.getMessage());
ex.printStackTrace();
return null; return null;
} }
return new String(data); return new String(data);
@@ -150,7 +152,7 @@ public final class BungeeHelper {
* *
* @param receivedMessage <p>The received teleport message</p> * @param receivedMessage <p>The received teleport message</p>
*/ */
public static void handleTeleportMessage(String receivedMessage) { public static void handleTeleportMessage(@NotNull String receivedMessage) {
//Get the player id and destination from the message //Get the player id and destination from the message
String[] messageParts = receivedMessage.split(teleportMessageDelimiter); String[] messageParts = receivedMessage.split(teleportMessageDelimiter);
UUID playerUUID = UUID.fromString(messageParts[0]); UUID playerUUID = UUID.fromString(messageParts[0]);
@@ -161,7 +163,7 @@ public final class BungeeHelper {
if (player == null) { if (player == null) {
bungeeQueue.put(playerUUID, destination); bungeeQueue.put(playerUUID, destination);
} else { } else {
Portal destinationPortal = PortalHandler.getBungeePortal(destination); Portal destinationPortal = PortalUtil.getBungeePortal(destination);
//If teleporting to an invalid portal, let the server decide where the player arrives //If teleporting to an invalid portal, let the server decide where the player arrives
if (destinationPortal == null) { if (destinationPortal == null) {
Stargate.logInfo(String.format("Bungee portal %s does not exist", destination)); Stargate.logInfo(String.format("Bungee portal %s does not exist", destination));
@@ -179,18 +181,19 @@ public final class BungeeHelper {
* @param event <p>The event causing the teleportation</p> * @param event <p>The event causing the teleportation</p>
* @return <p>True if the teleportation was successful</p> * @return <p>True if the teleportation was successful</p>
*/ */
public static boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) { public static boolean bungeeTeleport(@NotNull Player player, @NotNull Portal entrancePortal,
@NotNull PlayerMoveEvent event) {
//Check if bungee is actually enabled //Check if bungee is actually enabled
if (!Stargate.getGateConfig().enableBungee()) { if (!Stargate.getGateConfig().enableBungee()) {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled")); new SGFormatBuilder(Message.BUNGEE_DISABLED).error(player);
} }
entrancePortal.getPortalOpener().closePortal(false); entrancePortal.getPortalOpener().closePortal(false);
return false; return false;
} }
//Teleport the player back to this gate, for sanity's sake //Teleport the player back to this gate, for sanity's sake
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
//Send the SGBungee packet first, it will be queued by BC if required //Send the SGBungee packet first, it will be queued by BC if required
if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) { if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
@@ -208,14 +211,4 @@ public final class BungeeHelper {
return true; return true;
} }
/**
* Strips all color tags from a string
*
* @param string <p>The string to strip color from</p>
* @return <p>The string without color codes</p>
*/
private static String stripColor(String string) {
return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string));
}
} }

View File

@@ -1,47 +0,0 @@
package net.knarcraft.stargate.utility;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ColorHelper {
/**
* Inverts the given color
*
* @param color <p>The color to invert</p>
* @return <p>The inverted color</p>
*/
public static Color invert(Color color) {
return color.setRed(255 - color.getRed()).setGreen(255 - color.getGreen()).setBlue(255 - color.getBlue());
}
/**
* Gets the chat color corresponding to the given color
*
* @param color <p>The color to convert into a chat color</p>
* @return <p>The resulting chat color</p>
*/
public static ChatColor fromColor(Color color) {
return ChatColor.of(String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()));
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
}

View File

@@ -1,8 +1,14 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Sign;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** /**
* This class helps with direction-related calculations * This class helps with direction-related calculations
@@ -13,6 +19,22 @@ public final class DirectionHelper {
} }
/**
* Gets the direction the given block is facing
*
* @param block <p>The block to get the direction of</p>
* @return <p>The direction the block is facing, or null if the block is not directional</p>
*/
@Nullable
public static BlockFace getFacing(@NotNull Block block) {
BlockData blockData = block.getBlockData();
if (blockData instanceof Directional directional) {
return directional.getFacing();
} else {
return null;
}
}
/** /**
* Gets a yaw by comparing two locations * Gets a yaw by comparing two locations
* *
@@ -24,7 +46,7 @@ public final class DirectionHelper {
* @param location2 <p>The second location, which the yaw will point towards</p> * @param location2 <p>The second location, which the yaw will point towards</p>
* @return <p>The yaw pointing from the first location to the second location</p> * @return <p>The yaw pointing from the first location to the second location</p>
*/ */
public static float getYawFromLocationDifference(Location location1, Location location2) { public static float getYawFromLocationDifference(@NotNull Location location1, @NotNull Location location2) {
Location difference = location1.clone().subtract(location2.clone()); Location difference = location1.clone().subtract(location2.clone());
if (difference.getX() > 0) { if (difference.getX() > 0) {
return 90; return 90;
@@ -38,6 +60,32 @@ public final class DirectionHelper {
throw new IllegalArgumentException("Locations given are equal or at the same x and y axis"); throw new IllegalArgumentException("Locations given are equal or at the same x and y axis");
} }
/**
* Gets a block face by comparing two locations
*
* <p>The block face here is the direction an observer at the from location has to look to face the to location.
* The block face is only meant to be calculated for locations where both have either the same x value or the same
* z value. Equal locations, or locations with equal x and equal z will throw an exception.</p>
*
* @param fromLocation <p>The origin location</p>
* @param toLocation <p>The target location, which the block face will point towards</p>
* @return <p>The yaw pointing from the first location to the second location</p>
* @throws IllegalArgumentException <p>If the locations are the same, or equal except y</p>
*/
public static BlockFace getBlockFaceFromLocationDifference(@NotNull Location fromLocation, @NotNull Location toLocation) throws IllegalArgumentException {
Location difference = fromLocation.clone().subtract(toLocation.clone());
if (difference.getX() > 0) {
return BlockFace.WEST;
} else if (difference.getX() < 0) {
return BlockFace.EAST;
} else if (difference.getZ() > 0) {
return BlockFace.NORTH;
} else if (difference.getZ() < 0) {
return BlockFace.SOUTH;
}
throw new IllegalArgumentException("Locations given are equal or at the same x and y axis");
}
/** /**
* Gets a block face given a yaw value * Gets a block face given a yaw value
* *
@@ -45,8 +93,10 @@ public final class DirectionHelper {
* *
* @param yaw <p>The yaw value to convert</p> * @param yaw <p>The yaw value to convert</p>
* @return <p>The block face the yaw corresponds to</p> * @return <p>The block face the yaw corresponds to</p>
* @throws IllegalArgumentException <p>If a yaw not divisible by 90 us given</p>
*/ */
public static BlockFace getBlockFaceFromYaw(double yaw) { @NotNull
public static BlockFace getBlockFaceFromYaw(double yaw) throws IllegalArgumentException {
//Make sure the yaw is between 0 and 360 //Make sure the yaw is between 0 and 360
yaw = normalizeYaw(yaw); yaw = normalizeYaw(yaw);
@@ -63,13 +113,32 @@ public final class DirectionHelper {
} }
} }
/**
* Gets a yaw from the specified block face
*
* @param blockFace <p>The block face to convert into a yaw</p>
* @return <p>The yaw of the block face</p>
* @throws IllegalArgumentException <p>If the block face is not pointing to one of the four primary directions</p>
*/
public static double getYawFromBlockFace(@NotNull BlockFace blockFace) throws IllegalArgumentException {
return switch (blockFace) {
case SOUTH -> 0;
case WEST -> 90;
case NORTH -> 180;
case EAST -> 270;
default -> throw new IllegalArgumentException("Invalid block face given. It must be one of N,S,W,E");
};
}
/** /**
* Gets a direction vector given a yaw * Gets a direction vector given a yaw
* *
* @param yaw <p>The yaw to convert to a direction vector</p> * @param yaw <p>The yaw to convert to a direction vector</p>
* @return <p>The direction vector pointing in the same direction as the yaw</p> * @return <p>The direction vector pointing in the same direction as the yaw</p>
* @throws IllegalArgumentException <p>If a yaw not divisible by 90 is given</p>
*/ */
public static Vector getDirectionVectorFromYaw(double yaw) { @NotNull
public static Vector getDirectionVectorFromYaw(double yaw) throws IllegalArgumentException {
//Make sure the yaw is between 0 and 360 //Make sure the yaw is between 0 and 360
yaw = normalizeYaw(yaw); yaw = normalizeYaw(yaw);
@@ -99,7 +168,8 @@ public final class DirectionHelper {
* @param yaw <p>The yaw when looking directly outwards from a portal</p> * @param yaw <p>The yaw when looking directly outwards from a portal</p>
* @return <p>A location relative to the given location</p> * @return <p>A location relative to the given location</p>
*/ */
public static Location moveLocation(Location location, double right, double down, double out, double yaw) { @NotNull
public static Location moveLocation(@NotNull Location location, double right, double down, double out, double yaw) {
return location.add(getCoordinateVectorFromRelativeVector(right, down, out, yaw)); return location.add(getCoordinateVectorFromRelativeVector(right, down, out, yaw));
} }
@@ -111,8 +181,14 @@ public final class DirectionHelper {
* @param out <p>The distance outward from the top-left origin</p> * @param out <p>The distance outward from the top-left origin</p>
* @param yaw <p>The yaw when looking directly outwards from a portal</p> * @param yaw <p>The yaw when looking directly outwards from a portal</p>
* @return <p>A normal vector</p> * @return <p>A normal vector</p>
* @throws IllegalArgumentException <p>If a yaw not divisible by 90 is given</p>
*/ */
public static Vector getCoordinateVectorFromRelativeVector(double right, double down, double out, double yaw) { @NotNull
public static Vector getCoordinateVectorFromRelativeVector(double right, double down, double out,
double yaw) throws IllegalArgumentException {
//Make sure the yaw is between 0 and 360
yaw = normalizeYaw(yaw);
if (yaw == 0) { if (yaw == 0) {
//South //South
return new Vector(right, -down, out); return new Vector(right, -down, out);
@@ -130,6 +206,36 @@ public final class DirectionHelper {
} }
} }
/**
* Gets this block location's parent block
*
* <p>The parent block is the block the item at this block location is attached to. Usually this is the block a
* sign or wall sign is attached to.</p>
*
* @return <p>This block location's parent block</p>
*/
@Nullable
public static Block getParent(@NotNull Block block) {
int offsetX = 0;
int offsetY = 0;
int offsetZ = 0;
BlockData blockData = block.getBlockData();
if (blockData instanceof Directional) {
//Get the offset of the block "behind" this block
BlockFace facing = ((Directional) blockData).getFacing().getOppositeFace();
offsetX = facing.getModX();
offsetZ = facing.getModZ();
} else if (blockData instanceof Sign) {
//Get offset the block beneath the sign
offsetY = -1;
} else {
return null;
}
return block.getRelative(offsetX, offsetY, offsetZ);
}
/** /**
* Normalizes a yaw to make it positive and no larger than 360 degrees * Normalizes a yaw to make it positive and no larger than 360 degrees
* *

View File

@@ -1,12 +1,19 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import net.knarcraft.knarlib.formatting.FormatBuilder;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.EconomyConfig; import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.config.addons.EconomyConfig;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.property.PortalOwner; import net.knarcraft.stargate.portal.property.PortalOwner;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
@@ -27,7 +34,7 @@ public final class EconomyHelper {
* @param cost <p>The cost of teleportation</p> * @param cost <p>The cost of teleportation</p>
* @return <p>False if payment was successful. True if the payment was unsuccessful</p> * @return <p>False if payment was successful. True if the payment was unsuccessful</p>
*/ */
public static boolean cannotPayTeleportFee(Portal entrancePortal, Player player, int cost) { public static boolean cannotPayTeleportFee(@NotNull Portal entrancePortal, @NotNull Player player, int cost) {
boolean success; boolean success;
//Try to charge the player. Paying the portal owner is only possible if a UUID is available //Try to charge the player. Paying the portal owner is only possible if a UUID is available
@@ -76,10 +83,8 @@ public final class EconomyHelper {
* @param portalOwner <p>The owner of the portal</p> * @param portalOwner <p>The owner of the portal</p>
* @param earnings <p>The amount the owner earned</p> * @param earnings <p>The amount the owner earned</p>
*/ */
public static void sendObtainMessage(String portalName, Player portalOwner, int earnings) { public static void sendObtainMessage(@NotNull String portalName, @NotNull Player portalOwner, int earnings) {
String obtainedMsg = Stargate.getString("ecoObtain"); replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_OBTAINED), portalName, earnings).success(portalOwner);
obtainedMsg = replaceVars(obtainedMsg, portalName, earnings);
Stargate.getMessageSender().sendSuccessMessage(portalOwner, obtainedMsg);
} }
/** /**
@@ -89,10 +94,8 @@ public final class EconomyHelper {
* @param player <p>The interacting player</p> * @param player <p>The interacting player</p>
* @param cost <p>The cost of the interaction</p> * @param cost <p>The cost of the interaction</p>
*/ */
public static void sendDeductMessage(String portalName, Player player, int cost) { public static void sendDeductMessage(@NotNull String portalName, @NotNull Player player, int cost) {
String deductMsg = Stargate.getString("ecoDeduct"); replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_DEDUCTED), portalName, cost).success(player);
deductMsg = replaceVars(deductMsg, portalName, cost);
Stargate.getMessageSender().sendSuccessMessage(player, deductMsg);
} }
/** /**
@@ -102,10 +105,8 @@ public final class EconomyHelper {
* @param player <p>The interacting player</p> * @param player <p>The interacting player</p>
* @param cost <p>The cost of the interaction</p> * @param cost <p>The cost of the interaction</p>
*/ */
public static void sendInsufficientFundsMessage(String portalName, Player player, int cost) { public static void sendInsufficientFundsMessage(@NotNull String portalName, @NotNull Player player, int cost) {
String inFundMsg = Stargate.getString("ecoInFunds"); replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_INSUFFICIENT), portalName, cost).error(player);
inFundMsg = replaceVars(inFundMsg, portalName, cost);
Stargate.getMessageSender().sendErrorMessage(player, inFundMsg);
} }
/** /**
@@ -115,10 +116,8 @@ public final class EconomyHelper {
* @param player <p>The player breaking the portal</p> * @param player <p>The player breaking the portal</p>
* @param cost <p>The amount the user has to pay for destroying the portal. (expects a negative value)</p> * @param cost <p>The amount the user has to pay for destroying the portal. (expects a negative value)</p>
*/ */
public static void sendRefundMessage(String portalName, Player player, int cost) { public static void sendRefundMessage(@NotNull String portalName, @NotNull Player player, int cost) {
String refundMsg = Stargate.getString("ecoRefund"); replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_REFUNDED), portalName, -cost).success(player);
refundMsg = replaceVars(refundMsg, portalName, -cost);
Stargate.getMessageSender().sendSuccessMessage(player, refundMsg);
} }
/** /**
@@ -129,7 +128,7 @@ public final class EconomyHelper {
* @param destination <p>The destination portal</p> * @param destination <p>The destination portal</p>
* @return <p>The cost of using the portal</p> * @return <p>The cost of using the portal</p>
*/ */
public static int getUseCost(Player player, Portal source, Portal destination) { public static int getUseCost(@NotNull Player player, @NotNull Portal source, @Nullable Portal destination) {
EconomyConfig config = Stargate.getEconomyConfig(); EconomyConfig config = Stargate.getEconomyConfig();
//No payment required //No payment required
if (!config.useEconomy() || source.getOptions().isFree()) { if (!config.useEconomy() || source.getOptions().isFree()) {
@@ -144,7 +143,7 @@ public final class EconomyHelper {
return 0; return 0;
} }
//Player gets free gate use //Player gets free gate use
if (PermissionHelper.hasPermission(player, "stargate.free.use")) { if (PermissionHelper.hasPermission(player, Permission.FREE_USAGE)) {
return 0; return 0;
} }
@@ -159,7 +158,7 @@ public final class EconomyHelper {
* @param cost <p>The cost of the transaction</p> * @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p> * @return <p>True if the player was charged successfully</p>
*/ */
public static boolean chargePlayerIfNecessary(Player player, UUID target, int cost) { public static boolean chargePlayerIfNecessary(@NotNull Player player, @NotNull UUID target, int cost) {
if (skipPayment(cost)) { if (skipPayment(cost)) {
return true; return true;
} }
@@ -174,17 +173,43 @@ public final class EconomyHelper {
* @param amount <p>The amount to charge</p> * @param amount <p>The amount to charge</p>
* @return <p>True if the payment succeeded, or if no payment was necessary</p> * @return <p>True if the payment succeeded, or if no payment was necessary</p>
*/ */
private static boolean chargePlayer(Player player, double amount) { private static boolean chargePlayer(@NotNull Player player, double amount) {
Economy economy = Stargate.getEconomyConfig().getEconomy(); Economy economy = Stargate.getEconomyConfig().getEconomy();
if (Stargate.getEconomyConfig().isEconomyEnabled() && economy != null) { if (Stargate.getEconomyConfig().isEconomyEnabled() && economy != null) {
if (!economy.has(player, amount)) { if (!economy.has(player, amount)) {
return false; return false;
} }
economy.withdrawPlayer(player, amount); if (amount > 0) {
economy.withdrawPlayer(player, amount);
} else {
economy.depositPlayer(player, -amount);
}
} }
return true; return true;
} }
/**
* Transfers the given fees to the tax account
*
* @param economy <p>The economy to use</p>
* @param cost <p>The cost to transfer</p>
*/
@SuppressWarnings("deprecation")
private static void transferFees(@NotNull Economy economy, int cost) {
String accountName = Stargate.getEconomyConfig().getTaxAccount();
if (accountName == null || accountName.isEmpty()) {
return;
}
try {
UUID accountId = UUID.fromString(accountName);
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(accountId);
economy.depositPlayer(offlinePlayer, cost);
} catch (IllegalArgumentException exception) {
economy.depositPlayer(accountName, cost);
}
}
/** /**
* Charges the player for an action, if required * Charges the player for an action, if required
* *
@@ -192,12 +217,20 @@ public final class EconomyHelper {
* @param cost <p>The cost of the transaction</p> * @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p> * @return <p>True if the player was charged successfully</p>
*/ */
public static boolean chargePlayerIfNecessary(Player player, int cost) { public static boolean chargePlayerIfNecessary(@NotNull Player player, int cost) {
if (skipPayment(cost)) { if (skipPayment(cost)) {
return true; return true;
} }
//Charge player //Charge player
return chargePlayer(player, cost); boolean charged = chargePlayer(player, cost);
// Transfer the charged amount to the tax account
Economy economy = Stargate.getEconomyConfig().getEconomy();
if (charged && economy != null) {
transferFees(economy, cost);
}
return charged;
} }
/** /**
@@ -218,9 +251,10 @@ public final class EconomyHelper {
* @param amount <p>The amount to charge</p> * @param amount <p>The amount to charge</p>
* @return <p>True if the payment succeeded, or if no payment was necessary</p> * @return <p>True if the payment succeeded, or if no payment was necessary</p>
*/ */
private static boolean chargePlayer(Player player, UUID target, double amount) { private static boolean chargePlayer(@NotNull Player player, @NotNull UUID target, double amount) {
Economy economy = Stargate.getEconomyConfig().getEconomy(); Economy economy = Stargate.getEconomyConfig().getEconomy();
if (Stargate.getEconomyConfig().isEconomyEnabled() && player.getUniqueId().compareTo(target) != 0 && economy != null) { if (Stargate.getEconomyConfig().isEconomyEnabled() && player.getUniqueId().compareTo(target) != 0 &&
economy != null) {
if (!economy.has(player, amount)) { if (!economy.has(player, amount)) {
return false; return false;
} }
@@ -232,16 +266,16 @@ public final class EconomyHelper {
} }
/** /**
* Replaces the cost and portal variables in a string * Replaces the cost and portal variables in a format builder
* *
* @param message <p>The message to replace variables in</p> * @param builder <p>The format builder to replace variables for</p>
* @param portalName <p>The name of the relevant portal</p> * @param portalName <p>The name of the relevant portal</p>
* @param cost <p>The cost for a given interaction</p> * @param cost <p>The cost for a given interaction</p>
* @return <p>The same string with cost and portal variables replaced</p> * @return <p>The same format builder</p>
*/ */
private static String replaceVars(String message, String portalName, int cost) { private static FormatBuilder replacePlaceholders(@NotNull FormatBuilder builder, @NotNull String portalName, int cost) {
return Stargate.replaceVars(message, new String[]{"%cost%", "%portal%"}, builder.replace("%cost%", Stargate.getEconomyConfig().format(cost)).replace("%portal%", portalName);
new String[]{Stargate.getEconomyConfig().format(cost), portalName}); return builder;
} }
} }

View File

@@ -1,6 +1,7 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
/** /**
* This helper class helps with entity properties not immediately available * This helper class helps with entity properties not immediately available
@@ -21,7 +22,7 @@ public final class EntityHelper {
* @param entity <p>The entity to get max size for</p> * @param entity <p>The entity to get max size for</p>
* @return <p>The max size of the entity</p> * @return <p>The max size of the entity</p>
*/ */
public static int getEntityMaxSizeInt(Entity entity) { public static int getEntityMaxSizeInt(@NotNull Entity entity) {
return (int) Math.ceil((float) getEntityMaxSize(entity)); return (int) Math.ceil((float) getEntityMaxSize(entity));
} }
@@ -31,7 +32,7 @@ public final class EntityHelper {
* @param entity <p>The entity to get max size for</p> * @param entity <p>The entity to get max size for</p>
* @return <p>The max size of the entity</p> * @return <p>The max size of the entity</p>
*/ */
public static double getEntityMaxSize(Entity entity) { public static double getEntityMaxSize(@NotNull Entity entity) {
return Math.max(entity.getBoundingBox().getWidthX(), entity.getBoundingBox().getWidthZ()); return Math.max(entity.getBoundingBox().getWidthX(), entity.getBoundingBox().getWidthZ());
} }

View File

@@ -1,127 +0,0 @@
package net.knarcraft.stargate.utility;
import net.md_5.bungee.api.ChatColor;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* Helper class for reading files
*/
public final class FileHelper {
private FileHelper() {
}
/**
* Gets an input stream from a string pointing to an internal file
*
* <p>This is used for getting an input stream for reading a file contained within the compiled .jar file. The file
* should be in the resources directory, and the file path should start with a forward slash ("/") character.</p>
*
* @param file <p>The file to read</p>
* @return <p>An input stream for the file</p>
*/
public static InputStream getInputStreamForInternalFile(String file) {
return FileHelper.class.getResourceAsStream(file);
}
/**
* Gets a buffered reader from a string pointing to a file
*
* @param file <p>The file to read</p>
* @return <p>A buffered reader reading the file</p>
* @throws FileNotFoundException <p>If the given file does not exist</p>
*/
public static BufferedReader getBufferedReaderFromString(String file) throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream(file);
return getBufferedReaderFromInputStream(fileInputStream);
}
/**
* 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);
}
/**
* Gets a buffered writer from a string pointing to a file
*
* @param file <p>The file to write to</p>
* @return <p>A buffered writer writing to the file</p>
* @throws FileNotFoundException <p>If the file does not exist</p>
*/
public static BufferedWriter getBufferedWriterFromString(String file) throws FileNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8);
return new BufferedWriter(outputStreamWriter);
}
/**
* Reads key/value pairs from an input stream
*
* @param bufferedReader <p>The buffered reader to read</p>
* @return <p>A map containing the read pairs</p>
* @throws IOException <p>If unable to read from the stream</p>
*/
public static Map<String, String> readKeyValuePairs(BufferedReader bufferedReader) throws IOException {
Map<String, String> readPairs = new HashMap<>();
String line = bufferedReader.readLine();
boolean firstLine = true;
while (line != null) {
//Strip UTF BOM from the first line
if (firstLine) {
line = removeUTF8BOM(line);
firstLine = false;
}
//Split at first "="
int equalSignIndex = line.indexOf('=');
if (equalSignIndex == -1) {
line = bufferedReader.readLine();
continue;
}
//Read the line
String key = line.substring(0, equalSignIndex);
String value = ChatColor.translateAlternateColorCodes('&', line.substring(equalSignIndex + 1));
readPairs.put(key, value);
line = bufferedReader.readLine();
}
bufferedReader.close();
return readPairs;
}
/**
* Removes the UTF-8 Byte Order Mark if present
*
* @param string <p>The string to remove the BOM from</p>
* @return <p>A string guaranteed without a BOM</p>
*/
private static String removeUTF8BOM(String string) {
String UTF8_BOM = "\uFEFF";
if (string.startsWith(UTF8_BOM)) {
string = string.substring(1);
}
return string;
}
}

View File

@@ -1,13 +1,16 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
import net.knarcraft.stargate.config.material.MaterialSpecifier;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.configuration.InvalidConfigurationException;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set;
/** /**
* Helper class for reading gate files * Helper class for reading gate files
@@ -25,15 +28,16 @@ public final class GateReader {
* @param characterMaterialMap <p>The map of characters to store valid symbols in</p> * @param characterMaterialMap <p>The map of characters to store valid symbols in</p>
* @param fileName <p>The filename of the loaded gate config file</p> * @param fileName <p>The filename of the loaded gate config file</p>
* @param design <p>The list to store the loaded design/layout to</p> * @param design <p>The list to store the loaded design/layout to</p>
* @param frameTypes <p>The set to store frame/border materials to</p>
* @param config <p>The map of config values to store to</p> * @param config <p>The map of config values to store to</p>
* @return <p>The column count/width of the loaded gate</p> * @return <p>The column count/width of the loaded gate</p>
*/ */
public static int readGateFile(Scanner scanner, Map<Character, Material> characterMaterialMap, String fileName, public static int readGateFile(@NotNull Scanner scanner,
List<List<Character>> design, Set<Material> frameTypes, Map<String, String> config) { @NotNull Map<Character, List<MaterialSpecifier>> characterMaterialMap,
@NotNull String fileName, @NotNull List<List<Character>> design,
@NotNull Map<String, String> config) {
boolean designing = false; boolean designing = false;
int columns = 0; int columns = 0;
try { try (scanner) {
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {
String line = scanner.nextLine(); String line = scanner.nextLine();
@@ -46,7 +50,7 @@ public final class GateReader {
} else { } else {
if (!line.isEmpty() && !line.startsWith("#")) { if (!line.isEmpty() && !line.startsWith("#")) {
//Read a normal config value //Read a normal config value
readGateConfigValue(line, characterMaterialMap, frameTypes, config); readGateConfigValue(line, characterMaterialMap, config);
} else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) { } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) {
//An empty line marks the start of the gate's layout/design //An empty line marks the start of the gate's layout/design
designing = true; designing = true;
@@ -56,10 +60,6 @@ public final class GateReader {
} catch (Exception exception) { } catch (Exception exception) {
Stargate.logSevere(String.format("Could not load Gate %s - %s", fileName, exception.getMessage())); Stargate.logSevere(String.format("Could not load Gate %s - %s", fileName, exception.getMessage()));
return -1; return -1;
} finally {
if (scanner != null) {
scanner.close();
}
} }
return columns; return columns;
} }
@@ -77,8 +77,9 @@ public final class GateReader {
* @param design <p>The two-dimensional list to store the loaded design to</p> * @param design <p>The two-dimensional list to store the loaded design to</p>
* @return <p>The new max columns value of the design</p> * @return <p>The new max columns value of the design</p>
*/ */
private static int readGateDesignLine(String line, int maxColumns, Map<Character, Material> characterMaterialMap, private static int readGateDesignLine(@NotNull String line, int maxColumns,
String fileName, List<List<Character>> design) { @NotNull Map<Character, List<MaterialSpecifier>> characterMaterialMap,
@NotNull String fileName, @NotNull List<List<Character>> design) {
List<Character> row = new ArrayList<>(); List<Character> row = new ArrayList<>();
//Update the max columns number if this line has more columns //Update the max columns number if this line has more columns
@@ -88,7 +89,7 @@ public final class GateReader {
for (Character symbol : line.toCharArray()) { for (Character symbol : line.toCharArray()) {
//Refuse read gate designs with unknown characters //Refuse read gate designs with unknown characters
if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol))) { if (symbol.equals('?') || !characterMaterialMap.containsKey(symbol)) {
Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName, Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName,
symbol)); symbol));
return -1; return -1;
@@ -107,12 +108,12 @@ public final class GateReader {
* *
* @param line <p>The line to read</p> * @param line <p>The line to read</p>
* @param characterMaterialMap <p>The character to material map to store to</p> * @param characterMaterialMap <p>The character to material map to store to</p>
* @param frameTypes <p>The set to store gate frame/border types to</p>
* @param config <p>The config value map to store to</p> * @param config <p>The config value map to store to</p>
* @throws Exception <p>If an invalid material is encountered</p> * @throws InvalidConfigurationException <p>If an invalid material is encountered</p>
*/ */
private static void readGateConfigValue(String line, Map<Character, Material> characterMaterialMap, private static void readGateConfigValue(@NotNull String line,
Set<Material> frameTypes, Map<String, String> config) throws Exception { @NotNull Map<Character, List<MaterialSpecifier>> characterMaterialMap,
@NotNull Map<String, String> config) throws InvalidConfigurationException {
String[] split = line.split("="); String[] split = line.split("=");
String key = split[0].trim(); String key = split[0].trim();
String value = split[1].trim(); String value = split[1].trim();
@@ -120,14 +121,13 @@ public final class GateReader {
if (key.length() == 1) { if (key.length() == 1) {
//Read a gate frame material //Read a gate frame material
Character symbol = key.charAt(0); Character symbol = key.charAt(0);
Material material = Material.getMaterial(value);
if (material == null) { List<MaterialSpecifier> materials = MaterialHelper.parseTagsAndMaterials(value);
throw new Exception("Invalid material in line: " + line); if (!materials.isEmpty()) {
characterMaterialMap.put(symbol, materials);
} else {
throw new InvalidConfigurationException("Invalid material in line: " + line);
} }
//Register the map between the read symbol and the corresponding material
characterMaterialMap.put(symbol, material);
//Save the material as one of the frame materials used for this kind of gate
frameTypes.add(material);
} else { } else {
//Read a normal config value //Read a normal config value
config.put(key, value); config.put(key, value);
@@ -142,12 +142,13 @@ public final class GateReader {
* @param key <p>The config key to read</p> * @param key <p>The config key to read</p>
* @return <p>The read value, or -1 if it could not be read</p> * @return <p>The read value, or -1 if it could not be read</p>
*/ */
public static int readGateConfig(Map<String, String> config, String fileName, String key) { public static int readGateConfig(@NotNull Map<String, String> config, @NotNull String fileName,
@NotNull String key) {
if (config.containsKey(key)) { if (config.containsKey(key)) {
try { try {
return Integer.parseInt(config.get(key)); return Integer.parseInt(config.get(key));
} catch (NumberFormatException ex) { } catch (NumberFormatException exception) {
Stargate.logWarning(String.format("%s reading %s: %s is not numeric", ex.getClass().getName(), Stargate.logWarning(String.format("%s reading %s: %s is not numeric", exception.getClass().getName(),
fileName, key)); fileName, key));
} }
} }
@@ -164,17 +165,18 @@ public final class GateReader {
* @param defaultMaterial <p>The default material to use, in case the config is invalid</p> * @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
* @return <p>The material specified in the config, or the default material if it could not be read</p> * @return <p>The material specified in the config, or the default material if it could not be read</p>
*/ */
public static Material readGateConfig(Map<String, String> config, String fileName, String key, @NotNull
Material defaultMaterial) { public static List<MaterialSpecifier> readGateConfig(@NotNull Map<String, String> config, @NotNull String fileName,
@NotNull String key, @NotNull Material defaultMaterial) {
if (config.containsKey(key)) { if (config.containsKey(key)) {
Material material = Material.getMaterial(config.get(key)); List<MaterialSpecifier> materialSpecifiers = MaterialHelper.parseTagsAndMaterials(config.get(key));
if (material != null) { if (!materialSpecifiers.isEmpty()) {
return material; return materialSpecifiers;
} else { } else {
Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key)); Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key));
} }
} }
return defaultMaterial; return List.of(new BukkitMaterialSpecifier(defaultMaterial));
} }
/** /**
@@ -187,7 +189,8 @@ public final class GateReader {
* @param columns <p>The largest amount of columns in the design</p> * @param columns <p>The largest amount of columns in the design</p>
* @return <p>A matrix containing the gate's layout</p> * @return <p>A matrix containing the gate's layout</p>
*/ */
public static Character[][] generateLayoutMatrix(List<List<Character>> design, int columns) { @NotNull
public static Character[][] generateLayoutMatrix(@NotNull List<List<Character>> design, int columns) {
Character[][] layout = new Character[design.size()][columns]; Character[][] layout = new Character[design.size()][columns];
for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) { for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) {
List<Character> row = design.get(lineIndex); List<Character> row = design.get(lineIndex);

View File

@@ -0,0 +1,28 @@
package net.knarcraft.stargate.utility;
import java.util.List;
import java.util.Random;
/**
* A helper class for dealing with lists
*/
public final class ListHelper {
private static final Random random = new Random();
private ListHelper() {
}
/**
* Gets a random item from a list
*
* @param list <p>The list to get an item from</p>
* @param <T> <p>The type of item the list contains</p>
* @return <p>A random item</p>
*/
public static <T> T getRandom(List<T> list) {
return list.get(random.nextInt(list.size()));
}
}

View File

@@ -1,7 +1,19 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier;
import net.knarcraft.stargate.config.material.BukkitTagSpecifier;
import net.knarcraft.stargate.config.material.MaterialSpecifier;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** /**
* This class helps decide properties of materials not already present in the Spigot API * This class helps decide properties of materials not already present in the Spigot API
@@ -18,7 +30,7 @@ public final class MaterialHelper {
* @param material <p>The material to check</p> * @param material <p>The material to check</p>
* @return <p>True if the material is a wall coral</p> * @return <p>True if the material is a wall coral</p>
*/ */
public static boolean isWallCoral(Material material) { public static boolean isWallCoral(@NotNull Material material) {
//Unfortunately, there is no tag for dead wall corals, so they need to be checked manually //Unfortunately, there is no tag for dead wall corals, so they need to be checked manually
return Tag.WALL_CORALS.isTagged(material) || return Tag.WALL_CORALS.isTagged(material) ||
material.equals(Material.DEAD_BRAIN_CORAL_WALL_FAN) || material.equals(Material.DEAD_BRAIN_CORAL_WALL_FAN) ||
@@ -34,7 +46,7 @@ public final class MaterialHelper {
* @param material <p>The material to check</p> * @param material <p>The material to check</p>
* @return <p>True if the material is a container</p> * @return <p>True if the material is a container</p>
*/ */
public static boolean isContainer(Material material) { public static boolean isContainer(@NotNull Material material) {
return Tag.SHULKER_BOXES.isTagged(material) || material == Material.CHEST || return Tag.SHULKER_BOXES.isTagged(material) || material == Material.CHEST ||
material == Material.TRAPPED_CHEST || material == Material.ENDER_CHEST; material == Material.TRAPPED_CHEST || material == Material.ENDER_CHEST;
} }
@@ -45,8 +57,84 @@ public final class MaterialHelper {
* @param material <p>The material to check</p> * @param material <p>The material to check</p>
* @return <p>True if the material can be used as a button</p> * @return <p>True if the material can be used as a button</p>
*/ */
public static boolean isButtonCompatible(Material material) { public static boolean isButtonCompatible(@NotNull Material material) {
return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || isContainer(material); return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || isContainer(material);
} }
@NotNull
public static String specifiersToString(@NotNull List<MaterialSpecifier> specifiers) {
List<String> names = new ArrayList<>();
for (MaterialSpecifier specifier : specifiers) {
names.add(specifier.asString());
}
return String.join(",", names);
}
/**
* Converts a list of material specifiers to a set of materials
*
* @param specifiers <p>The material specifiers to convert</p>
* @return <p>The materials the specifiers represent</p>
*/
@NotNull
public static Set<Material> specifiersToMaterials(@NotNull List<MaterialSpecifier> specifiers) {
Set<Material> output = new HashSet<>();
for (MaterialSpecifier specifier : specifiers) {
output.addAll(specifier.asMaterials());
}
return output;
}
/**
* Parses all materials and material tags found in the input string
*
* @param input <p>The input string to parse</p>
* @return <p>All material specifiers found</p>
*/
@NotNull
public static List<MaterialSpecifier> parseTagsAndMaterials(@NotNull String input) {
List<MaterialSpecifier> specifiers = new ArrayList<>();
// Nothing to parse
if (input.isBlank()) {
return specifiers;
}
String[] parts;
if (input.contains(",")) {
parts = input.split(",");
} else {
parts = new String[]{input};
}
for (String part : parts) {
MaterialSpecifier materialSpecifier = parseTagOrMaterial(part.trim());
if (materialSpecifier != null) {
specifiers.add(materialSpecifier);
}
}
return specifiers;
}
@Nullable
private static MaterialSpecifier parseTagOrMaterial(@NotNull String input) {
if (input.startsWith("#")) {
String tagString = input.replaceFirst("#", "").toLowerCase();
Tag<Material> tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString), Material.class);
if (tag != null) {
return new BukkitTagSpecifier(tag);
}
} else {
Material material = Material.matchMaterial(input);
if (material != null) {
return new BukkitMaterialSpecifier(material);
}
}
return null;
}
} }

View File

@@ -1,12 +1,17 @@
package net.knarcraft.stargate.utility; package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.event.StargateAccessEvent; import net.knarcraft.stargate.event.StargateAccessEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.knarcraft.stargate.Stargate.getMaxNameNetworkLength; import static net.knarcraft.stargate.Stargate.getMaxNameNetworkLength;
@@ -25,7 +30,7 @@ public final class PermissionHelper {
* @param player <p>The player opening the portal</p> * @param player <p>The player opening the portal</p>
* @param portal <p>The portal to open</p> * @param portal <p>The portal to open</p>
*/ */
public static void openPortal(Player player, Portal portal) { public static void openPortal(@NotNull Player player, @NotNull Portal portal) {
Portal destination = portal.getPortalActivator().getDestination(); Portal destination = portal.getPortalActivator().getDestination();
//For an always open portal, no action is necessary //For an always open portal, no action is necessary
@@ -35,8 +40,8 @@ public final class PermissionHelper {
//Destination is invalid or the same portal. Send an error message //Destination is invalid or the same portal. Send an error message
if (destination == null || destination == portal) { if (destination == null || destination == portal) {
if (!portal.getOptions().isSilent()) { if (!portal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg")); new SGFormatBuilder(Message.INVALID_DESTINATION).error(player);
} }
return; return;
} }
@@ -50,28 +55,8 @@ public final class PermissionHelper {
return; return;
} }
//Deny access if another player has activated the portal, and it's still in use // Check if the player is able to open the portal
if (!portal.getOptions().isFixed() && portal.getPortalActivator().isActive() && if (canNotOpen(player, portal, destination)) {
portal.getActivePlayer() != player) {
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
return;
}
//Check if the player can use the private gate
if (portal.getOptions().isPrivate() && !PermissionHelper.canUsePrivatePortal(player, portal)) {
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
return;
}
//Destination is currently in use by another player, blocking teleportation
if (destination.isOpen() && !destination.getOptions().isAlwaysOn()) {
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("blockMsg"));
}
return; return;
} }
@@ -79,6 +64,43 @@ public final class PermissionHelper {
portal.getPortalOpener().openPortal(player, false); portal.getPortalOpener().openPortal(player, false);
} }
/**
* Checks whether something prevents the player from opening the given portal to the given destination
*
* @param player <p>The player trying to open the portal</p>
* @param portal <p>The portal to open</p>
* @param destination <p>The destination the player is attempting to open</p>
* @return <p>True if the player cannot open the portal</p>
*/
private static boolean canNotOpen(Player player, Portal portal, Portal destination) {
//Deny access if another player has activated the portal, and it's still in use
if (!portal.getOptions().isFixed() && portal.getPortalActivator().isActive() &&
portal.getActivePlayer() != player) {
if (!portal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
}
return true;
}
//Check if the player can use the private gate
if (portal.getOptions().isPrivate() && !PermissionHelper.canUsePrivatePortal(player, portal)) {
if (!portal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
}
return true;
}
//Destination is currently in use by another player, blocking teleportation
if (destination.isOpen() && !destination.getOptions().isAlwaysOn()) {
if (!portal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.DESTINATION_BLOCKED).error(player);
}
return true;
}
return false;
}
/** /**
* Creates a StargateAccessEvent and gets the updated deny value * Creates a StargateAccessEvent and gets the updated deny value
* *
@@ -89,7 +111,7 @@ public final class PermissionHelper {
* @param deny <p>Whether the player's access has already been denied by a previous check</p> * @param deny <p>Whether the player's access has already been denied by a previous check</p>
* @return <p>False if the player should be allowed through the portal</p> * @return <p>False if the player should be allowed through the portal</p>
*/ */
public static boolean portalAccessDenied(Player player, Portal portal, boolean deny) { public static boolean portalAccessDenied(@NotNull Player player, @NotNull Portal portal, boolean deny) {
StargateAccessEvent event = new StargateAccessEvent(player, portal, deny); StargateAccessEvent event = new StargateAccessEvent(player, portal, deny);
Stargate.getInstance().getServer().getPluginManager().callEvent(event); Stargate.getInstance().getServer().getPluginManager().callEvent(event);
return event.getDeny(); return event.getDeny();
@@ -103,23 +125,31 @@ public final class PermissionHelper {
* @param destination <p>The portal the user wants to exit from</p> * @param destination <p>The portal the user wants to exit from</p>
* @return <p>False if the user is allowed to access the portal</p> * @return <p>False if the user is allowed to access the portal</p>
*/ */
public static boolean cannotAccessPortal(Player player, Portal entrancePortal, Portal destination) { public static boolean cannotAccessPortal(@NotNull Player player, @NotNull Portal entrancePortal,
@Nullable Portal destination) {
boolean deny = false; boolean deny = false;
String route = "PermissionHelper::cannotAccessPortal";
if (entrancePortal.getOptions().isBungee()) { if (entrancePortal.getOptions().isBungee()) {
if (!PermissionHelper.canAccessServer(player, entrancePortal.getCleanNetwork())) { if (!PermissionHelper.canAccessServer(player, entrancePortal.getCleanNetwork())) {
//If the portal is a bungee portal, and the player cannot access the server, deny //If the portal is a bungee portal, and the player cannot access the server, deny
Stargate.debug("cannotAccessPortal", "Cannot access server"); Stargate.debug(route, "Cannot access server");
deny = true; deny = true;
} }
} else if (PermissionHelper.cannotAccessNetwork(player, entrancePortal.getCleanNetwork())) { } else if (PermissionHelper.cannotAccessNetwork(player, entrancePortal.getCleanNetwork())) {
//If the player does not have access to the network, deny //If the player does not have access to the network, deny
Stargate.debug("cannotAccessPortal", "Cannot access network"); Stargate.debug(route, "Cannot access network");
deny = true;
} else if (PermissionHelper.cannotAccessWorld(player, destination.getWorld().getName())) {
//If the player does not have access to the portal's world, deny
Stargate.debug("cannotAccessPortal", "Cannot access world");
deny = true; deny = true;
} else {
if (destination == null) {
//If there is no destination, deny
Stargate.debug(route, "Portal has no destination");
deny = true;
} else if (PermissionHelper.cannotAccessWorld(player, destination.getLocation().getWorld().getName())) {
//If the player does not have access to the portal's world, deny
Stargate.debug(route, "Cannot access world");
deny = true;
}
} }
//Allow other plugins to override whether the player can access the portal //Allow other plugins to override whether the player can access the portal
return portalAccessDenied(player, entrancePortal, deny); return portalAccessDenied(player, entrancePortal, deny);
@@ -134,7 +164,7 @@ public final class PermissionHelper {
* @param permission <p>The permission to check</p> * @param permission <p>The permission to check</p>
* @return <p>True if the player has the permission</p> * @return <p>True if the player has the permission</p>
*/ */
public static boolean hasPermission(Player player, String permission) { public static boolean hasPermission(@NotNull Player player, @NotNull String permission) {
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) { if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) {
Stargate.debug("hasPerm::Permission(" + player.getName() + ")", permission + " => " + Stargate.debug("hasPerm::Permission(" + player.getName() + ")", permission + " => " +
player.hasPermission(permission)); player.hasPermission(permission));
@@ -142,6 +172,19 @@ public final class PermissionHelper {
return player.hasPermission(permission); return player.hasPermission(permission);
} }
/**
* Checks whether a player has the given permission
*
* <p>This is the same as player.hasPermission(), but this function allows for printing permission debugging info.</p>
*
* @param player <p>The player to check</p>
* @param permission <p>The permission to check</p>
* @return <p>True if the player has the permission</p>
*/
public static boolean hasPermission(@NotNull Player player, @NotNull Permission permission) {
return hasPermission(player, permission.getNode());
}
/** /**
* Check if a player has been given a permission implicitly * Check if a player has been given a permission implicitly
* *
@@ -152,14 +195,15 @@ public final class PermissionHelper {
* @param permission <p>The permission to check</p> * @param permission <p>The permission to check</p>
* @return <p>True if the player has the permission implicitly or explicitly</p> * @return <p>True if the player has the permission implicitly or explicitly</p>
*/ */
public static boolean hasPermissionImplicit(Player player, String permission) { public static boolean hasPermissionImplicit(@NotNull Player player, @NotNull String permission) {
boolean debug = Stargate.getStargateConfig().isPermissionDebuggingEnabled();
if (!player.isPermissionSet(permission)) { if (!player.isPermissionSet(permission)) {
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) { if (debug) {
Stargate.debug("hasPermissionImplicit::Permission", permission + " => implicitly true"); Stargate.debug("hasPermissionImplicit::Permission", permission + " => implicitly true");
} }
return true; return true;
} }
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) { if (debug) {
Stargate.debug("hasPermissionImplicit::Permission", permission + " => " + Stargate.debug("hasPermissionImplicit::Permission", permission + " => " +
player.hasPermission(permission)); player.hasPermission(permission));
} }
@@ -173,14 +217,8 @@ public final class PermissionHelper {
* @param world <p>The world the player is trying to access</p> * @param world <p>The world the player is trying to access</p>
* @return <p>False if the player should be allowed to access the world</p> * @return <p>False if the player should be allowed to access the world</p>
*/ */
public static boolean cannotAccessWorld(Player player, String world) { public static boolean cannotAccessWorld(@NotNull Player player, @NotNull String world) {
//The player can access all worlds return hasPermission(player, Permission.ACCESS_WORLD, world);
if (hasPermission(player, "stargate.world")) {
//Check if the world permission has been explicitly denied
return !hasPermissionImplicit(player, "stargate.world." + world);
}
//The player can access the destination world
return !hasPermission(player, "stargate.world." + world);
} }
/** /**
@@ -190,22 +228,17 @@ public final class PermissionHelper {
* @param network <p>The network to check</p> * @param network <p>The network to check</p>
* @return <p>True if the player is denied from accessing the network</p> * @return <p>True if the player is denied from accessing the network</p>
*/ */
public static boolean cannotAccessNetwork(Player player, String network) { public static boolean cannotAccessNetwork(@NotNull Player player, @NotNull String network) {
//The player can access all networks if (hasPermission(player, Permission.ACCESS_NETWORK, network)) {
if (hasPermission(player, "stargate.network")) {
//Check if the world permission has been explicitly denied
return !hasPermissionImplicit(player, "stargate.network." + network);
}
//Check if the player can access this network
if (hasPermission(player, "stargate.network." + network)) {
return false; return false;
} }
//Is able to create personal gates (Assumption is made they can also access them) //Is able to create personal gates (Assumption is made they can also access them)
String playerName = player.getName(); String playerName = player.getName();
if (playerName.length() > getMaxNameNetworkLength()) { if (playerName.length() > getMaxNameNetworkLength()) {
playerName = playerName.substring(0, getMaxNameNetworkLength()); playerName = playerName.substring(0, getMaxNameNetworkLength());
} }
return !network.equals(playerName) || !hasPermission(player, "stargate.create.personal"); return !network.equals(playerName) || !hasPermission(player, Permission.CREATE_PERSONAL);
} }
/** /**
@@ -215,14 +248,8 @@ public final class PermissionHelper {
* @param server <p>The server the player is trying to connect to</p> * @param server <p>The server the player is trying to connect to</p>
* @return <p>True if the player is allowed to access the given server</p> * @return <p>True if the player is allowed to access the given server</p>
*/ */
public static boolean canAccessServer(Player player, String server) { public static boolean canAccessServer(@NotNull Player player, @NotNull String server) {
//The player can access all servers return hasPermission(player, Permission.ACCESS_SERVER, server);
if (hasPermission(player, "stargate.server")) {
//Check if the server permission has been explicitly denied
return hasPermissionImplicit(player, "stargate.server." + server);
}
//The player can access the destination server
return hasPermission(player, "stargate.server." + server);
} }
/** /**
@@ -233,13 +260,13 @@ public final class PermissionHelper {
* @param dest <p>The portal the player wants to teleport to</p> * @param dest <p>The portal the player wants to teleport to</p>
* @return <p>True if the player can travel for free</p> * @return <p>True if the player can travel for free</p>
*/ */
public static boolean isFree(Player player, Portal src, Portal dest) { public static boolean isFree(@NotNull Player player, @NotNull Portal src, @Nullable Portal dest) {
//This portal is free //This portal is free
if (src.getOptions().isFree()) { if (src.getOptions().isFree()) {
return true; return true;
} }
//Player can use this portal for free //Player can use this portal for free
if (hasPermission(player, "stargate.free.use")) { if (hasPermission(player, Permission.FREE_USAGE)) {
return true; return true;
} }
//Don't charge for free destinations unless specified in the config //Don't charge for free destinations unless specified in the config
@@ -255,13 +282,13 @@ public final class PermissionHelper {
* @param portal <p>The portal to check</p> * @param portal <p>The portal to check</p>
* @return <p>True if the given player can see the given portal</p> * @return <p>True if the given player can see the given portal</p>
*/ */
public static boolean canSeePortal(Player player, Portal portal) { public static boolean canSeePortal(@NotNull Player player, @NotNull Portal portal) {
//The portal is not hidden //The portal is not hidden
if (!portal.getOptions().isHidden()) { if (!portal.getOptions().isHidden()) {
return true; return true;
} }
//The player can see all hidden portals //The player can see all hidden portals
if (hasPermission(player, "stargate.admin.hidden")) { if (hasPermission(player, Permission.SEE_HIDDEN)) {
return true; return true;
} }
//The player is the owner of the portal //The player is the owner of the portal
@@ -275,13 +302,13 @@ public final class PermissionHelper {
* @param portal <p>The private portal used</p> * @param portal <p>The private portal used</p>
* @return <p>True if the player is allowed to use the portal</p> * @return <p>True if the player is allowed to use the portal</p>
*/ */
public static boolean canUsePrivatePortal(Player player, Portal portal) { public static boolean canUsePrivatePortal(@NotNull Player player, @NotNull Portal portal) {
//Check if the player is the owner of the gate //Check if the player is the owner of the gate
if (portal.isOwner(player)) { if (portal.isOwner(player)) {
return true; return true;
} }
//The player is an admin with the ability to use private gates //The player is an admin with the ability to use private gates
return hasPermission(player, "stargate.admin.private"); return hasPermission(player, Permission.USE_PRIVATE);
} }
/** /**
@@ -291,8 +318,8 @@ public final class PermissionHelper {
* @param option <p>The option the player is trying to use</p> * @param option <p>The option the player is trying to use</p>
* @return <p>True if the player is allowed to create a portal with the given option</p> * @return <p>True if the player is allowed to create a portal with the given option</p>
*/ */
public static boolean canUseOption(Player player, PortalOption option) { public static boolean canUseOption(@NotNull Player player, @NotNull PortalOption option) {
return hasPermission(player, option.getPermissionString()); return hasPermission(player, option.getPermission());
} }
/** /**
@@ -302,14 +329,8 @@ public final class PermissionHelper {
* @param network <p>The network the player is trying to create a gate on</p> * @param network <p>The network the player is trying to create a gate on</p>
* @return <p>True if the player is allowed to create the new gate</p> * @return <p>True if the player is allowed to create the new gate</p>
*/ */
public static boolean canCreateNetworkGate(Player player, String network) { public static boolean canCreateNetworkGate(@NotNull Player player, @NotNull String network) {
//Check if the player is allowed to create a portal on any network return hasPermission(player, Permission.CREATE_NETWORK, network);
if (hasPermission(player, "stargate.create.network")) {
//Check if the network has been explicitly denied
return hasPermissionImplicit(player, "stargate.create.network." + network);
}
//Check if the player is allowed to create on this specific network
return hasPermission(player, "stargate.create.network." + network);
} }
/** /**
@@ -318,8 +339,8 @@ public final class PermissionHelper {
* @param player <p>The player trying to create the new gate</p> * @param player <p>The player trying to create the new gate</p>
* @return <p>True if the player is allowed</p> * @return <p>True if the player is allowed</p>
*/ */
public static boolean canCreatePersonalPortal(Player player) { public static boolean canCreatePersonalPortal(@NotNull Player player) {
return hasPermission(player, "stargate.create.personal"); return hasPermission(player, Permission.CREATE_PERSONAL);
} }
/** /**
@@ -329,14 +350,8 @@ public final class PermissionHelper {
* @param gate <p>The gate type of the new portal</p> * @param gate <p>The gate type of the new portal</p>
* @return <p>True if the player is allowed to create a portal with the given gate layout</p> * @return <p>True if the player is allowed to create a portal with the given gate layout</p>
*/ */
public static boolean canCreatePortal(Player player, String gate) { public static boolean canCreatePortal(@NotNull Player player, @NotNull String gate) {
//Check if the player is allowed to create all gates return hasPermission(player, Permission.CREATE_GATE, gate);
if (hasPermission(player, "stargate.create.gate")) {
//Check if the gate type has been explicitly denied
return hasPermissionImplicit(player, "stargate.create.gate." + gate);
}
//Check if the player can create the specific gate type
return hasPermission(player, "stargate.create.gate." + gate);
} }
/** /**
@@ -346,25 +361,19 @@ public final class PermissionHelper {
* @param portal <p>The portal to destroy</p> * @param portal <p>The portal to destroy</p>
* @return <p>True if the player is allowed to destroy the portal</p> * @return <p>True if the player is allowed to destroy the portal</p>
*/ */
public static boolean canDestroyPortal(Player player, Portal portal) { public static boolean canDestroyPortal(@NotNull Player player, @NotNull Portal portal) {
String network = portal.getCleanNetwork(); String network = portal.getCleanNetwork();
//Use a special check for bungee portals //Use a special check for bungee portals
if (portal.getOptions().isBungee()) { if (portal.getOptions().isBungee()) {
return hasPermission(player, "stargate.admin.bungee"); return hasPermission(player, Permission.CREATE_BUNGEE);
} }
//Check if the player is allowed to destroy on all networks if (hasPermission(player, Permission.DESTROY_NETWORK, network)) {
if (hasPermission(player, "stargate.destroy.network")) {
//Check if the network has been explicitly denied
return hasPermissionImplicit(player, "stargate.destroy.network." + network);
}
//Check if the player is allowed to destroy on the network
if (hasPermission(player, "stargate.destroy.network." + network)) {
return true; return true;
} }
//Check if personal portal and if the player is allowed to destroy it //Check if personal portal and if the player is allowed to destroy it
return portal.isOwner(player) && hasPermission(player, "stargate.destroy.personal"); return portal.isOwner(player) && hasPermission(player, Permission.DESTROY_PERSONAL);
} }
/** /**
@@ -376,7 +385,8 @@ public final class PermissionHelper {
* @param event <p>The move event causing the teleportation</p> * @param event <p>The move event causing the teleportation</p>
* @return <p>True if the player cannot teleport. False otherwise</p> * @return <p>True if the player cannot teleport. False otherwise</p>
*/ */
public static boolean playerCannotTeleport(Portal entrancePortal, Portal destination, Player player, PlayerMoveEvent event) { public static boolean playerCannotTeleport(@Nullable Portal entrancePortal, @Nullable Portal destination,
@NotNull Player player, @Nullable PlayerMoveEvent event) {
//No portal or not open //No portal or not open
if (entrancePortal == null || !entrancePortal.isOpen()) { if (entrancePortal == null || !entrancePortal.isOpen()) {
return true; return true;
@@ -384,24 +394,27 @@ public final class PermissionHelper {
//Not open for this player //Not open for this player
if (!entrancePortal.getPortalOpener().isOpenFor(player)) { if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
} }
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
return true; return true;
} }
//No destination //No destination
if (!entrancePortal.getOptions().isBungee() && destination == null) { boolean isBungee = entrancePortal.getOptions().isBungee();
if (!isBungee && destination == null) {
return true; return true;
} }
//Player cannot access portal //Player cannot access portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) { if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isQuiet()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
} }
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
Stargate.debug("PermissionHelper::playerCannotTeleport", "Closed portal because player is " +
"missing necessary permissions");
entrancePortal.getPortalOpener().closePortal(false); entrancePortal.getPortalOpener().closePortal(false);
return true; return true;
} }
@@ -414,4 +427,23 @@ public final class PermissionHelper {
return false; return false;
} }
/**
* Checks if the given player has a permission for the given value, or has the catch-all permission
*
* @param player <p>The player trying to create a portal</p>
* @param permission <p>The parent permission to check</p>
* @param value <p>The child node to check</p>
* @return <p>True if the player has the explicit child node, or gets the permission implicitly from the parent</p>
*/
private static boolean hasPermission(@NotNull Player player, @NotNull Permission permission, @NotNull String value) {
String fullNode = permission.getNode() + "." + value;
//Check if the player is allowed to create all gates
if (hasPermission(player, permission)) {
//Check if the gate type has been explicitly denied
return hasPermissionImplicit(player, fullNode);
}
//Check if the player can create the specific gate type
return hasPermission(player, fullNode);
}
} }

View File

@@ -2,29 +2,34 @@ package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.ControlBlockUpdateRequest;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner; import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.PortalStrings;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler; import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.transformation.SimpleVectorOperation;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional; import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Waterlogged; import org.bukkit.block.data.Waterlogged;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Scanner; import java.util.Scanner;
import static net.knarcraft.stargate.portal.PortalSignDrawer.markPortalWithInvalidGate; import static net.knarcraft.stargate.portal.PortalSignDrawer.markPortalWithInvalidGate;
@@ -43,7 +48,7 @@ public final class PortalFileHelper {
* *
* @param world <p>The world to save portals for</p> * @param world <p>The world to save portals for</p>
*/ */
public static void saveAllPortals(World world) { public static void saveAllPortals(@NotNull World world) {
Stargate.getStargateConfig().addManagedWorld(world.getName()); Stargate.getStargateConfig().addManagedWorld(world.getName());
String saveFileLocation = Stargate.getPortalFolder() + "/" + world.getName() + ".db"; String saveFileLocation = Stargate.getPortalFolder() + "/" + world.getName() + ".db";
@@ -52,7 +57,7 @@ public final class PortalFileHelper {
for (Portal portal : PortalRegistry.getAllPortals()) { for (Portal portal : PortalRegistry.getAllPortals()) {
//Skip portals in other worlds //Skip portals in other worlds
String worldName = portal.getWorld().getName(); String worldName = portal.getLocation().getWorld().getName();
if (!worldName.equalsIgnoreCase(world.getName())) { if (!worldName.equalsIgnoreCase(world.getName())) {
continue; continue;
} }
@@ -61,8 +66,8 @@ public final class PortalFileHelper {
} }
bufferedWriter.close(); bufferedWriter.close();
} catch (Exception e) { } catch (Exception exception) {
Stargate.logSevere(String.format("Exception while writing stargates to %s: %s", saveFileLocation, e)); Stargate.logSevere(String.format("Exception while writing stargates to %s: %s", saveFileLocation, exception));
} }
} }
@@ -73,21 +78,21 @@ public final class PortalFileHelper {
* @param portal <p>The portal to save</p> * @param portal <p>The portal to save</p>
* @throws IOException <p>If unable to write to the buffered writer</p> * @throws IOException <p>If unable to write to the buffered writer</p>
*/ */
private static void savePortal(BufferedWriter bufferedWriter, Portal portal) throws IOException { private static void savePortal(@NotNull BufferedWriter bufferedWriter, @NotNull Portal portal) throws IOException {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
BlockLocation button = portal.getStructure().getButton(); Block button = portal.getStructure().getButton();
//WARNING: Because of the primitive save format, any change in order will break everything! //WARNING: Because of the primitive save format, any change in order will break everything!
builder.append(portal.getName()).append(':'); builder.append(portal.getName()).append(':');
builder.append(portal.getSignLocation().toString()).append(':'); builder.append(portal.getLocation().getSignBlock()).append(':');
builder.append((button != null) ? button.toString() : "").append(':'); builder.append((button != null) ? button.toString() : "").append(':');
//Add removes config values to keep indices consistent //Add removes config values to keep indices consistent
builder.append(0).append(':'); builder.append(0).append(':');
builder.append(0).append(':'); builder.append(0).append(':');
builder.append(portal.getYaw()).append(':'); builder.append(DirectionHelper.getYawFromBlockFace(portal.getLocation().getFacing())).append(':');
builder.append(portal.getTopLeft().toString()).append(':'); builder.append(portal.getLocation().getTopLeft()).append(':');
builder.append(portal.getGate().getFilename()).append(':'); builder.append(portal.getGate().getFilename()).append(':');
//Only save the destination name if the gate is fixed as it doesn't matter otherwise //Only save the destination name if the gate is fixed as it doesn't matter otherwise
@@ -111,20 +116,20 @@ public final class PortalFileHelper {
* @param portal <p>The portal to save</p> * @param portal <p>The portal to save</p>
* @param builder <p>The string builder to append to</p> * @param builder <p>The string builder to append to</p>
*/ */
private static void savePortalOptions(Portal portal, StringBuilder builder) { private static void savePortalOptions(@NotNull Portal portal, @NotNull StringBuilder builder) {
PortalOptions options = portal.getOptions(); PortalOptions options = portal.getOptions();
builder.append(':'); builder.append(':');
builder.append(options.isHidden()).append(':'); builder.append(options.isHidden()).append(':');
builder.append(options.isAlwaysOn()).append(':'); builder.append(options.isAlwaysOn()).append(':');
builder.append(options.isPrivate()).append(':'); builder.append(options.isPrivate()).append(':');
builder.append(portal.getWorld().getName()).append(':'); builder.append(portal.getLocation().getWorld().getName()).append(':');
builder.append(options.isFree()).append(':'); builder.append(options.isFree()).append(':');
builder.append(options.isBackwards()).append(':'); builder.append(options.isBackwards()).append(':');
builder.append(options.isShown()).append(':'); builder.append(options.isShown()).append(':');
builder.append(options.isNoNetwork()).append(':'); builder.append(options.isNoNetwork()).append(':');
builder.append(options.isRandom()).append(':'); builder.append(options.isRandom()).append(':');
builder.append(options.isBungee()).append(':'); builder.append(options.isBungee()).append(':');
builder.append(options.isSilent()).append(':'); builder.append(options.isQuiet()).append(':');
builder.append(options.hasNoSign()); builder.append(options.hasNoSign());
} }
@@ -134,7 +139,7 @@ public final class PortalFileHelper {
* @param world <p>The world to load portals for</p> * @param world <p>The world to load portals for</p>
* @return <p>True if portals could be loaded</p> * @return <p>True if portals could be loaded</p>
*/ */
public static boolean loadAllPortals(World world) { public static boolean loadAllPortals(@NotNull World world) {
String location = Stargate.getPortalFolder(); String location = Stargate.getPortalFolder();
File database = new File(location, world.getName() + ".db"); File database = new File(location, world.getName() + ".db");
@@ -154,7 +159,7 @@ public final class PortalFileHelper {
* @param database <p>The database file containing the portals</p> * @param database <p>The database file containing the portals</p>
* @return <p>True if the portals were loaded successfully</p> * @return <p>True if the portals were loaded successfully</p>
*/ */
private static boolean loadPortals(World world, File database) { private static boolean loadPortals(@NotNull World world, @NotNull File database) {
int lineIndex = 0; int lineIndex = 0;
try { try {
Scanner scanner = new Scanner(database); Scanner scanner = new Scanner(database);
@@ -170,10 +175,9 @@ public final class PortalFileHelper {
"Starting post loading tasks", world)); "Starting post loading tasks", world));
doPostLoadTasks(world, needsToSaveDatabase); doPostLoadTasks(world, needsToSaveDatabase);
return true; return true;
} catch (Exception e) { } catch (Exception exception) {
Stargate.logSevere(String.format("Exception while reading stargates from %s: %d", database.getName(), Stargate.logSevere(String.format("Exception while reading stargates from %s: %d! Message: %s",
lineIndex)); database.getName(), lineIndex, exception.getMessage()));
e.printStackTrace();
} }
return false; return false;
} }
@@ -186,7 +190,7 @@ public final class PortalFileHelper {
* @param world <p>The world for which portals are currently being read</p> * @param world <p>The world for which portals are currently being read</p>
* @return <p>True if the read portal has changed and the world's database needs to be saved</p> * @return <p>True if the read portal has changed and the world's database needs to be saved</p>
*/ */
private static boolean readPortalLine(Scanner scanner, int lineIndex, World world) { private static boolean readPortalLine(@NotNull Scanner scanner, int lineIndex, @NotNull World world) {
String line = scanner.nextLine().trim(); String line = scanner.nextLine().trim();
//Ignore empty and comment lines //Ignore empty and comment lines
@@ -214,21 +218,26 @@ public final class PortalFileHelper {
* @param world <p>The world portals have been loaded for</p> * @param world <p>The world portals have been loaded for</p>
* @param needsToSaveDatabase <p>Whether the portal database's file needs to be updated</p> * @param needsToSaveDatabase <p>Whether the portal database's file needs to be updated</p>
*/ */
private static void doPostLoadTasks(World world, boolean needsToSaveDatabase) { private static void doPostLoadTasks(@NotNull World world, boolean needsToSaveDatabase) {
//Open any always-on portals. Do this here as it should be more efficient than in the loop. //Open any always-on portals. Do this here as it should be more efficient than in the loop.
PortalHandler.verifyAllPortals(); PortalUtil.verifyAllPortals();
int portalCount = PortalRegistry.getAllPortals().size(); int portalCount = PortalRegistry.getAllPortals().size();
int openCount = PortalHandler.openAlwaysOpenPortals(); int openCount = PortalUtil.openAlwaysOpenPortals();
//Print info about loaded stargates so that admins can see if all stargates loaded //Print info about loaded stargates so that admins can see if all stargates loaded
Stargate.logInfo(String.format("{%s} Loaded %d stargates with %d set as always-on", world.getName(), Stargate.logInfo(String.format("{%s} Loaded %d stargates with %d set as always-on", world.getName(),
portalCount, openCount)); portalCount, openCount));
//Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since //Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since
Stargate.debug("PortalFileHelper::doPostLoadTasks::update",
String.format("Queueing portal sign/button updates for %s", world));
for (Portal portal : PortalRegistry.getAllPortals()) { for (Portal portal : PortalRegistry.getAllPortals()) {
if (portal.isRegistered()) { if (portal.isRegistered() && portal.getLocation().getWorld().equals(world) &&
portal.drawSign(); world.getWorldBorder().isInside(portal.getLocation().getSignBlock().getLocation())) {
updatePortalButton(portal); Stargate.addControlBlockUpdateRequest(new ControlBlockUpdateRequest(portal));
Stargate.debug("UpdateSignsButtons", String.format("Queued sign and button updates for portal %s",
portal.getName()));
} }
} }
//Save the portals to disk to update with any changes //Save the portals to disk to update with any changes
@@ -246,16 +255,16 @@ public final class PortalFileHelper {
* @param lineIndex <p>The line index to report in case the user needs to fix an error</p> * @param lineIndex <p>The line index to report in case the user needs to fix an error</p>
* @return <p>True if the portal's data has changed and its database needs to be updated</p> * @return <p>True if the portal's data has changed and its database needs to be updated</p>
*/ */
private static boolean loadPortal(String[] portalData, World world, int lineIndex) { private static boolean loadPortal(@NotNull String[] portalData, @NotNull World world, int lineIndex) {
//Load min. required portal data //Load min. required portal data
String name = portalData[0]; String name = portalData[0];
BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null; Block button = (!portalData[2].isEmpty()) ? getBlock(world, portalData[2]) : null;
//Load the portal's location //Load the portal's location
PortalLocation portalLocation = new PortalLocation();
portalLocation.setSignLocation(new BlockLocation(world, portalData[1]));
portalLocation.setYaw(Float.parseFloat(portalData[5])); PortalLocation portalLocation = new PortalLocation(getBlock(world, portalData[6]),
portalLocation.setTopLeft(new BlockLocation(world, portalData[6])); DirectionHelper.getBlockFaceFromYaw(Float.parseFloat(portalData[5])), getBlock(world, portalData[1]), button);
//Check if the portal's gate type exists and is loaded //Check if the portal's gate type exists and is loaded
Gate gate = GateHandler.getGateByName(portalData[7]); Gate gate = GateHandler.getGateByName(portalData[7]);
@@ -275,48 +284,30 @@ public final class PortalFileHelper {
PortalOwner owner = new PortalOwner(ownerString); PortalOwner owner = new PortalOwner(ownerString);
//Create the new portal //Create the new portal
Portal portal = new Portal(portalLocation, button, destination, name, network, gate, owner, PortalStrings portalStrings = new PortalStrings(name, network, destination);
PortalHandler.getPortalOptions(portalData)); Portal portal = new Portal(portalLocation, button, portalStrings, gate, owner,
PortalUtil.getPortalOptions(portalData));
//Register the portal, and close it in case it wasn't properly closed when the server stopped //Register the portal, and close it in case it wasn't properly closed when the server stopped
boolean buttonLocationChanged = updateButtonVector(portal); boolean buttonLocationChanged = updateButtonVector(portal);
PortalHandler.registerPortal(portal); PortalUtil.registerPortal(portal);
portal.getPortalOpener().closePortal(true); portal.getPortalOpener().closePortal(true);
return buttonLocationChanged; return buttonLocationChanged;
} }
/**
* Updates a portal's button if it does not match the correct material
*
* @param portal <p>The portal update the button of</p>
*/
private static void updatePortalButton(Portal portal) {
BlockLocation buttonLocation = getButtonLocation(portal);
if (portal.getOptions().isAlwaysOn()) {
//Clear button if not already air or water
if (MaterialHelper.isButtonCompatible(buttonLocation.getType())) {
Material newMaterial = decideRemovalMaterial(buttonLocation, portal);
Stargate.addBlockChangeRequest(new BlockChangeRequest(buttonLocation, newMaterial, null));
}
} else {
//Replace button if the material does not match
if (buttonLocation.getType() != portal.getGate().getPortalButton()) {
generatePortalButton(portal, DirectionHelper.getBlockFaceFromYaw(portal.getYaw()));
}
}
}
/** /**
* Decides the material to use for removing a portal's button/sign * Decides the material to use for removing a portal's button/sign
* *
* @param location <p>The location of the button/sign to replace</p> * @param block <p>The location of the button/sign to replace</p>
* @param portal <p>The portal the button/sign belongs to</p> * @param portal <p>The portal the button/sign belongs to</p>
* @return <p>The material to use for removing the button/sign</p> * @return <p>The material to use for removing the button/sign</p>
*/ */
public static Material decideRemovalMaterial(BlockLocation location, Portal portal) { @NotNull
public static Material decideRemovalMaterial(@NotNull Block block, @NotNull Portal portal) {
//Get the blocks to each side of the location //Get the blocks to each side of the location
Location leftLocation = location.getRelativeLocation(-1, 0, 0, portal.getYaw()); SimpleVectorOperation vectorOperation = portal.getLocation().getVectorOperation();
Location rightLocation = location.getRelativeLocation(1, 0, 0, portal.getYaw()); Location leftLocation = block.getLocation().clone().add(vectorOperation.performToRealSpaceOperation(new Vector(-1, 0, 0)));
Location rightLocation = block.getLocation().clone().add(vectorOperation.performToRealSpaceOperation(new Vector(1, 0, 0)));
//If the block is water or is waterlogged, assume the portal is underwater //If the block is water or is waterlogged, assume the portal is underwater
if (isUnderwater(leftLocation) || isUnderwater(rightLocation)) { if (isUnderwater(leftLocation) || isUnderwater(rightLocation)) {
@@ -334,7 +325,7 @@ public final class PortalFileHelper {
* @param location <p>The location to check</p> * @param location <p>The location to check</p>
* @return <p>True if the location is underwater</p> * @return <p>True if the location is underwater</p>
*/ */
private static boolean isUnderwater(Location location) { private static boolean isUnderwater(@NotNull Location location) {
BlockData blockData = location.getBlock().getBlockData(); BlockData blockData = location.getBlock().getBlockData();
return blockData.getMaterial() == Material.WATER || return blockData.getMaterial() == Material.WATER ||
(blockData instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()); (blockData instanceof Waterlogged waterlogged && waterlogged.isWaterlogged());
@@ -349,18 +340,15 @@ public final class PortalFileHelper {
* @param portal <p>The portal to update the button vector for</p> * @param portal <p>The portal to update the button vector for</p>
* @return <p>True if the calculated button location is not the same as the one in the portal file</p> * @return <p>True if the calculated button location is not the same as the one in the portal file</p>
*/ */
private static boolean updateButtonVector(Portal portal) { private static boolean updateButtonVector(@NotNull Portal portal) {
for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) { for (BlockVector control : portal.getGate().getLayout().getControls()) {
BlockLocation controlLocation = portal.getLocation().getTopLeft().getRelativeLocation(control, Block buttonLocation = portal.getLocation().getRelative(control.clone().add(new Vector(0, 0, 1)).toBlockVector());
portal.getYaw()); if (!buttonLocation.equals(portal.getLocation().getSignBlock())) {
BlockLocation buttonLocation = controlLocation.getRelativeLocation( portal.getLocation().setButtonBlock(buttonLocation);
new RelativeBlockVector(0, 0, 1), portal.getYaw());
if (!buttonLocation.equals(portal.getLocation().getSignLocation())) {
portal.getLocation().setButtonVector(control);
BlockLocation oldButtonLocation = portal.getStructure().getButton(); Block oldButtonLocation = portal.getStructure().getButton();
if (oldButtonLocation != null && !oldButtonLocation.equals(buttonLocation)) { if (oldButtonLocation != null && !oldButtonLocation.equals(buttonLocation)) {
Stargate.addBlockChangeRequest(new BlockChangeRequest(oldButtonLocation, Material.AIR, null)); Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(oldButtonLocation, Material.AIR, null));
portal.getStructure().setButton(buttonLocation); portal.getStructure().setButton(buttonLocation);
return true; return true;
} }
@@ -375,27 +363,40 @@ public final class PortalFileHelper {
* @param portal <p>The portal to generate button for</p> * @param portal <p>The portal to generate button for</p>
* @param buttonFacing <p>The direction the button should be facing</p> * @param buttonFacing <p>The direction the button should be facing</p>
*/ */
public static void generatePortalButton(Portal portal, BlockFace buttonFacing) { public static void generatePortalButton(@NotNull Portal portal, @NotNull BlockFace buttonFacing) {
//Go one block outwards to find the button's location rather than the control block's location //Go one block outwards to find the button's location rather than the control block's location
BlockLocation button = getButtonLocation(portal); Block button = portal.getLocation().getButtonBlock();
Directional buttonData = (Directional) Bukkit.createBlockData(portal.getGate().getPortalButton()); // If the button location is null here, it is assumed that the button generation wasn't necessary
buttonData.setFacing(buttonFacing); if (button == null) {
button.getBlock().setBlockData(buttonData); return;
}
if (!MaterialHelper.isButtonCompatible(button.getType())) {
@NotNull List<Material> possibleMaterials = MaterialHelper.specifiersToMaterials(
portal.getGate().getPortalButtonMaterials()).stream().toList();
Material buttonType = ListHelper.getRandom(possibleMaterials);
Directional buttonData = (Directional) Bukkit.createBlockData(buttonType);
buttonData.setFacing(buttonFacing);
button.setBlockData(buttonData);
}
portal.getStructure().setButton(button); portal.getStructure().setButton(button);
} }
/** /**
* Gets the location of a portal's button * Gets the block specified in the input
* *
* @param portal <p>The portal to find the button for</p> * @param world <p>The world the block belongs to</p>
* @return <p>The location of the portal's button</p> * @param string <p>Comma-separated coordinate string</p>
* @return <p>The specified block</p>
* @throws NumberFormatException <p>If non-numeric values are encountered</p>
*/ */
private static BlockLocation getButtonLocation(Portal portal) { @NotNull
BlockLocation topLeft = portal.getTopLeft(); private static Block getBlock(@NotNull World world, @NotNull String string) throws NumberFormatException {
RelativeBlockVector buttonVector = portal.getLocation().getButtonVector(); String[] parts = string.split(",");
return topLeft.getRelativeLocation(buttonVector.addToVector(RelativeBlockVector.Property.OUT, 1), return new Location(world, Integer.parseInt(parts[0]), Integer.parseInt(parts[1]),
portal.getYaw()); Integer.parseInt(parts[2])).getBlock();
} }
} }

View File

@@ -1,18 +1,25 @@
package net.knarcraft.stargate.portal; package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.config.Permission;
import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.config.material.BukkitTagSpecifier;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.property.PortalStructure; import net.knarcraft.stargate.portal.property.PortalStructure;
import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler; import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.Tag;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -22,9 +29,9 @@ import java.util.Map;
/** /**
* Keeps track of all loaded portals, and handles portal creation * Keeps track of all loaded portals, and handles portal creation
*/ */
public class PortalHandler { public final class PortalUtil {
private PortalHandler() { private PortalUtil() {
} }
@@ -33,26 +40,19 @@ public class PortalHandler {
* *
* @return <p>A copy of all portal networks</p> * @return <p>A copy of all portal networks</p>
*/ */
@NotNull
public static Map<String, List<String>> getAllPortalNetworks() { public static Map<String, List<String>> getAllPortalNetworks() {
return PortalRegistry.getAllPortalNetworks(); return PortalRegistry.getAllPortalNetworks();
} }
/**
* Gets a copy of all bungee portals
*
* @return <p>A copy of all bungee portals</p>
*/
public static Map<String, Portal> getBungeePortals() {
return PortalRegistry.getBungeePortals();
}
/** /**
* Gets names of all portals within a network * Gets names of all portals within a network
* *
* @param network <p>The network to get portals from</p> * @param network <p>The network to get portals from</p>
* @return <p>A list of portal names</p> * @return <p>A list of portal names</p>
*/ */
public static List<String> getNetwork(String network) { @Nullable
public static List<String> getNetwork(@NotNull String network) {
return PortalRegistry.getNetwork(network); return PortalRegistry.getNetwork(network);
} }
@@ -64,53 +64,72 @@ public class PortalHandler {
* @param network <p>The network to get destinations from</p> * @param network <p>The network to get destinations from</p>
* @return <p>All destinations the player can go to</p> * @return <p>All destinations the player can go to</p>
*/ */
public static List<String> getDestinations(Portal entrancePortal, Player player, String network) { @NotNull
public static List<String> getDestinations(@NotNull Portal entrancePortal, @Nullable Player player,
@NotNull String network) {
List<String> destinations = new ArrayList<>(); List<String> destinations = new ArrayList<>();
for (String destination : PortalRegistry.getAllPortalNetworks().get(network)) { List<String> portalsInNetwork = PortalRegistry.getNetwork(network);
if (portalsInNetwork == null) {
return List.of();
}
for (String destination : portalsInNetwork) {
Portal portal = getByName(destination, network); Portal portal = getByName(destination, network);
if (portal == null) { if (portal == null) {
continue; continue;
} }
//Check if destination is a random portal
if (portal.getOptions().isRandom()) { if (isDestinationAvailable(entrancePortal, portal, player)) {
continue;
}
//Check if destination is always open (Don't show if so)
if (portal.getOptions().isAlwaysOn() && !portal.getOptions().isShown()) {
continue;
}
//Check if destination is this portal
if (destination.equals(entrancePortal.getCleanName())) {
continue;
}
//Check if destination is a fixed portal not pointing to this portal
if (portal.getOptions().isFixed() &&
!Portal.cleanString(portal.getDestinationName()).equals(entrancePortal.getCleanName())) {
continue;
}
//Allow random use by non-players (Minecarts)
if (player == null) {
destinations.add(portal.getName());
continue;
}
//Check if this player can access the destination world
if (PermissionHelper.cannotAccessWorld(player, portal.getWorld().getName())) {
continue;
}
//The portal is visible to the player
if (PermissionHelper.canSeePortal(player, portal)) {
destinations.add(portal.getName()); destinations.add(portal.getName());
} }
} }
return destinations; return destinations;
} }
/**
* Checks whether the given destination is available to the given player
*
* @param entrancePortal <p>The portal the player has activated</p>
* @param destinationPortal <p>The destination portal to check if is valid</p>
* @param player <p>The player trying to attempt the destination</p>
* @return <p>True if the destination is available</p>
*/
private static boolean isDestinationAvailable(@NotNull Portal entrancePortal, @NotNull Portal destinationPortal,
@Nullable Player player) {
//Check if destination is a random portal
if (destinationPortal.getOptions().isRandom()) {
return false;
}
//Check if destination is always open (Don't show if so)
if (destinationPortal.getOptions().isAlwaysOn() && !destinationPortal.getOptions().isShown()) {
return false;
}
//Check if destination is this portal
if (destinationPortal.equals(entrancePortal)) {
return false;
}
//Check if destination is a fixed portal not pointing to this portal
if (destinationPortal.getOptions().isFixed() &&
!Portal.cleanString(destinationPortal.getDestinationName()).equals(entrancePortal.getCleanName())) {
return false;
}
//Allow random use by non-players (Minecarts)
if (player == null) {
return true;
}
//Check if this player can access the destination world
if (PermissionHelper.cannotAccessWorld(player, destinationPortal.getLocation().getWorld().getName())) {
return false;
}
//The portal is visible to the player
return PermissionHelper.canSeePortal(player, destinationPortal);
}
/** /**
* Registers a portal * Registers a portal
* *
* @param portal <p>The portal to register</p> * @param portal <p>The portal to register</p>
*/ */
public static void registerPortal(Portal portal) { public static void registerPortal(@NotNull Portal portal) {
PortalRegistry.registerPortal(portal); PortalRegistry.registerPortal(portal);
} }
@@ -123,19 +142,20 @@ public class PortalHandler {
* @param network <p>The name of the portal's network</p> * @param network <p>The name of the portal's network</p>
* @return <p>False if the portal is an invalid bungee portal. True otherwise</p> * @return <p>False if the portal is an invalid bungee portal. True otherwise</p>
*/ */
static boolean isValidBungeePortal(Map<PortalOption, Boolean> portalOptions, Player player, public static boolean isValidBungeePortal(@NotNull Map<PortalOption, Boolean> portalOptions, @NotNull Player player,
String destinationName, String network) { @NotNull String destinationName, String network) {
if (portalOptions.get(PortalOption.BUNGEE)) { if (!portalOptions.get(PortalOption.BUNGEE)) {
if (!PermissionHelper.hasPermission(player, "stargate.admin.bungee")) { return true;
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDeny")); }
return false; if (!PermissionHelper.hasPermission(player, Permission.CREATE_BUNGEE)) {
} else if (!Stargate.getGateConfig().enableBungee()) { new SGFormatBuilder(Message.BUNGEE_CREATION_DENIED).error(player);
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled")); return false;
return false; } else if (!Stargate.getGateConfig().enableBungee()) {
} else if (destinationName.isEmpty() || network.isEmpty()) { new SGFormatBuilder(Message.BUNGEE_DISABLED).error(player);
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeEmpty")); return false;
return false; } else if (destinationName.isEmpty() || network.isEmpty()) {
} new SGFormatBuilder(Message.BUNGEE_MISSING_INFO).error(player);
return false;
} }
return true; return true;
} }
@@ -144,34 +164,40 @@ public class PortalHandler {
* Tries to find a gate matching the portal the user is trying to create * Tries to find a gate matching the portal the user is trying to create
* *
* @param portalLocation <p>The location data for the new portal</p> * @param portalLocation <p>The location data for the new portal</p>
* @param world <p>The world the player is located in</p>
* @return <p>The matching gate type, or null if no such gate could be found</p> * @return <p>The matching gate type, or null if no such gate could be found</p>
*/ */
static Gate findMatchingGate(PortalLocation portalLocation, World world) { @Nullable
Block signParent = portalLocation.getSignLocation().getParent(); public static Gate findMatchingGate(@NotNull PortalLocation portalLocation) {
BlockLocation parent = new BlockLocation(world, signParent.getX(), signParent.getY(), Block signParent = DirectionHelper.getParent(portalLocation.getSignBlock());
signParent.getZ()); if (signParent == null) {
return null;
}
//Get all gates with the used type of control blocks //Get all gates with the used type of control blocks
Gate[] possibleGates = GateHandler.getGatesByControlBlock(signParent); List<Gate> possibleGates = GateHandler.getGatesByControlBlock(signParent);
double yaw = portalLocation.getYaw();
Gate gate = null; Gate gate = null;
for (Gate possibleGate : possibleGates) { for (Gate possibleGate : possibleGates) {
//Get gate controls //Get gate controls
RelativeBlockVector[] vectors = possibleGate.getLayout().getControls(); BlockVector[] vectors = possibleGate.getLayout().getControls();
portalLocation.setButtonVector(null); portalLocation.setButtonBlock(null);
for (RelativeBlockVector controlVector : vectors) { for (BlockVector controlVector : vectors) {
//Assuming the top-left location is pointing to the gate's top-left location, check if it's a match //Assuming the top-left location is pointing to the gate's top-left location, check if it's a match
BlockLocation possibleTopLocation = parent.getRelativeLocation(controlVector.invert(), yaw); Block possibleTopLocation = portalLocation.getRelative(controlVector);
if (possibleGate.matches(possibleTopLocation, portalLocation.getYaw(), true)) {
if (possibleGate.matches(possibleTopLocation, portalLocation.getFacing(), true)) {
gate = possibleGate; gate = possibleGate;
portalLocation.setTopLeft(possibleTopLocation); portalLocation.setTopLeft(possibleTopLocation);
} else { } else {
portalLocation.setButtonVector(controlVector); portalLocation.setButtonBlock(possibleTopLocation);
} }
} }
//If our gate has been found, look no further
if (gate != null) {
break;
}
} }
return gate; return gate;
@@ -182,12 +208,16 @@ public class PortalHandler {
* *
* @param portal <p>The newly created portal</p> * @param portal <p>The newly created portal</p>
*/ */
static void updatePortalsPointingAtNewPortal(Portal portal) { public static void updatePortalsPointingAtNewPortal(@NotNull Portal portal) {
for (String originName : PortalRegistry.getAllPortalNetworks().get(portal.getCleanNetwork())) { List<String> portalsInNetwork = PortalRegistry.getNetwork(portal.getCleanNetwork());
if (portalsInNetwork == null) {
return;
}
for (String originName : portalsInNetwork) {
Portal origin = getByName(originName, portal.getCleanNetwork()); Portal origin = getByName(originName, portal.getCleanNetwork());
if (origin == null || if (origin == null ||
!Portal.cleanString(origin.getDestinationName()).equals(portal.getCleanName()) || !Portal.cleanString(origin.getDestinationName()).equals(portal.getCleanName()) ||
!origin.getStructure().isVerified()) { !new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(origin.getLocation().getSignBlock().getType())) {
continue; continue;
} }
//Update sign of fixed gates pointing at this gate //Update sign of fixed gates pointing at this gate
@@ -209,7 +239,9 @@ public class PortalHandler {
* @param options <p>The string on the option line of the sign</p> * @param options <p>The string on the option line of the sign</p>
* @return <p>A map containing all portal options and their values</p> * @return <p>A map containing all portal options and their values</p>
*/ */
static Map<PortalOption, Boolean> getPortalOptions(Player player, String destinationName, String options) { @NotNull
public static Map<PortalOption, Boolean> getPortalOptions(@NotNull Player player, @NotNull String destinationName,
@NotNull String options) {
Map<PortalOption, Boolean> portalOptions = new HashMap<>(); Map<PortalOption, Boolean> portalOptions = new HashMap<>();
for (PortalOption option : PortalOption.values()) { for (PortalOption option : PortalOption.values()) {
portalOptions.put(option, options.indexOf(option.getCharacterRepresentation()) != -1 && portalOptions.put(option, options.indexOf(option.getCharacterRepresentation()) != -1 &&
@@ -217,7 +249,7 @@ public class PortalHandler {
} }
//Can not create a non-fixed always-on portal //Can not create a non-fixed always-on portal
if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.length() == 0) { if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.isEmpty()) {
portalOptions.put(PortalOption.ALWAYS_ON, false); portalOptions.put(PortalOption.ALWAYS_ON, false);
} }
@@ -247,13 +279,9 @@ public class PortalHandler {
* @param network <p>The network the portal is connected to</p> * @param network <p>The network the portal is connected to</p>
* @return <p>The portal with the given name or null</p> * @return <p>The portal with the given name or null</p>
*/ */
public static Portal getByName(String name, String network) { @Nullable
Map<String, Map<String, Portal>> lookupMap = PortalRegistry.getPortalLookupByNetwork(); public static Portal getByName(@NotNull String name, @NotNull String network) {
if (!lookupMap.containsKey(network.toLowerCase())) { return PortalRegistry.getPortalInNetwork(Portal.cleanString(network), Portal.cleanString(name));
return null;
}
return lookupMap.get(network.toLowerCase()).get(name.toLowerCase());
} }
/** /**
@@ -262,9 +290,12 @@ public class PortalHandler {
* @param location <p>The location of the portal's entrance</p> * @param location <p>The location of the portal's entrance</p>
* @return <p>The portal at the given location</p> * @return <p>The portal at the given location</p>
*/ */
public static Portal getByEntrance(Location location) { @Nullable
return PortalRegistry.getLookupEntrances().get(new BlockLocation(location.getWorld(), location.getBlockX(), public static Portal getByEntrance(@NotNull Location location) {
location.getBlockY(), location.getBlockZ())); if (location.getWorld() == null) {
return null;
}
return PortalRegistry.getPortalFromEntrance(location.getBlock());
} }
/** /**
@@ -273,8 +304,9 @@ public class PortalHandler {
* @param block <p>The block at the portal's entrance</p> * @param block <p>The block at the portal's entrance</p>
* @return <p>The portal at the given block's location</p> * @return <p>The portal at the given block's location</p>
*/ */
public static Portal getByEntrance(Block block) { @Nullable
return PortalRegistry.getLookupEntrances().get(new BlockLocation(block)); public static Portal getByEntrance(@NotNull Block block) {
return PortalRegistry.getPortalFromEntrance(block);
} }
/** /**
@@ -283,7 +315,8 @@ public class PortalHandler {
* @param location <p>A location adjacent to the portal's entrance</p> * @param location <p>A location adjacent to the portal's entrance</p>
* @return <p>The portal adjacent to the given location</p> * @return <p>The portal adjacent to the given location</p>
*/ */
public static Portal getByAdjacentEntrance(Location location) { @Nullable
public static Portal getByAdjacentEntrance(@NotNull Location location) {
return getByAdjacentEntrance(location, 1); return getByAdjacentEntrance(location, 1);
} }
@@ -294,26 +327,27 @@ public class PortalHandler {
* @param range <p>The range to scan for portals</p> * @param range <p>The range to scan for portals</p>
* @return <p>The portal adjacent to the given location</p> * @return <p>The portal adjacent to the given location</p>
*/ */
public static Portal getByAdjacentEntrance(Location location, int range) { @Nullable
List<BlockLocation> adjacentPositions = new ArrayList<>(); public static Portal getByAdjacentEntrance(@NotNull Location location, int range) {
BlockLocation centerLocation = new BlockLocation(location.getBlock()); List<Block> adjacentPositions = new ArrayList<>();
Block centerLocation = location.getBlock();
adjacentPositions.add(centerLocation); adjacentPositions.add(centerLocation);
for (int index = 1; index <= range; index++) { for (int index = 1; index <= range; index++) {
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, 0)); adjacentPositions.add(location.clone().add(new Vector(index, 0, 0)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, 0)); adjacentPositions.add(location.clone().add(new Vector(-index, 0, 0)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(0, 0, index)); adjacentPositions.add(location.clone().add(new Vector(0, 0, index)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(0, 0, -index)); adjacentPositions.add(location.clone().add(new Vector(0, 0, -index)).getBlock());
if (index < range) { if (index < range) {
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, index)); adjacentPositions.add(location.clone().add(new Vector(index, 0, index)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, -index)); adjacentPositions.add(location.clone().add(new Vector(-index, 0, -index)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, -index)); adjacentPositions.add(location.clone().add(new Vector(index, 0, -index)).getBlock());
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, index)); adjacentPositions.add(location.clone().add(new Vector(-index, 0, index)).getBlock());
} }
} }
for (BlockLocation adjacentPosition : adjacentPositions) { for (Block adjacentPosition : adjacentPositions) {
Portal portal = PortalRegistry.getLookupEntrances().get(adjacentPosition); Portal portal = PortalRegistry.getPortalFromEntrance(adjacentPosition);
if (portal != null) { if (portal != null) {
return portal; return portal;
} }
@@ -327,8 +361,9 @@ public class PortalHandler {
* @param block <p>The portal's control block</p> * @param block <p>The portal's control block</p>
* @return <p>The portal with the given control block</p> * @return <p>The portal with the given control block</p>
*/ */
public static Portal getByControl(Block block) { @Nullable
return PortalRegistry.getLookupControls().get(new BlockLocation(block)); public static Portal getByControl(@NotNull Block block) {
return PortalRegistry.getPortalFromControl(block);
} }
/** /**
@@ -337,8 +372,9 @@ public class PortalHandler {
* @param block <p>One of the loaded lookup blocks</p> * @param block <p>One of the loaded lookup blocks</p>
* @return <p>The portal corresponding to the block</p> * @return <p>The portal corresponding to the block</p>
*/ */
public static Portal getByBlock(Block block) { @Nullable
return PortalRegistry.getLookupBlocks().get(new BlockLocation(block)); public static Portal getByBlock(@NotNull Block block) {
return PortalRegistry.getPortalFromFrame(block);
} }
/** /**
@@ -347,8 +383,9 @@ public class PortalHandler {
* @param name <p>The name of the bungee portal to get</p> * @param name <p>The name of the bungee portal to get</p>
* @return <p>A bungee portal</p> * @return <p>A bungee portal</p>
*/ */
public static Portal getBungeePortal(String name) { @Nullable
return PortalRegistry.getBungeePortals().get(name.toLowerCase()); public static Portal getBungeePortal(@NotNull String name) {
return PortalRegistry.getBungeePortal(name);
} }
/** /**
@@ -357,7 +394,8 @@ public class PortalHandler {
* @param portalData <p>The string list containing all information about a portal</p> * @param portalData <p>The string list containing all information about a portal</p>
* @return <p>A map between portal options and booleans</p> * @return <p>A map between portal options and booleans</p>
*/ */
public static Map<PortalOption, Boolean> getPortalOptions(String[] portalData) { @NotNull
public static Map<PortalOption, Boolean> getPortalOptions(@NotNull String[] portalData) {
Map<PortalOption, Boolean> portalOptions = new HashMap<>(); Map<PortalOption, Boolean> portalOptions = new HashMap<>();
for (PortalOption option : PortalOption.values()) { for (PortalOption option : PortalOption.values()) {
int saveIndex = option.getSaveIndex(); int saveIndex = option.getSaveIndex();
@@ -394,7 +432,19 @@ public class PortalHandler {
for (Portal portal : PortalRegistry.getAllPortals()) { for (Portal portal : PortalRegistry.getAllPortals()) {
//Try and verify the portal. Invalidate it if it cannot be validated //Try and verify the portal. Invalidate it if it cannot be validated
PortalStructure structure = portal.getStructure(); PortalStructure structure = portal.getStructure();
if (!structure.wasVerified() && (!structure.isVerified() || !structure.checkIntegrity())) {
Stargate.debug("PortalHandler::verifyAllPortals", "Checking portal: " + portal.getName() + " | " + portal.getNetwork());
if (!portal.getOptions().hasNoSign() && !(new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(
portal.getLocation().getSignBlock().getType()))) {
Stargate.debug("PortalHandler::verifyAllPortals", "Stargate is missing its sign");
invalidPortals.add(portal);
} else if (!portal.getOptions().isAlwaysOn() && portal.getLocation().getButtonBlock() != null &&
!MaterialHelper.isButtonCompatible(portal.getLocation().getButtonBlock().getLocation().add(
new Vector(0, 0, 1)).getBlock().getType())) {
Stargate.debug("PortalHandler::verifyAllPortals", "Stargate is missing a valid button");
invalidPortals.add(portal);
} else if (!structure.checkIntegrity()) {
Stargate.debug("PortalHandler::verifyAllPortals", "Stargate's border or entrance has invalid blocks");
invalidPortals.add(portal); invalidPortals.add(portal);
} }
} }
@@ -410,18 +460,19 @@ public class PortalHandler {
* *
* @param portal <p>The portal of the star portal</p> * @param portal <p>The portal of the star portal</p>
*/ */
private static void unregisterInvalidPortal(Portal portal) { private static void unregisterInvalidPortal(@NotNull Portal portal) {
//Show debug information //Show debug information
for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) { for (BlockVector control : portal.getGate().getLayout().getControls()) {
Block block = portal.getBlockAt(control).getBlock(); Block block = portal.getLocation().getRelative(control);
//Log control blocks not matching the gate layout //Log control blocks not matching the gate layout
if (!block.getType().equals(portal.getGate().getControlBlock())) { if (!MaterialHelper.specifiersToMaterials(portal.getGate().getControlBlockMaterials()).contains(
Stargate.debug("PortalHandler::destroyInvalidPortal", "Control Block Type == " + block.getType())) {
Stargate.debug("PortalHandler::unregisterInvalidPortal", "Control Block Type == " +
block.getType().name()); block.getType().name());
} }
} }
PortalRegistry.unregisterPortal(portal, false); PortalRegistry.unregisterPortal(portal, false);
Stargate.logInfo(String.format("Destroying stargate at %s", portal)); Stargate.logInfo(String.format("Disabled stargate at %s", portal));
} }
/** /**
@@ -442,10 +493,12 @@ public class PortalHandler {
* @param input <p>The name to filter</p> * @param input <p>The name to filter</p>
* @return <p>The filtered name</p> * @return <p>The filtered name</p>
*/ */
public static String filterName(String input) { @NotNull
public static String filterName(@Nullable String input) {
if (input == null) { if (input == null) {
return ""; return "";
} }
return input.replaceAll("[|:#]", "").trim(); return input.replaceAll("[|:#]", "").trim();
} }
} }

View File

@@ -0,0 +1,107 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalOption;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
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.HashMap;
import java.util.Map;
/**
* A helper class for saving and loading Stargates from or to YML files
*/
public final class PortalYMLHelper {
private PortalYMLHelper() {
}
/**
* Saves all portals
*
* @throws IOException <p>If unable to read or write a portal file</p>
*/
public void savePortals() throws IOException {
Map<World, File> worldFiles = new HashMap<>();
Map<World, FileConfiguration> configurations = new HashMap<>();
// Get and alter each configuration without saving
for (Portal portal : PortalRegistry.getAllPortals()) {
World world = portal.getLocation().getWorld();
File file = worldFiles.get(world);
FileConfiguration configuration = configurations.get(world);
if (file == null) {
file = new File(Stargate.getStargateConfig().getPortalFolder(), world.getUID() + ".yml");
worldFiles.put(world, file);
}
if (configuration == null) {
configuration = new YamlConfiguration();
configurations.put(world, configuration);
}
savePortal(portal, configuration);
}
// Save each configuration
for (Map.Entry<World, FileConfiguration> configuration : configurations.entrySet()) {
configuration.getValue().save(worldFiles.get(configuration.getKey()));
}
}
/**
* Saves the given portal
*
* @param portal <p>The portal to save</p>
*/
public void savePortal(@NotNull Portal portal, @NotNull FileConfiguration configuration) {
String root = portal.getNetwork() + "." + portal.getName();
ConfigurationSection portalSection = configuration.createSection(root);
portalSection.set("name", portal.getName());
portalSection.set("network", portal.getNetwork());
portalSection.set("destination", portal.getOptions().isFixed() ? portal.getDestinationName() : null);
portalSection.set("owner", portal.getOwner().getIdentifier());
portalSection.set("gate", portal.getGate().getFilename());
portalSection.set("topLeft", portal.getLocation().getTopLeft());
portalSection.set("signLocation", portal.getLocation().getSignBlock());
portalSection.set("buttonLocation", portal.getLocation().getButtonBlock());
portalSection.set("facing", portal.getLocation().getFacing());
for (PortalOption option : PortalOption.values()) {
portalSection.set("options." + option.getCharacterRepresentation(), portal.getOptions().checkOption(option));
}
}
@Nullable
public Portal loadPortal(@NotNull ConfigurationSection portalSection) {
/* PortalLocation location = new PortalLocation();
BlockLocation signLocation = (BlockLocation) portalSection.get("signLocation");
BlockLocation topLeft = (BlockLocation) portalSection.get("topLeft");
if (signLocation == null || topLeft == null) {
return null;
}
BlockFace facing = (BlockFace) Objects.requireNonNull(portalSection.get("facing"));
location.setSignLocation(signLocation).setButtonFacing(facing).setYaw(BlockFace.);
BlockLocation button;
PortalStrings portalStrings = new PortalStrings();
Gate gate = GateHandler.getGateByName();
PortalOwner owner;
Map<PortalOption, Boolean> options;
return new Portal(location, button, portalStrings, gate, owner, options);*/
return null;
}
}

View File

@@ -0,0 +1,51 @@
package net.knarcraft.stargate.utility;
import org.bukkit.DyeColor;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A helper class for dealing with signs
*/
public final class SignHelper {
private SignHelper() {
}
/**
* Gets the lines of the given sign
*
* @param sign <p>The sign to get lines from</p>
* @return <p>The lines of the sign</p>
*/
@NotNull
public static String[] getLines(@NotNull Sign sign) {
return sign.getSide(Side.FRONT).getLines();
}
/**
* Gets the dye color of the given sign
*
* @param sign <p>The sign to check</p>
* @return <p>The dye currently applied to the sign</p>
*/
@Nullable
public static DyeColor getDye(@NotNull Sign sign) {
return sign.getSide(Side.FRONT).getColor();
}
/**
* Sets the text of a line on a sign
*
* @param sign <p>The sign to set text for</p>
* @param line <p>The line to set</p>
* @param text <p>The text to set</p>
*/
public static void setSignLine(@NotNull Sign sign, int line, @NotNull String text) {
sign.getSide(Side.FRONT).setLine(line, text);
}
}

View File

@@ -0,0 +1,253 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.formatting.Message;
import net.knarcraft.stargate.config.formatting.SGFormatBuilder;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.teleporter.EntityTeleporter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class with methods for various teleportation tasks
*
* <p>The teleport helper mainly helps with passengers and leashed creatures</p>
*/
public final class TeleportHelper {
private TeleportHelper() {
}
/**
* Checks whether a player has leashed creatures that block the teleportation
*
* @param player <p>The player trying to teleport</p>
* @return <p>False if the player has leashed any creatures that cannot go through the portal</p>
*/
public static boolean noLeashedCreaturesPreventTeleportation(@NotNull Player player) {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyCreatures = getLeashedCreatures(player);
//Disallow creatures with passengers to prevent smuggling
for (Creature creature : nearbyCreatures) {
if (!creature.getPassengers().isEmpty()) {
return false;
}
}
//If it's enabled, there is no problem
if (Stargate.getGateConfig().handleLeashedCreatures()) {
return true;
} else {
return nearbyCreatures.isEmpty();
}
}
/**
* Gets all creatures leashed by a player within the given range
*
* @param player <p>The player to check</p>
* @return <p>A list of all creatures the player is holding in a leash (lead)</p>
*/
@NotNull
public static List<Creature> getLeashedCreatures(@NotNull Player player) {
List<Creature> leashedCreatures = new ArrayList<>();
//Find any nearby leashed entities to teleport with the player
List<Entity> nearbyEntities = player.getNearbyEntities(15, 15, 15);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Entity entity : nearbyEntities) {
if (entity instanceof Creature creature && creature.isLeashed() && creature.getLeashHolder() == player) {
leashedCreatures.add(creature);
}
}
return leashedCreatures;
}
/**
* Teleports and adds a passenger to an entity
*
* <p>Teleportation of living vehicles is really buggy if you wait between the teleportation and passenger adding,
* but there needs to be a delay between teleporting the vehicle and teleporting and adding the passenger.</p>
*
* @param targetVehicle <p>The entity to add the passenger to</p>
* @param passenger <p>The passenger to teleport and add</p>
* @param exitDirection <p>The direction of any passengers exiting the stargate</p>
* @param newVelocity <p>The new velocity of the teleported passenger</p>
*/
public static void teleportAndAddPassenger(@NotNull Entity targetVehicle, @NotNull Entity passenger,
@NotNull Vector exitDirection, @NotNull Vector newVelocity) {
Location passengerExit = targetVehicle.getLocation().clone().setDirection(exitDirection);
if (!passenger.teleport(passengerExit)) {
Stargate.debug("TeleportHelper::handleVehiclePassengers", "Failed to teleport passenger" +
passenger);
} else {
Stargate.debug("TeleportHelper::handleVehiclePassengers", "Teleported " + passenger +
" to " + passengerExit);
}
if (!targetVehicle.addPassenger(passenger)) {
Stargate.debug("TeleportHelper::handleVehiclePassengers", "Failed to add passenger" +
passenger);
} else {
Stargate.debug("TeleportHelper::handleVehiclePassengers", "Added passenger " + passenger +
" to " + targetVehicle);
}
Stargate.debug("VehicleTeleporter::teleportVehicle", "Setting velocity " + newVelocity +
" for passenger " + passenger);
passenger.setVelocity(newVelocity);
}
/**
* Ejects, teleports and adds all passengers to the target entity
*
* @param passengers <p>The passengers to handle</p>
* @param entity <p>The entity the passengers should be put into</p
* @param origin <p>The portal the entity teleported from</p>
* @param target <p>The portal the entity is teleporting to</p>
* @param exitRotation <p>The rotation of any passengers exiting the stargate</p>
* @param newVelocity <p>The new velocity of the teleported passengers</p>
*/
public static void handleEntityPassengers(@NotNull List<Entity> passengers, @NotNull Entity entity,
@NotNull Portal origin, @NotNull Portal target,
@NotNull Vector exitRotation, @NotNull Vector newVelocity) {
for (Entity passenger : passengers) {
List<Entity> passengerPassengers = passenger.getPassengers();
if (!passengerPassengers.isEmpty()) {
Stargate.debug("Teleporter::handleEntityPassengers", "Found the entities: " +
passengerPassengers + " as passengers of " + entity);
}
if (passenger.eject()) {
//Teleport any passengers of the passenger
handleEntityPassengers(passengerPassengers, passenger, origin, target, exitRotation, newVelocity);
}
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
if (passenger instanceof Player player) {
//Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin, target);
}
teleportAndAddPassenger(entity, passenger, exitRotation, newVelocity);
}, passenger instanceof Player ? Stargate.getGateConfig().waitForPlayerAfterTeleportDelay() : 0);
}
}
/**
* Teleports any creatures leashed by the player
*
* <p>Will return false if the teleportation should be aborted because the player has leashed creatures that
* aren't allowed to be teleported with the player.</p>
*
* @param player <p>The player which is teleported</p>
* @param origin <p>The portal the player is teleporting from</p>
* @param target <p>The portal the player is teleporting to</p>
*/
public static void teleportLeashedCreatures(@NotNull Player player, @NotNull Portal origin, @NotNull Portal target) {
//If this feature is disabled, just return
if (!Stargate.getGateConfig().handleLeashedCreatures()) {
return;
}
BukkitScheduler scheduler = Bukkit.getScheduler();
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyEntities = TeleportHelper.getLeashedCreatures(player);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Creature creature : nearbyEntities) {
creature.setLeashHolder(null);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
new EntityTeleporter(target, creature).teleportEntity(origin);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player),
Stargate.getGateConfig().waitForPlayerAfterTeleportDelay());
}, 2);
}
}
/**
* Checks whether a list of entities or any of their passengers contains any non-players
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one entity is not a player</p>
*/
public static boolean containsNonPlayer(@NotNull List<Entity> entities) {
for (Entity entity : entities) {
if (!(entity instanceof Player) || containsNonPlayer(entity.getPassengers())) {
return true;
}
}
return false;
}
/**
* Checks whether a list of entities of their passengers contains at least one player
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one player is present among the passengers</p>
*/
public static boolean containsPlayer(@NotNull List<Entity> entities) {
for (Entity entity : entities) {
if (entity instanceof Player || containsPlayer(entity.getPassengers())) {
return true;
}
}
return false;
}
/**
* Gets all players recursively from a list of entities
*
* @param entities <p>The entities to check for players</p>
* @return <p>The found players</p>
*/
@NotNull
public static List<Player> getPlayers(@NotNull List<Entity> entities) {
List<Player> players = new ArrayList<>(5);
for (Entity entity : entities) {
if (entity instanceof Player) {
players.add((Player) entity);
}
players.addAll(getPlayers(entity.getPassengers()));
}
return players;
}
/**
* Checks whether the given player is allowed to and can afford to teleport
*
* @param player <p>The player trying to teleport</p>
* @param entrancePortal <p>The portal the player is entering</p>
* @param destinationPortal <p>The portal the player is to exit from</p>
* @return <p>True if the player is allowed to teleport and is able to pay necessary fees</p>
*/
public static boolean playerCanTeleport(@NotNull Player player, @NotNull Portal entrancePortal,
@NotNull Portal destinationPortal) {
//Make sure the user can access the portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destinationPortal)) {
if (!entrancePortal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.ACCESS_DENIED).error(player);
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Check if the player is able to afford the teleport fee
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
if (!canAffordFee) {
if (!entrancePortal.getOptions().isQuiet()) {
new SGFormatBuilder(Message.ECONOMY_INSUFFICIENT).error(player);
}
return false;
}
return TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
}
}

View File

@@ -2,11 +2,11 @@ package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalOwner; import net.knarcraft.stargate.portal.property.PortalOwner;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World; import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -35,7 +35,7 @@ public final class UUIDMigrationHelper {
* *
* @param player <p>The player to migrate</p> * @param player <p>The player to migrate</p>
*/ */
public static void migrateUUID(OfflinePlayer player) { public static void migrateUUID(@NotNull OfflinePlayer player) {
Map<String, List<Portal>> playersToMigrate = getPlayersToMigrate(); Map<String, List<Portal>> playersToMigrate = getPlayersToMigrate();
String playerName = player.getName(); String playerName = player.getName();
@@ -53,7 +53,7 @@ public final class UUIDMigrationHelper {
migratePortalsToUUID(portalsOwned, player.getUniqueId()); migratePortalsToUUID(portalsOwned, player.getUniqueId());
//Remove the player to prevent the migration to happen every time the player joins //Remove the player to prevent the migration from happening every time the player joins
playersToMigrate.remove(playerName); playersToMigrate.remove(playerName);
} }
@@ -63,15 +63,15 @@ public final class UUIDMigrationHelper {
* @param portals <p>The portals to migrate</p> * @param portals <p>The portals to migrate</p>
* @param uniqueId <p>The unique ID of the portals' owner</p> * @param uniqueId <p>The unique ID of the portals' owner</p>
*/ */
private static void migratePortalsToUUID(List<Portal> portals, UUID uniqueId) { private static void migratePortalsToUUID(@NotNull List<Portal> portals, @NotNull UUID uniqueId) {
Set<World> worldsToSave = new HashSet<>(); Set<World> worldsToSave = new HashSet<>();
//Get the real portal from the copy and set UUID //Get the real portal from the copy and set UUID
for (Portal portalCopy : portals) { for (Portal portalCopy : portals) {
Portal portal = PortalHandler.getByName(portalCopy.getCleanName(), portalCopy.getCleanNetwork()); Portal portal = PortalUtil.getByName(portalCopy.getCleanName(), portalCopy.getCleanNetwork());
if (portal != null) { if (portal != null) {
portal.getOwner().setUUID(uniqueId); portal.getOwner().setUUID(uniqueId);
worldsToSave.add(portal.getWorld()); worldsToSave.add(portal.getLocation().getWorld());
} }
} }
@@ -86,6 +86,7 @@ public final class UUIDMigrationHelper {
* *
* @return <p>The player names to migrate</p> * @return <p>The player names to migrate</p>
*/ */
@NotNull
private static Map<String, List<Portal>> getPlayersToMigrate() { private static Map<String, List<Portal>> getPlayersToMigrate() {
//Make sure to only go through portals once //Make sure to only go through portals once
if (playerNamesToMigrate != null) { if (playerNamesToMigrate != null) {

View File

@@ -1,87 +0,0 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.logging.Level;
/**
* The update checker is responsible for looking for new updates
*/
public final class UpdateChecker {
private final static String APIResourceURL = "https://api.spigotmc.org/legacy/update.php?resource=97784";
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() {
BukkitScheduler scheduler = Stargate.getInstance().getServer().getScheduler();
scheduler.runTaskAsynchronously(Stargate.getInstance(), UpdateChecker::queryAPI);
}
/**
* Queries the spigot API to check for a newer version, and informs the user
*/
private static void queryAPI() {
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 = Stargate.getPluginVersion();
//If there is a newer version, notify the user
if (isVersionHigher(oldVersion, newVersion)) {
Stargate.getConsoleLogger().log(Level.INFO, Stargate.getBackupString("prefix") +
getUpdateAvailableString(newVersion, oldVersion));
Stargate.setUpdateAvailable(newVersion);
}
} catch (IOException e) {
Stargate.debug("UpdateChecker", "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;
}
}

Some files were not shown because too many files have changed in this diff Show More