140 Commits

Author SHA1 Message Date
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
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
a3ed1058e6 Improves the double click prevention by accounting for heavy concurrent usage and server lag 2022-01-27 22:20:38 +01:00
f70ba24e95 Un-does the outwards offset reduction for teleported entities 2022-01-27 21:56:19 +01:00
f6438eb872 Updates the plugin version to 0.9.3.2 2022-01-27 21:39:13 +01:00
2bf5422b2a Updates README with recent changes 2022-01-27 21:37:59 +01:00
f95ee0b85d Reduces the outwards offset of teleported entities 2022-01-27 21:35:40 +01:00
1e06e0e01d Fixes some rotation problems for teleported passengers 2022-01-27 21:35:04 +01:00
bddf8c55d5 Adds an option for setting the exit velocity of a teleporting player
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-27 05:15:43 +01:00
05a5fb2160 Updates version to 0.9.3.1
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-27 01:32:49 +01:00
d9f535cd07 Updates README 2022-01-27 00:59:45 +01:00
62952611b8 Ignores the type of air when verifying a stargate's opening 2022-01-27 00:58:42 +01:00
29fdc8f849 Adds a new feature to the README 2022-01-27 00:44:31 +01:00
043c5f2408 Updates README and version to 0.9.3.0 2022-01-26 23:41:05 +01:00
a74d47e999 Makes sure default config values are stored with comments on the first run 2022-01-26 23:37:34 +01:00
e86be3e848 Finishes the work required for changing per-sign colors using the /sg config command 2022-01-26 23:27:31 +01:00
366cd3107e Updates per-sign inverted colors when the main or highlighting color is changed 2022-01-26 20:30:16 +01:00
02f6c6e9fc Updates README with recent changes 2022-01-26 18:17:10 +01:00
97670c9367 Adds the possibility to use the inverted default colors for per-sign colors
Adds code for getting inverted colors if "inverted" is given as the per-sign color
Fixes exceptions thrown when the per-sign config value is malformed
Changes the default to use inverted colors for crimson, dark_oak, spruce and warped signs
Adds methods to get main and highlighting colors from the portal sign drawer in case the given default colors are invalid and fallback colors are necessary instead
2022-01-26 18:02:53 +01:00
2773079a09 Uses the inverted color for a dyed sign's highlighting color 2022-01-26 15:49:07 +01:00
071f1895de Makes sure only the owner and players with stargate.admin.dye can dye stargate signs 2022-01-26 14:57:15 +01:00
4cae54e46f Adds support for dyed stargate signs
A dyed sign's color will now take priority over its main sign color. Black is used as the "default" color, so use black dye to remove dye effects
Adds a new class to keep track of a sign and its colors
Updates a bunch of code to make everything account for the dye color
2022-01-26 14:40:29 +01:00
7d41b75bac Makes string list config values display their default value properly
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-26 05:09:34 +01:00
404b4d0543 Changes the comment format for config files 2022-01-26 02:07:07 +01:00
842fd9c452 Fixes a few bugs regarding the new sign color system
Fixes missing STRING_LIST in loadConfig switch
Fixes missing color valid check in loadGateConfig
Fixes typing of PER_SIGN_COLORS
2022-01-26 02:06:42 +01:00
2bb0e8670d Makes a lot of changes necessary for RGB and per-sign color configuration support 2022-01-25 16:46:29 +01:00
acbdcd3ce3 Fixes a hang caused by MockBukkit 2022-01-25 15:34:27 +01:00
948f92f140 Updates Spigot and MockBukkit versions
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2022-01-15 21:59:46 +01:00
5d1d6ffaf0 Removes some unnecessary calculation when converting relative vectors to real vectors 2021-12-03 23:23:19 +01:00
a7dc02eef0 Updates plugin version and README
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-12-02 04:19:44 +01:00
9177773a0e Updates Spigot and Java version 2021-12-02 04:05:27 +01:00
fc5bac937a Changes the max age of gateway blocks to prevent the occasional purple beam
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-26 16:55:22 +01:00
f8ae83bc08 Updates README and version to 0.9.2.4
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-25 14:56:44 +01:00
cfb4910977 Adds a toggle-able admin alert when any admin joins the server 2021-11-25 14:56:05 +01:00
a61a03be33 Adds an update checker for alerting about new updates 2021-11-25 14:54:50 +01:00
445638a561 Releases a small hotfix for sign color as version 0.9.2.3 2021-11-25 04:27:36 +01:00
7f9dc6756b Updates version to 0.9.2.2 2021-11-25 03:58:42 +01:00
b2bb995d7e Prevents teleportation of a leashed creature with a passenger 2021-11-25 03:54:09 +01:00
f92fd2de5d Prevents loading of gate files using non-blocks as part of the border 2021-11-25 03:39:15 +01:00
2ed0fe7817 Updates README with recent changes 2021-11-25 03:26:12 +01:00
27b964837f Fixes a potential exception when a portal with an invalid gate type is missing a sign 2021-11-25 03:25:58 +01:00
4b34ea3cf5 Fixes a potential exception when a gate's open-block or closed-block is not a block 2021-11-25 03:25:22 +01:00
0740cd0a66 Removes the error message displayed when teleportation is cancelled because handleLeashedCreatures is disabled
The message should be removed as it's inconsistent with how disabled vehicle teleportation is silent
2021-11-25 02:02:20 +01:00
bab51d76fd Prevents teleportation of players holding one or more creatures on a leash if handleLeashedCreatures is disabled 2021-11-25 01:53:47 +01:00
6ddc15eeef Merge branch 'config-reloading' into dev 2021-11-24 22:39:15 +01:00
14511f558d Updates readme and version to 0.9.2.1
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-24 22:38:44 +01:00
32975ca35d Rewrites a lot of the config command to only do the minimum necessary steps to load the changes
Adds a ConfigTag class for helping to decide the action necessary for updating a given config option
Splits the color setting in PortalSignDrawer as only one color is set at a time when the /config command is used
Updates the configOptions map when a config option is changed
2021-11-24 22:33:45 +01:00
6e7ac5dbb9 Makes the protectEntrance option protect the entrance from all block placement 2021-11-24 22:20:47 +01:00
2b4d15dab4 Adds some boldness to the description and fixes an inaccuracy
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-24 03:06:35 +01:00
22bb75d4df Moves some methods from EconomyConfig to EconomyHelper and tries to simplify EconomyConfig
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-23 07:20:48 +01:00
ab9a118d49 Prevents an empty deny reason from being displayed
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-22 22:01:43 +01:00
a80a8520ce Improves the description of the /sg command 2021-11-22 17:58:56 +01:00
48bb68d665 Adds a missing 'a' in a sentence
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-21 13:34:52 +01:00
1247c54899 Fixes some wrong migration information 2021-11-21 13:10:13 +01:00
37ac3d4877 Fixes a debug route
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-15 17:25:03 +01:00
70495220eb Simplifies some function calls
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-15 17:18:00 +01:00
10c3914a60 Makes the free gate color configurable 2021-11-15 00:35:28 +01:00
4699f717ec Ignores color codes and case when sorting destination names
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-14 14:35:45 +01:00
29c1a00fcd Makes the max portal name and network character limit more easily changeable 2021-11-13 19:28:17 +01:00
51f5420f9e Removes the replacement of spaces to underscores for the cleanString method
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-13 17:04:13 +01:00
e1a3d2d560 Makes a fixed sign always use the destination portal's actual name if possible 2021-11-13 17:02:13 +01:00
a84210d550 Fixes a couple of bugs which prevented portals using spaces in the name from being properly connected 2021-11-13 03:44:27 +01:00
351d1762e7 Updates changelog, and updates version to 0.9.2.0
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-12 15:50:32 +01:00
2c53b7d2a6 Makes portal names and networks case and color-insensitive and increases length limit to 13. #17
Ignores &[0-9a-f] color codes when counting towards the name and network string limits
Makes portal lists store cleaned portal and network names to ignore case and color
Names and networks will now match regardless of case and color
Increases portal name/network limit to 13 characters
2021-11-12 15:33:15 +01:00
42e02eb141 Makes the UUIDMigrationHelper final 2021-11-12 13:46:01 +01:00
ad310ddb9c Allows a sneaking player to see information about a silent stargate (0.9.1.2)
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-10 22:54:20 +01:00
3db630f60e Updates version and changelog
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-09 22:51:11 +01:00
7a03f49fb1 Translates the & character to make sure portal signs are colored on all servers 2021-11-09 22:47:38 +01:00
1c2cad2ec1 Updates version to 0.9.1.0, updates README and adds a permission for changing config values
Adds the stargate.admin.config permission, which is required to use /sg config
2021-11-09 21:16:53 +01:00
b4d908eaa0 Improves tab completion for Stargate commands by taking into account typed text
Sends tab completion for the config command to the config tab completer
2021-11-09 20:58:55 +01:00
8546fd4f78 Fully implements the config command 2021-11-09 20:57:06 +01:00
05328fc456 Adds a tab completer for the config sub-command 2021-11-09 20:56:43 +01:00
aba70b669e Adds a method to be able to read config options 2021-11-09 18:21:25 +01:00
d5f4b87e9b Changes how config values are loaded by using the ConfigOption class 2021-11-09 15:40:10 +01:00
85edaa4657 Adds a new class to represent a data type usable by configs 2021-11-09 15:38:42 +01:00
7c501cefe8 Gives all config options data types to make loading config values work properly 2021-11-09 15:38:10 +01:00
6466c7b0ff Adds the config command to the stargate auto completer
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
Additionally makes the reload command only auto-complete if the command sender can use it
2021-11-09 02:04:59 +01:00
37cf75ada1 Adds the config command as a child to the Stargate command 2021-11-09 02:01:58 +01:00
1ca2d36f5f Adds an unfinished implementation of the config command, which is only able to display config options for now 2021-11-09 02:01:11 +01:00
01b2907b01 Adds an enum containing information about all config options 2021-11-09 01:59:54 +01:00
94b9848b70 Updates version and README
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-08 15:17:29 +01:00
2a17714e8d Makes sure to only remove buttons from always-on portals if the block is a type of button 2021-11-08 15:16:51 +01:00
901f9c555c Prevents the sign location of a portal with no sing from being added to lookup blocks and controls 2021-11-08 15:15:58 +01:00
1efd89cdb0 Makes sure to not display portal information when placing a block 2021-11-08 15:14:21 +01:00
88bb02dfbd Adds translation for portal information shown when right-clicking
Adds a distinction between network and server when displaying portal information
Adds translated portal info strings for english and both flavors of Norwegian
2021-11-08 14:16:44 +01:00
4db6274dc3 Fixes underwater signs and buttons being replaced with AIR instead of water 2021-11-08 01:34:18 +01:00
9c963c9e8c Renames Portal to Stargate when displaying information about a clicked portal
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-07 14:02:33 +01:00
fc744b04dc Adds an option for a stargate without a sign #10 2021-11-07 13:41:19 +01:00
1565707809 De-activates an unregistered portal to prevent its sign to be re-drawn if destroyed while activated 2021-11-07 13:40:12 +01:00
527562bc60 Fixes code formatting 2021-11-07 04:29:25 +01:00
42e208402e Documents stargates using several border materials 2021-11-07 04:27:39 +01:00
7f91808baf Adds a new default stargate to display the possibility of using several border materials 2021-11-07 04:26:23 +01:00
0540498818 Moves the drawing of unregistered signs to the portal sign drawer 2021-11-07 04:04:14 +01:00
57ec7071cf Updates README changelog 2021-11-06 18:12:47 +01:00
5f2d7988e2 Fixes button updating
Fixes a bug causing signs to be removed
Makes the old button properly disappear
Saves the portal database(s) to store the new button location if necessary
2021-11-06 18:08:01 +01:00
ac25f2747f Adds String.format to some debug strings 2021-11-06 18:06:00 +01:00
aa3bb58b33 Fixes some bugs regarding sign drawing and button updating
Fixes a bug displaying a portal as usable even if it's been unregistered
Fixes a bug which causes the portal button to be re-generated, even if the portal has been unregistered
2021-11-06 15:33:06 +01:00
ee0e66e9be Fixes the order in which the portal's button is updated to fix one of the bugs in #15
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-06 14:49:56 +01:00
f90a09143f Makes sure to also clear bungee portals when clearing portals 2021-11-06 04:10:15 +01:00
8c4cf16375 Adds an option for silent stargates which don't print teleportation messages or errors to the player's chat #7 2021-11-05 23:39:18 +01:00
4566c15350 Updates version to 0.9.0.6 and updates README 2021-11-05 21:43:38 +01:00
0297d62d6d Sets the always on option to true for bungee portals and removes some unnecessary checks 2021-11-05 21:40:06 +01:00
80ff241d4b Makes stargates' buttons update when portals are loaded #14 2021-11-05 21:38:33 +01:00
f3292cff99 Makes containers used as buttons no longer open when right-clicked 2021-11-05 19:21:27 +01:00
8c37b11484 Increases the delay before setting the leash holder for teleported creatures as they sometimes got stuck
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-05 14:04:24 +01:00
e0ac9b41e7 Adds some sub-packages to the portal package to improve logical structure
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-04 00:07:03 +01:00
e5c1ad1f3a 0.9.0.5 - Adds three new options to disable features of vehicle teleportation with more granularity #9
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-03 15:55:56 +01:00
cab99e11b0 Adds leashed teleportation as a feature to the README
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-01 19:25:52 +01:00
3c4d10ae3f Updates changelog and version to 0.9.0.4 2021-11-01 18:49:05 +01:00
aff0082906 Adds a config option to disable leashed creatures from teleporting with a player 2021-11-01 18:44:10 +01:00
20c3c93c06 Adds a few fixes which seem to make leash teleportation work as expected
Un-leashes and re-leashes teleported leashed entities
Delays leashed creature teleportation and the re-leashing to prevent odd behavior such as infinite leash or no-ai creature
2021-11-01 17:54:38 +01:00
dab9378a67 Merge branch 'master' into leashed-teleportation 2021-11-01 17:05:34 +01:00
62661f65f4 Updates info and version to 0.9.0.3
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-11-01 16:32:56 +01:00
b8d98c26d4 Adds triggers for teleporting leashed entities 2021-11-01 16:28:13 +01:00
8bb9c464d6 Adds some code for teleporting creatures leashed by a player 2021-11-01 16:27:29 +01:00
2b5d791581 Adds the EntityTeleporter for generic teleportation of entities 2021-11-01 16:26:46 +01:00
2a61480684 Adds UUID fetching on player join. See #12
Whenever a player joins, their names will be checked against a map containing all names which need to be migrated to UUID. All portals the player has created which still use the player name will be updated.
2021-11-01 15:13:22 +01:00
91a0316e6e Adds a missing insufficient funds message when checking if vehicle passengers can pay for the teleportation 2021-11-01 13:54:31 +01:00
70 changed files with 3957 additions and 1083 deletions

217
README.md
View File

@ -3,16 +3,22 @@
Create gates that allow for instant-teleportation between large distances. Gates can be always-open or triggered; they
can share a network or be split into clusters; they can be hidden on a network or accessible to everybody.
- Player permissions -- let players build their own networks.
- Vault economy support -- can add costs for create, destroy and use.
- Ability to create custom gate configurations. Three different default gate configurations are available.
- Message customization
- Multiple built-in languages (de, en, es, fr, hu, it, nb-no, nl, nn-no, pt-br, ru)
- Teleport across worlds or servers (BungeeCord supported)
- Vehicle teleportation -- teleport minecarts, boats, horses, pigs and striders
- Underwater portals -- portals can be placed underwater as long as a waterproof button is used
- API available -- using the API, a lot of behavior can be changed
- Button customization -- a large amount of materials usable as buttons (buttons, wall corals, shulkers, chests)
- **Player permissions** -- let players build their own networks.
- **Vault economy support** -- can add costs for create, destroy and use.
- **Ability to create custom gate configurations**. Four different default gate configurations are available.
- **Message customization**
- **Multiple built-in languages** (de, en, es, fr, hu, it, nb-no, nl, nn-no, pt-br, ru, zh_cn)
- **Teleport across worlds or servers** (BungeeCord supported)
- **Vehicle teleportation** -- teleport minecarts, boats, horses, pigs and striders
- **Leashed teleportation** -- teleport any creature in a leash with the player
- **Underwater portals** -- portals can be placed underwater as long as a waterproof button is used
- **API available** -- using the API, a lot of behavior can be changed
- **Button customization** -- a large amount of materials usable as buttons (buttons, wall corals, shulkers, chests)
- **Config commands** -- All main config values can be changed from the commandline
- **Color customization** -- Stargate signs can be colored in many ways. Colors can be set globally, or on a per sign
type basis
- **RGB and dye support** -- Signs can use RGB colors (using hex codes) as their main and highlighting colors, and can
also be dyed on a per-sign basis
## Background
@ -34,10 +40,8 @@ Permissions have had a few changes, so you should check the permissions section
permissions.
Payment to owner using Economy, through Vault, is only possible if the portal owner in the portal database is defined by
a UUID, and not a username. Right now, there is no automatic upgrade from usernames to UUID. You must either make the
stargate owner re-create the stargate or edit the file in the portals folder in a text editor. There are various ways to
find the UUID of players. You may look in the usercache.json file in the server directory or search for the username on
various websites.
a UUID, and not a username. A player name will be upgraded to a UUID when the player with the given name joins the
server.
# Permissions
@ -58,7 +62,9 @@ stargate.option -- Allow use of all options
stargate.option.backwards -- Allow use of 'B'ackwards
stargate.option.show -- Allow use of 'S'how
stargate.option.nonetwork -- Allow use of 'N'oNetwork
stargate.option.random -- Allow use of 'Random' stargates
stargate.option.random -- Allow use of 'R'andom stargates
stargate.option.silent -- Allow use of S'i'lent stargates
stargate.option.nosign -- Allow use of 'E' (No sign)
stargate.create -- Allow creating Stargates on any network (Override all create permissions)
stargate.create.personal -- Allow creating Stargates on network {playername}
@ -77,11 +83,13 @@ stargate.free -- Allow free use/creation/destruction of Stargates
stargate.free.create -- Allow free creation of Stargates
stargate.free.destroy -- Allow free destruction of Stargates
stargate.admin -- Allow all admin features (Hidden/Private only so far)
stargate.admin -- Allow all admin features (Hidden/Private bypass, BungeeCord, Reload, Config)
stargate.admin.private -- Allow use of Private gates not owned by user
stargate.admin.hidden -- Allow access to Hidden gates not ownerd by user
stargate.admin.bungee -- Allow the creation of BungeeCord stargates (U option)
stargate.admin.reload -- Allow use of the reload command
stargate.admin.config -- Allows the player to change config values from the chat
stargate.admin.dye -- Allows this player to change the dye of any stargate's sign
```
## Default Permissions
@ -114,9 +122,9 @@ section). See the Custom Gate Layout section to learn how to add custom gates.
### Sign Layout:
- Line 1: Gate Name (Max 11 characters)
- Line 2: Destination Name \[Optional] (Max 11 characters, used for fixed-gates only)
- Line 3: Network name \[Optional] (Max 11 characters)
- Line 1: Gate Name (Max 13 characters)
- Line 2: Destination Name \[Optional] (Max 13 characters, used for fixed-gates only)
- Line 3: Network name \[Optional] (Max 13 characters)
- Line 4: Options \[Optional] :
- 'A' for always-on fixed gate
- 'H' for hidden networked gate
@ -126,10 +134,14 @@ section). See the Custom Gate Layout section to learn how to add custom gates.
- 'S' is for showing an always-on gate in the network list
- 'N' is for hiding the network name
- 'R' is for random gates. These follow standard permissions of gates, but have a random exit location every time a
player enters.
- 'U' is for a gate connecting to another through bungee
player enters. (Implicitly always on)
- 'U' is for a gate connecting to another through bungee (Implicitly always on)
- 'I' is for a silent gate, which does not output anything to the chat while teleporting. Increases immersion
- 'E' is for a gate without a sign. Only for fixed stargates
The options are the single letter, not the word. So to make a private hidden gate, your 4th line would be 'PH'.
The options are the single letter, not the word. So to make a private hidden gate, your 4th line would be 'PH'. The
&\[0-9a-f] color codes are not counted in the character limit, thus allowing a 13-character name with an additional 2
characters used for the color code.
#### Gate networks:
@ -199,7 +211,11 @@ The key `button` is used to define the type of button that is generated for this
a type of wall coral (dead or alive), a type of shulker box or a chest.
`X` and `-` are used to define block types for the layout (Any single-character can be used, such as `#`).
In the gate format, you can see we use `X` to show where obsidian must be, `-` where the controls (Button/sign) are.
In the gate format, you can see we use `X` to show where obsidian must be, `-` where the controls (Button/sign) are.
For more complex gate designs, it is possible to add more materials. If you add something like a=GLOWSTONE, `a` can then
be used in the gate layout, just as `X` is used. See the `squarenetherglowstonegate.gate` file for an example.
You will also notice a `*` in the gate layout, this is the "exit point" of the gate, the block at which the player will
teleport in front of.
@ -283,17 +299,20 @@ while the per-gate costs re defined in the .gate files. To define a certain cost
```
language - The language to use (Included languages: en, de, es, fr, hu, it, nb-no, nl, nn-no, pt-br, ru)
adminUpdateAlert - Whether to alert admins about an available update when joining the server
folders:
portalFolder - The folder your portal databases are saved in
gateFolder - The folder containing your .gate files
gates:
maxGatesEachNetwork - If non-zero, will define the maximum amount of gates allowed on any network.
defaultGateNetwork - The default gate network
exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity (1 = same as entry velocity)
cosmetic:
rememberDestination - Whether to set the first destination as the last used destination for all gates
sortNetworkDestinations - If true, network lists will be sorted alphabetically.
mainSignColor - This allows you to specify the color of the gate signs.
highlightSignColor - This allows you to specify the color of the sign markings.
mainSignColor - This allows you to specify the color of the gate signs. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes.
highlightSignColor - This allows you to specify the color of the sign markings. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes.
perSignColors: - A list of per-sign color specifications. Format: "SIGN_TYPE:mainColor,highlight_color". The SIGN_TYPE is OAK for an oak sign, DARK_OAK for a dark oak sign and so on. The colors can be "default" to use the color specified in "mainSignColor" or "highlightSignColor", "inverted" to use the inverse color of the default color, a normal color such as BLACK,WHITE,YELLOW or a hex color code such as #ed76d9.
integrity:
destroyedByExplosion - Whether to destroy a stargate with explosions, or stop an explosion if it contains a gates controls.
verifyPortals - Whether or not all the non-sign blocks are checked to match the gate layout when an old stargate is loaded at startup.
@ -301,6 +320,11 @@ gates:
functionality:
enableBungee - Enable this for BungeeCord support. This allows portals across Bungee servers.
handleVehicles - Whether or not to handle vehicles going through gates. Set to false to disallow vehicles (Manned or not) going through gates.
handleEmptyVehicles - Whether or not to handle empty vehicles going through gates (chest/hopper/tnt/furnace minecarts included).
handleCreatureTransportation - Whether or not to handle players that transport creatures by sending vehicles (minecarts, boats) through gates.
handleNonPlayerVehicles - Whether or not to handle vehicles with a passenger which is not a player going through gates (pigs, horses, villagers, creepers, etc.). handleCreatureTransportation must be enabled.
handleLeashedCreatures - Whether or not to handle creatures leashed by a player going through gates. Set to false to disallow leashed creatures going through gates.
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
economy:
useEconomy - Whether or not to enable Economy using Vault (must have the Vault plugin)
createCost - The cost to create a stargate
@ -308,15 +332,18 @@ economy:
useCost - The cost to use a stargate
toOwner - Whether the money from gate-use goes to the owner or nobody
chargeFreeDestination - Enable to make players pay for teleportation even if the destination is free
freeGatesGreen - Enable to make gates that won't cost the player money show up as green
freeGatesColored - Enable to make gates that won't cost the player money show up as green
freeGatesColor - This allows you to specify the color of the markings and name of free stargates
debugging:
debug - Whether to show massive debug output
permissionDebug - Whether to show massive permission debug output
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.
```
# Message Customization
It is possible to customize all the messages Stargate displays, including the [Stargate] prefix. You can find the
It is possible to customize all the messages Stargate displays, including the \[Stargate] prefix. You can find the
strings in plugins/Stargate/lang/chosenLanguage.txt.
If a string is removed, or left blank, it will default to the default english string. There are some special cases
@ -363,10 +390,146 @@ bungeeDisabled=BungeeCord support is disabled.
bungeeDeny=You do not have permission to create BungeeCord gates.
bungeeEmpty=BungeeCord gates require both a destination and network.
bungeeSign=Teleport to
portalInfoTitle=[STARGATE INFO]
portalInfoName=Name: %name%
portalInfoDestination=Destination: %destination%
portalInfoNetwork=Network: %network%
portalInfoServer=Server: %server%
```
# Changes
#### \[Version 0.9.3.6] EpicKnarvik97 fork
- Adds the simplified Chinese language file provided by spigot user YKDZ
#### \[Version 0.9.3.5] EpicKnarvik97 fork
- Fixes the wait for player delay being too low by default
- Performs some minor code optimizations and restructuring
#### \[Version 0.9.3.4] EpicKnarvik97 fork
- Includes passengers of passengers when teleporting entities
- Fixes a bug which caused Stargate to use more CPU for no reason
- Teleports boats/minecarts like other vehicles unless *enableCraftBookRemoveOnEjectFix* is enabled
- Adds the *waitForPlayerAfterTeleportDelay* config option which allows changing the delay between vehicle teleportation
and the player being teleported to the vehicle
- Makes boats keep their wood type even when re-created
#### \[Version 0.9.3.3] EpicKnarvik97 fork
- Prevents Zombified Piglins from randomly spawning at Stargates
#### \[Version 0.9.3.2] EpicKnarvik97 fork
- Adds a config option to set the exit velocity of any players exiting a stargate
- Adjusts vehicle teleportation a bit to prevent passengers' exit rotation from being wrong
- Improves the checking for buggy double-clicks on non-button blocks
#### \[Version 0.9.3.1] EpicKnarvik97 fork
- Ignores the type of air when checking if a stargate is valid
#### \[Version 0.9.3.0] EpicKnarvik97 fork
- Adds support for RGB colors (use hex color codes)
- Adds support for dyed and glowing signs
- Adds support for specifying sign colors per sign type
- Adds a tab-completable config sub-command for easily changing per-sign colors
- Allows a per-sign color to be set as the inverse of the default color of the given type
#### \[Version 0.9.2.5] EpicKnarvik97 fork
- Updates Java version to JDK 17
- Updates Spigot API version to 1.18
#### \[Version 0.9.2.4] EpicKnarvik97 fork
- Adds update checking, which will display a notice in the console when updates are available
- Adds an alert about an available update when an admin joins the server
- Adds the adminUpdateAlert config option to allow the admin notices to be turned off
#### \[Version 0.9.2.3] EpicKnarvik97 fork
- Fixes a typo which caused both colors to change into the highlightSignColor
#### \[Version 0.9.2.2] EpicKnarvik97 fork
- Prevents teleportation of a player holding creatures on a leash when handleLeashedCreatures is disabled, to prevent
players accidentally losing the creatures during teleportation
- Fixes a potential exception when a gate's open-block or closed-block is set to a material which isn't a block
- Fixes a potential exception when a portal without a sign has an invalid gate type
- Prevents loading of gate files using non-blocks as part of the border
- Prevents a player smuggling another player through a restricted stargate by sitting on a creature held in a lead by
the first player
#### \[Version 0.9.2.1] EpicKnarvik97 fork
- Makes sure to only reload whatever is necessary when config values are changed using commands, instead of reloading
the entire config file every time
- Protects portals from block placement when protectEntrance is enabled
#### \[Version 0.9.2.0] EpicKnarvik97 fork
- Increases max length of names and networks to 13 characters
- Excludes color codes from the counted character length to allow a colored, 13-character name
- Makes portal names and networks case- and color-agnostic to prevent some confusion caused by typos or sloppy
configuration
- Makes the free gate color configurable, and renames freeGatesGreen to freeGatesColored
#### \[Version 0.9.1.2] EpicKnarvik97 fork
- Allows a sneaking player to see information about a silent stargate with no sign
#### \[Version 0.9.1.1] EpicKnarvik97 fork
- Makes sure to translate the `&` character to fix a bug causing portal signs to not be colored on some servers
#### \[Version 0.9.1.0] EpicKnarvik97 fork
- Rewrites config loading as a part of the changes required to implement config commands
- This update adds commands to change all config values from the chat or the console, complete with tab completion
- Adds a new permission "stargate.admin.config" which is required to edit config values from the chat
#### \[Version 0.9.0.7] EpicKnarvik97 fork
- Stops registering the sign as a lookup block for stargates without a sign
- Only removes a stargate's button if it's actually a button-compatible block
- Only displays portal info if not placing a block
#### \[Version 0.9.0.6] EpicKnarvik97 fork
- Makes containers no longer open when used as buttons
- Validates and updates stargate buttons when the plugin is loaded or reloaded
- Adds an option to make a stargate silent (no text in chat when teleporting) for better immersion on RP servers
- Makes buttons update and/or remove themselves when their location or material changes
- Adds another default gate to show that it's possible to use any number of materials for a stargate's border
- Adds an option for stargates without a sign. Right-clicking such a stargate will display gate information
- Fixes a bug causing signs to be re-drawn after they're broken
- Makes buttons and signs be replaced by water instead of air when underwater
- Makes portal info shown when right-clicking a stargate fully customizable
#### \[Version 0.9.0.5] EpicKnarvik97 fork
- Adds an option to stargate functionality to disable all teleportation of creatures
- Adds an option to stargate functionality to disable all teleportation of empty minecarts
- Adds an option to stargate functionality to disable teleportation of creatures if no player is present in the vehicle
- Prevents a player in a vehicle from teleporting without the vehicle if vehicle teleportation is disabled
- Prevents an infinite number of teleportation messages if vehicle teleportation is detected but denied
#### \[Version 0.9.0.4] EpicKnarvik97 fork
- Adds teleportation of leashed creatures. By default, any creature connected to a player by a lead will be teleported
with the player through stargates, even if the player is in a vehicle. This behavior can be disabled in the config
file.
#### \[Version 0.9.0.3] EpicKnarvik97 fork
- Adds a missing error message when a player in a vehicle cannot pay the teleportation fee
- Adds UUID migration to automatically update player names to UUID when possible
#### \[Version 0.9.0.2] EpicKnarvik97 fork
- Fixes a bug causing Stargates using NETHER_PORTAL blocks to generate nether portals in the nether.

22
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>net.knarcraft</groupId>
<artifactId>Stargate</artifactId>
<version>0.9.0.2</version>
<version>0.9.3.6</version>
<licenses>
<license>
@ -15,8 +15,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<repositories>
@ -34,7 +34,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.17.1-R0.1-SNAPSHOT</version>
<version>1.18.1-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
@ -44,25 +44,25 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.0-M1</version>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.17</artifactId>
<version>1.7.0</version>
<artifactId>MockBukkit-v1.18</artifactId>
<version>1.15.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>19.0.0</version>
<version>22.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
@ -75,8 +75,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>16</source>
<target>16</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>

View File

@ -10,6 +10,7 @@ import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.listener.BlockEventListener;
import net.knarcraft.stargate.listener.EntityEventListener;
import net.knarcraft.stargate.listener.EntitySpawnListener;
import net.knarcraft.stargate.listener.PlayerEventListener;
import net.knarcraft.stargate.listener.PluginEventListener;
import net.knarcraft.stargate.listener.PortalEventListener;
@ -21,6 +22,7 @@ import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.thread.BlockChangeThread;
import net.knarcraft.stargate.thread.ChunkUnloadThread;
import net.knarcraft.stargate.thread.StarGateThread;
import net.knarcraft.stargate.utility.UpdateChecker;
import org.bukkit.Server;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.file.FileConfiguration;
@ -43,17 +45,15 @@ import java.util.logging.Logger;
@SuppressWarnings("unused")
public class Stargate extends JavaPlugin {
//Used for changing gate open/closed material.
private static final Queue<BlockChangeRequest> blockChangeRequestQueue = new LinkedList<>();
private static final Queue<ChunkUnloadRequest> chunkUnloadQueue = new PriorityQueue<>();
private static Logger logger;
private static Stargate stargate;
private static String pluginVersion;
private static PluginManager pluginManager;
private static StargateConfig stargateConfig;
private static String updateAvailable = null;
/**
* Empty constructor necessary for Spigot
@ -74,6 +74,26 @@ public class Stargate extends JavaPlugin {
super(loader, descriptionFile, dataFolder, file);
}
/**
* Stores information about an available update
*
* <p>If a non-null version is given, joining admins will be alerted about the new update.</p>
*
* @param version <p>The version of the new update available</p>
*/
public static void setUpdateAvailable(String version) {
updateAvailable = version;
}
/**
* Gets information about an available update
*
* @return <p>The version number if an update is available. Null otherwise</p>
*/
public static String getUpdateAvailable() {
return updateAvailable;
}
/**
* Gets an instance of this plugin
*
@ -139,6 +159,16 @@ public class Stargate extends JavaPlugin {
return logger;
}
/**
* Gets the max length of portal names and networks
*
* @return <p>The max portal name/network length</p>
*/
@SuppressWarnings("SameReturnValue")
public static int getMaxNameNetworkLength() {
return 13;
}
/**
* Sends a debug message
*
@ -306,6 +336,9 @@ public class Stargate extends JavaPlugin {
PluginDescriptionFile pluginDescriptionFile = this.getDescription();
pluginManager = getServer().getPluginManager();
FileConfiguration newConfig = this.getConfig();
this.saveDefaultConfig();
newConfig.options().copyDefaults(true);
logger = Logger.getLogger("Minecraft");
Server server = getServer();
stargate = this;
@ -324,6 +357,9 @@ public class Stargate extends JavaPlugin {
runThreads();
this.registerCommands();
//Check for any available updates
UpdateChecker.checkForUpdate();
}
/**
@ -349,6 +385,7 @@ public class Stargate extends JavaPlugin {
pluginManager.registerEvents(new WorldEventListener(), this);
pluginManager.registerEvents(new PluginEventListener(this), this);
pluginManager.registerEvents(new TeleportEventListener(), this);
pluginManager.registerEvents(new EntitySpawnListener(), this);
}
/**
@ -389,4 +426,5 @@ public class Stargate extends JavaPlugin {
public static StargateConfig getStargateConfig() {
return stargateConfig;
}
}

View File

@ -1,7 +1,7 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@ -23,8 +23,9 @@ public class CommandAbout implements CommandExecutor {
commandSender.sendMessage(textColor + "Go to " + highlightColor +
"https://git.knarcraft.net/EpicKnarvik97/Stargate " + textColor + "for the official repository");
String author = Stargate.getStargateConfig().getLanguageLoader().getString("author");
if (!author.isEmpty())
if (!author.isEmpty()) {
commandSender.sendMessage(textColor + "Language created by " + highlightColor + author);
}
return true;
}

View File

@ -0,0 +1,425 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.ConfigOption;
import net.knarcraft.stargate.config.ConfigTag;
import net.knarcraft.stargate.config.OptionDataType;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.PortalSignDrawer;
import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* This command represents the config command for changing config values
*/
public class CommandConfig implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (commandSender instanceof Player player) {
if (!player.hasPermission("stargate.admin.config")) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied");
return true;
}
}
if (args.length > 0) {
ConfigOption selectedOption = ConfigOption.getByName(args[0]);
if (selectedOption == null) {
return false;
}
if (args.length > 1) {
if (selectedOption.getDataType() == OptionDataType.STRING_LIST) {
updateListConfigValue(selectedOption, commandSender, args);
} else {
updateConfigValue(selectedOption, commandSender, args[1]);
}
} else {
//Display info and the current value of the given config value
printConfigOptionValue(commandSender, selectedOption);
}
return true;
} else {
//Display all config options
displayConfigValues(commandSender);
}
return true;
}
/**
* Updates a config value
*
* @param selectedOption <p>The option which should be updated</p>
* @param commandSender <p>The command sender that changed the value</p>
* @param value <p>The new value of the config option</p>
*/
private void updateConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) {
FileConfiguration configuration = Stargate.getInstance().getConfig();
//Validate any sign colors
if (ConfigTag.COLOR.isTagged(selectedOption)) {
try {
ChatColor.of(value.toUpperCase());
} catch (IllegalArgumentException | NullPointerException ignored) {
commandSender.sendMessage(ChatColor.RED + "Invalid color given");
return;
}
}
//Store the config values, accounting for the data type
switch (selectedOption.getDataType()) {
case BOOLEAN -> updateBooleanConfigValue(selectedOption, value, configuration);
case INTEGER -> {
Integer intValue = getInteger(commandSender, selectedOption, value);
if (intValue == null) {
return;
} else {
Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, intValue);
configuration.set(selectedOption.getConfigNode(), intValue);
}
}
case DOUBLE -> {
Double doubleValue = getDouble(commandSender, selectedOption, value);
if (doubleValue == null) {
return;
} else {
Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, doubleValue);
configuration.set(selectedOption.getConfigNode(), doubleValue);
}
}
case STRING -> {
updateStringConfigValue(selectedOption, commandSender, value);
configuration.set(selectedOption.getConfigNode(), value);
}
default -> {
Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, value);
configuration.set(selectedOption.getConfigNode(), value);
}
}
saveAndReload(selectedOption, commandSender);
}
/**
* Updates a boolean config value
*
* @param selectedOption <p>The option which should be updated</p>
* @param value <p>The new value of the config option</p>
* @param configuration <p>The configuration file to save to</p>
*/
private void updateBooleanConfigValue(ConfigOption selectedOption, String value, FileConfiguration configuration) {
boolean newValue = Boolean.parseBoolean(value);
if (selectedOption == ConfigOption.ENABLE_BUNGEE && newValue != Stargate.getGateConfig().enableBungee()) {
Stargate.getStargateConfig().startStopBungeeListener(newValue);
}
Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, newValue);
configuration.set(selectedOption.getConfigNode(), newValue);
}
/**
* Updates a string config value
*
* @param selectedOption <p>The option which should be updated</p>
* @param commandSender <p>The command sender that changed the value</p>
* @param value <p>The new value of the config option</p>
*/
private void updateStringConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) {
if (selectedOption == ConfigOption.GATE_FOLDER || selectedOption == ConfigOption.PORTAL_FOLDER ||
selectedOption == ConfigOption.DEFAULT_GATE_NETWORK) {
if (value.contains("../") || value.contains("..\\")) {
commandSender.sendMessage(ChatColor.RED + "Path traversal characters cannot be used");
return;
}
}
if (ConfigTag.COLOR.isTagged(selectedOption)) {
if (!registerColor(selectedOption, value, commandSender)) {
return;
}
}
if (selectedOption == ConfigOption.LANGUAGE) {
Stargate.getStargateConfig().getLanguageLoader().setChosenLanguage(value);
}
Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, value);
}
/**
* Updates a config value
*
* @param selectedOption <p>The option which should be updated</p>
* @param commandSender <p>The command sender that changed the value</p>
* @param arguments <p>The arguments for the new config option</p>
*/
private void updateListConfigValue(ConfigOption selectedOption, CommandSender commandSender, String[] arguments) {
FileConfiguration configuration = Stargate.getInstance().getConfig();
if (selectedOption == ConfigOption.PER_SIGN_COLORS) {
if (arguments.length < 4) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Usage: /sg config perSignColors " +
"<SIGN_TYPE> <MAIN_COLOR> <HIGHLIGHTING_COLOR>");
return;
}
String colorString = parsePerSignColorInput(commandSender, arguments);
if (colorString == null) {
return;
}
//Update the per-sign colors according to input
updatePerSignColors(arguments[1], colorString, configuration);
}
saveAndReload(selectedOption, commandSender);
}
/**
* Parses the input given for changing the per-color string
*
* @param commandSender <p>The command sender that triggered the command</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>
*/
private String parsePerSignColorInput(CommandSender commandSender, String[] arguments) {
//Make sure the sign type is an actual sign
if (Material.matchMaterial(arguments[1] + "_SIGN") == null) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "The given sign type is invalid");
return null;
}
String colorString = arguments[1] + ":";
//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[] newColors = new String[2];
for (int i = 0; i < 2; i++) {
if (validatePerSignColor(arguments[i + 2])) {
newColors[i] = arguments[i + 2];
} else {
Stargate.getMessageSender().sendErrorMessage(commandSender, errorMessage[i]);
return null;
}
}
colorString += String.join(",", newColors);
return colorString;
}
/**
* Updates the per-sign colors with the given input
*
* @param signType <p>The sign type that is updated</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>
*/
private void updatePerSignColors(String signType, String colorString, FileConfiguration configuration) {
List<String> newColorStrings = new ArrayList<>();
List<?> oldColors = (List<?>) Stargate.getStargateConfig().getConfigOptionsReference().get(ConfigOption.PER_SIGN_COLORS);
for (Object object : oldColors) {
newColorStrings.add(String.valueOf(object));
}
newColorStrings.removeIf((item) -> item.startsWith(signType));
newColorStrings.add(colorString);
Stargate.getStargateConfig().getConfigOptionsReference().put(ConfigOption.PER_SIGN_COLORS, newColorStrings);
configuration.set(ConfigOption.PER_SIGN_COLORS.getConfigNode(), newColorStrings);
}
/**
* Tries to validate one of the colors given when changing per-sign colors
*
* @param color <p>The color chosen by the user</p>
* @return <p>True if the given color is valid</p>
*/
private boolean validatePerSignColor(String color) {
ChatColor newHighlightColor = parseColor(color);
return newHighlightColor != null || color.equalsIgnoreCase("default") ||
color.equalsIgnoreCase("inverted");
}
/**
* Saves the configuration file and reloads as necessary
*
* @param selectedOption <p>The config option that was changed</p>
* @param commandSender <p>The command sender that executed the config command</p>
*/
private void saveAndReload(ConfigOption selectedOption, CommandSender commandSender) {
//Save the config file and reload if necessary
Stargate.getInstance().saveConfig();
Stargate.getMessageSender().sendSuccessMessage(commandSender, "Config updated");
//Reload whatever is necessary
reloadIfNecessary(commandSender, selectedOption);
}
/**
* Registers the chat color if
*
* @param selectedOption <p>The option to change</p>
* @param commandSender <p>The command sender to alert if the color is invalid</p>
* @param value <p>The new option value</p>
*/
private boolean registerColor(ConfigOption selectedOption, String value, CommandSender commandSender) {
ChatColor parsedColor = parseColor(value);
if (parsedColor == null) {
commandSender.sendMessage(ChatColor.RED + "Invalid color given");
return false;
}
if (selectedOption == ConfigOption.FREE_GATES_COLOR) {
PortalSignDrawer.setFreeColor(parsedColor);
} else if (selectedOption == ConfigOption.MAIN_SIGN_COLOR) {
PortalSignDrawer.setMainColor(parsedColor);
} else if (selectedOption == ConfigOption.HIGHLIGHT_SIGN_COLOR) {
PortalSignDrawer.setHighlightColor(parsedColor);
}
return true;
}
/**
* Parses a chat color
*
* @param value <p>The value to parse</p>
* @return <p>The parsed color or null</p>
*/
private ChatColor parseColor(String value) {
try {
return ChatColor.of(value.toUpperCase());
} catch (IllegalArgumentException | NullPointerException ignored) {
return null;
}
}
/**
* Gets an integer from a string
*
* @param commandSender <p>The command sender that sent the config command</p>
* @param selectedOption <p>The option the command sender is trying to change</p>
* @param value <p>The value given</p>
* @return <p>An integer, or null if it was invalid</p>
*/
private Integer getInteger(CommandSender commandSender, ConfigOption selectedOption, String value) {
try {
int intValue = Integer.parseInt(value);
if ((selectedOption == ConfigOption.USE_COST || selectedOption == ConfigOption.CREATE_COST) && intValue < 0) {
commandSender.sendMessage(ChatColor.RED + "This config option cannot be negative.");
return null;
}
return intValue;
} catch (NumberFormatException exception) {
commandSender.sendMessage(ChatColor.RED + "Invalid number given");
return null;
}
}
/**
* Gets a double from a string
*
* @param commandSender <p>The command sender that sent the config command</p>
* @param selectedOption <p>The option the command sender is trying to change</p>
* @param value <p>The value given</p>
* @return <p>A double, or null if it was invalid</p>
*/
private Double getDouble(CommandSender commandSender, ConfigOption selectedOption, String value) {
try {
double doubleValue = Double.parseDouble(value);
if (selectedOption == ConfigOption.EXIT_VELOCITY && doubleValue < 0) {
commandSender.sendMessage(ChatColor.RED + "This config option cannot be negative.");
return null;
}
return doubleValue;
} catch (NumberFormatException exception) {
commandSender.sendMessage(ChatColor.RED + "Invalid number given");
return null;
}
}
/**
* Reloads the config if necessary
*
* @param commandSender <p>The command sender initiating the reload</p>
* @param configOption <p>The changed config option</p>
*/
private void reloadIfNecessary(CommandSender commandSender, ConfigOption configOption) {
if (ConfigTag.requiresFullReload(configOption)) {
//Reload everything
Stargate.getStargateConfig().reload(commandSender);
} else {
if (ConfigTag.requiresColorReload(configOption)) {
Stargate.getStargateConfig().getStargateGateConfig().loadPerSignColors();
}
if (ConfigTag.requiresPortalReload(configOption)) {
//Just unload and reload the portals
Stargate.getStargateConfig().unloadAllPortals();
Stargate.getStargateConfig().loadAllPortals();
}
if (ConfigTag.requiresLanguageReload(configOption)) {
//Reload the language loader
Stargate.getStargateConfig().getLanguageLoader().reload();
//Re-draw all portal signs
for (Portal portal : PortalRegistry.getAllPortals()) {
portal.drawSign();
}
}
if (ConfigTag.requiresEconomyReload(configOption)) {
//Load or unload Vault and Economy as necessary
Stargate.getStargateConfig().reloadEconomy();
}
}
}
/**
* Prints information about a config option and its current value
*
* @param sender <p>The command sender that sent the command</p>
* @param option <p>The config option to print information about</p>
*/
private void printConfigOptionValue(CommandSender sender, ConfigOption option) {
Object value = Stargate.getStargateConfig().getConfigOptions().get(option);
sender.sendMessage(getOptionDescription(option));
sender.sendMessage(ChatColor.GREEN + "Current value: " + ChatColor.GOLD + value);
}
/**
* Displays the name and a small description of every config value
*
* @param sender <p>The command sender to display the config list to</p>
*/
private void displayConfigValues(CommandSender sender) {
sender.sendMessage(ChatColor.GREEN + Stargate.getBackupString("prefix") + ChatColor.GOLD +
"Config values:");
for (ConfigOption option : ConfigOption.values()) {
sender.sendMessage(getOptionDescription(option));
}
}
/**
* Gets the description of a single config option
*
* @param option <p>The option to describe</p>
* @return <p>A string describing the config option</p>
*/
private String getOptionDescription(ConfigOption option) {
Object defaultValue = option.getDefaultValue();
String stringValue = String.valueOf(defaultValue);
if (option.getDataType() == OptionDataType.STRING_LIST) {
stringValue = "[" + StringUtils.join((String[]) defaultValue, ",") + "]";
}
return ChatColor.GOLD + option.getName() + ChatColor.WHITE + " - " + ChatColor.GREEN + option.getDescription() +
ChatColor.DARK_GRAY + " (Default: " + ChatColor.GRAY + stringValue + ChatColor.DARK_GRAY + ")";
}
}

View File

@ -1,7 +1,8 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@ -23,6 +24,9 @@ public class CommandStarGate implements CommandExecutor {
return new CommandAbout().onCommand(commandSender, command, s, args);
} else if (args[0].equalsIgnoreCase("reload")) {
return new CommandReload().onCommand(commandSender, command, s, args);
} else if (args[0].equalsIgnoreCase("config")) {
String[] subArgs = (String[]) ArrayUtils.remove(args, 0);
return new CommandConfig().onCommand(commandSender, command, s, subArgs);
}
return false;
} else {
@ -31,4 +35,5 @@ public class CommandStarGate implements CommandExecutor {
return true;
}
}
}

View File

@ -0,0 +1,250 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.config.ConfigOption;
import net.knarcraft.stargate.config.OptionDataType;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* This is the completer for stargates config sub-command (/sg config)
*/
public class ConfigTabCompleter implements TabCompleter {
private List<String> signTypes;
private List<String> booleans;
private List<String> integers;
private List<String> chatColors;
private List<String> languages;
private List<String> extendedColors;
private List<String> doubles;
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (signTypes == null || booleans == null || integers == null || chatColors == null || languages == null) {
initializeAutoCompleteLists();
}
if (args.length > 1) {
ConfigOption selectedOption = ConfigOption.getByName(args[0]);
if (selectedOption == null) {
return new ArrayList<>();
} else if (selectedOption.getDataType() == OptionDataType.STRING_LIST) {
return getPossibleStringListOptionValues(selectedOption, args);
} else {
return getPossibleOptionValues(selectedOption, args[1]);
}
} else {
List<String> configOptionNames = new ArrayList<>();
for (ConfigOption option : ConfigOption.values()) {
configOptionNames.add(option.getName());
}
return filterMatching(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
*
* @param selectedOption <p>The selected option</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>
*/
private List<String> getPossibleOptionValues(ConfigOption selectedOption, String typedText) {
switch (selectedOption) {
case LANGUAGE:
//Return available languages
return filterMatching(languages, typedText);
case GATE_FOLDER:
case PORTAL_FOLDER:
case DEFAULT_GATE_NETWORK:
//Just return the default value as most values should be possible
if (typedText.trim().isEmpty()) {
return putStringInList((String) selectedOption.getDefaultValue());
} else {
return new ArrayList<>();
}
case MAIN_SIGN_COLOR:
case HIGHLIGHT_SIGN_COLOR:
case FREE_GATES_COLOR:
//Return all colors
return filterMatching(chatColors, typedText);
}
//If the config value is a boolean, show the two boolean values
if (selectedOption.getDataType() == OptionDataType.BOOLEAN) {
return filterMatching(booleans, typedText);
}
//If the config value is an integer, display some valid numbers
if (selectedOption.getDataType() == OptionDataType.INTEGER) {
if (typedText.trim().isEmpty()) {
return integers;
} else {
return new ArrayList<>();
}
}
//If the config value is a double, display some valid numbers
if (selectedOption.getDataType() == OptionDataType.DOUBLE) {
if (typedText.trim().isEmpty()) {
return doubles;
} else {
return new ArrayList<>();
}
}
return null;
}
/**
* Get possible values for the selected string list option
*
* @param selectedOption <p>The selected option</p>
* @param args <p>The arguments given by the user</p>
* @return <p>Some or all of the valid values for the option</p>
*/
private List<String> getPossibleStringListOptionValues(ConfigOption selectedOption, String[] args) {
if (selectedOption == ConfigOption.PER_SIGN_COLORS) {
return getPerSignColorCompletion(args);
} else {
return null;
}
}
/**
* Gets the tab completion values for completing the per-sign color text
*
* @param args <p>The arguments given by the user</p>
* @return <p>The options to give the user</p>
*/
private List<String> getPerSignColorCompletion(String[] args) {
if (args.length < 3) {
return filterMatching(signTypes, args[1]);
} else if (args.length < 4) {
return filterMatching(extendedColors, args[2]);
} else if (args.length < 5) {
return filterMatching(extendedColors, args[3]);
}
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
*/
private void initializeAutoCompleteLists() {
booleans = new ArrayList<>();
booleans.add("true");
booleans.add("false");
integers = new ArrayList<>();
integers.add("0");
integers.add("5");
signTypes = new ArrayList<>();
for (Material material : Material.values()) {
if (Tag.STANDING_SIGNS.isTagged(material)) {
signTypes.add(material.toString().replace("_SIGN", ""));
}
}
getColors();
initializeLanguages();
extendedColors = new ArrayList<>(chatColors);
extendedColors.add("default");
extendedColors.add("inverted");
doubles = new ArrayList<>();
doubles.add("5");
doubles.add("1");
doubles.add("0.5");
doubles.add("0.1");
}
/**
* Initializes the list of chat colors
*/
private void getColors() {
chatColors = new ArrayList<>();
for (ChatColor color : getChatColors()) {
chatColors.add(color.getName());
}
}
/**
* Gets available chat colors
*
* @return <p>The available chat colors</p>
*/
private List<ChatColor> getChatColors() {
List<ChatColor> chatColors = new ArrayList<>();
char[] colors = new char[]{'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
for (char color : colors) {
chatColors.add(ChatColor.getByChar(color));
}
chatColors.add(ChatColor.of("#ed76d9"));
chatColors.add(ChatColor.of("#ffecb7"));
return chatColors;
}
/**
* Initializes the list of all available languages
*/
private void initializeLanguages() {
languages = new ArrayList<>();
languages.add("de");
languages.add("en");
languages.add("es");
languages.add("fr");
languages.add("hu");
languages.add("it");
languages.add("nb-no");
languages.add("nl");
languages.add("nn-no");
languages.add("pt-br");
languages.add("ru");
languages.add("zh_cn");
//TODO: Generate this list dynamically by listing the language files in the jar and adding the user's custom
// language files
}
}

View File

@ -1,8 +1,10 @@
package net.knarcraft.stargate.command;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -17,15 +19,39 @@ public class StarGateTabCompleter implements TabCompleter {
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s, @NotNull String[] args) {
List<String> commands = new ArrayList<>();
commands.add("about");
commands.add("reload");
if (args.length == 1) {
return commands;
List<String> commands = getAvailableCommands(commandSender);
List<String> matchingCommands = new ArrayList<>();
for (String availableCommand : commands) {
if (availableCommand.startsWith(args[0])) {
matchingCommands.add(availableCommand);
}
}
return matchingCommands;
} else if (args.length > 1 && args[0].equalsIgnoreCase("config")) {
String[] subArgs = (String[]) ArrayUtils.remove(args, 0);
return new ConfigTabCompleter().onTabComplete(commandSender, command, s, subArgs);
} else {
return new ArrayList<>();
}
}
/**
* Gets the available commands
*
* @param commandSender <p>The command sender to get available commands for</p>
* @return <p>The commands available to the command sender</p>
*/
private List<String> getAvailableCommands(CommandSender commandSender) {
List<String> commands = new ArrayList<>();
commands.add("about");
if (!(commandSender instanceof Player player) || player.hasPermission("stargate.admin.reload")) {
commands.add("reload");
}
if (!(commandSender instanceof Player player) || player.hasPermission("stargate.admin.config")) {
commands.add("config");
}
return commands;
}
}

View File

@ -0,0 +1,279 @@
package net.knarcraft.stargate.config;
/**
* A ConfigOption represents one of the available config options
*/
public enum ConfigOption {
/**
* The language used for player-interface text
*/
LANGUAGE("language", "The language used for all signs and all messages to players", "en"),
/**
* The folder for portal files
*/
PORTAL_FOLDER("folders.portalFolder", "The folder containing the portal databases", "plugins/Stargate/portals/"),
/**
* The folder for gate files
*/
GATE_FOLDER("folders.gateFolder", "The folder containing all gate files", "plugins/Stargate/gates/"),
/**
* The max number of portals on a single network
*/
MAX_GATES_EACH_NETWORK("gates.maxGatesEachNetwork", "The max number of stargates in a single network", 0),
/**
* The network used if not specified
*/
DEFAULT_GATE_NETWORK("gates.defaultGateNetwork", "The network used when no network is specified", "central"),
/**
* Whether to remember the lastly used destination
*/
REMEMBER_DESTINATION("gates.cosmetic.rememberDestination", "Whether to remember the last destination used", false),
/**
* Whether to sort the network destinations
*/
SORT_NETWORK_DESTINATIONS("gates.cosmetic.sortNetworkDestinations", "Whether to sort destinations by name", false),
/**
* The main color to use for all signs
*/
MAIN_SIGN_COLOR("gates.cosmetic.mainSignColor", "The main text color of all stargate signs", "BLACK"),
/**
* The color to use for highlighting sign text
*/
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[]{
"'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'"}),
/**
* Whether to destroy portals when any blocks are broken by explosions
*/
DESTROYED_BY_EXPLOSION("gates.integrity.destroyedByExplosion", "Whether stargates should be destroyed by explosions", false),
/**
* Whether to verify each portal's gate layout after each load
*/
VERIFY_PORTALS("gates.integrity.verifyPortals", "Whether to verify that portals match their gate layout on load", false),
/**
* Whether to protect the entrance of portals
*/
PROTECT_ENTRANCE("gates.integrity.protectEntrance", "Whether to protect stargates' entrances", false),
/**
* Whether to enable BungeeCord support
*/
ENABLE_BUNGEE("gates.functionality.enableBungee", "Whether to enable BungeeCord support", false),
/**
* Whether to enable vehicle teleportation
*/
HANDLE_VEHICLES("gates.functionality.handleVehicles", "Whether to enable vehicle teleportation", true),
/**
* Whether to enable teleportation of empty vehicles
*/
HANDLE_EMPTY_VEHICLES("gates.functionality.handleEmptyVehicles", "Whether to enable teleportation of empty vehicles", true),
/**
* Whether to enable teleportation of creatures using vehicles
*/
HANDLE_CREATURE_TRANSPORTATION("gates.functionality.handleCreatureTransportation",
"Whether to enable teleportation of vehicles containing non-player creatures", true),
/**
* Whether to allow creatures to teleport alone, bypassing any access restrictions
*/
HANDLE_NON_PLAYER_VEHICLES("gates.functionality.handleNonPlayerVehicles",
"Whether to enable teleportation of non-empty vehicles without a player", true),
/**
* Whether to enable teleportations of creatures on a leash
*/
HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures",
"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
*/
USE_ECONOMY("economy.useEconomy", "Whether to use economy to incur fees when stargates are used, created or destroyed", false),
/**
* The cost of creating a new stargate
*/
CREATE_COST("economy.createCost", "The cost of creating a new stargate", 0),
/**
* The cost of destroying a stargate
*/
DESTROY_COST("economy.destroyCost", "The cost of destroying a stargate. Negative to refund", 0),
/**
* The cost of using (teleporting through) a stargate
*/
USE_COST("economy.useCost", "The cost of using (teleporting through) a stargate", 0),
/**
* Whether any payments should go to the stargate's owner
*/
TO_OWNER("economy.toOwner", "Whether any teleportation fees should go to the owner of the used stargate", false),
/**
* Whether to charge for using a stargate, even if its destination is free
*/
CHARGE_FREE_DESTINATION("economy.chargeFreeDestination",
"Whether to require payment if the destination is free, but the entrance stargate is not", true),
/**
* Whether to mark free gates with a different color
*/
FREE_GATES_COLORED("economy.freeGatesColored", "Whether to use coloring to mark all free stargates", false),
/**
* The color to use for marking free stargates
*/
FREE_GATES_COLOR("economy.freeGatesColor", "The color to use for marking free stargates", "DARK_GREEN"),
/**
* Whether to enable debug output
*/
DEBUG("debugging.debug", "Whether to enable debugging output", false),
/**
* Whether to enable debug output for debugging permissions
*/
PERMISSION_DEBUG("debugging.permissionDebug", "Whether to enable permission debugging output", false),
/**
* Whether to alert admins about new updates
*/
ADMIN_UPDATE_ALERT("adminUpdateAlert", "Whether to alert admins about new plugin updates", true),
/**
* 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);
private final String configNode;
private final String description;
private final Object defaultValue;
private final OptionDataType dataType;
/**
* Instantiates a new config option
*
* @param configNode <p>The full path of this config option's config node</p>
* @param description <p>The description of what this config option does</p>
* @param defaultValue <p>The default value of this config option</p>
*/
ConfigOption(String configNode, String description, Object defaultValue) {
this.configNode = configNode;
this.description = description;
this.defaultValue = defaultValue;
if (defaultValue instanceof String[]) {
this.dataType = OptionDataType.STRING_LIST;
} else if (defaultValue instanceof String) {
this.dataType = OptionDataType.STRING;
} else if (defaultValue instanceof Boolean) {
this.dataType = OptionDataType.BOOLEAN;
} else if (defaultValue instanceof Integer) {
this.dataType = OptionDataType.INTEGER;
} else if (defaultValue instanceof Double) {
this.dataType = OptionDataType.DOUBLE;
} else {
throw new IllegalArgumentException("Unknown config data type encountered: " + defaultValue);
}
}
/**
* Gets a config option given its name
*
* @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>
*/
public static ConfigOption getByName(String name) {
for (ConfigOption option : ConfigOption.values()) {
if (option.getName().equalsIgnoreCase(name)) {
return option;
}
}
return null;
}
/**
* Gets the name of this config option
*
* @return <p>The name of this config option</p>
*/
public String getName() {
if (!this.configNode.contains(".")) {
return this.configNode;
}
String[] pathParts = this.configNode.split("\\.");
return pathParts[pathParts.length - 1];
}
/**
* Gets the data type used for storing this config option
*
* @return <p>The data type used</p>
*/
public OptionDataType getDataType() {
return this.dataType;
}
/**
* Gets the config node of this config option
*
* @return <p>This config option's config node</p>
*/
public String getConfigNode() {
return this.configNode;
}
/**
* Gets the description of what this config option does
*
* @return <p>The description of this config option</p>
*/
public String getDescription() {
return this.description;
}
/**
* Gets this config option's default value
*
* @return <p>This config option's default value</p>
*/
public Object getDefaultValue() {
return this.defaultValue;
}
}

View File

@ -0,0 +1,85 @@
package net.knarcraft.stargate.config;
import java.util.Arrays;
/**
* A config tag groups config values by a property
*/
public enum ConfigTag {
COLOR(new ConfigOption[]{ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR,
ConfigOption.HIGHLIGHT_SIGN_COLOR, ConfigOption.PER_SIGN_COLORS}),
FOLDER(new ConfigOption[]{ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER});
private final ConfigOption[] taggedOptions;
/**
* Instantiates a new config tag
*
* @param taggedOptions <p>The config options included in this tag</p>
*/
ConfigTag(ConfigOption[] taggedOptions) {
this.taggedOptions = taggedOptions;
}
/**
* Checks whether a config tag includes the given config option
*
* @param option <p>The config option to check</p>
* @return <p>True of the config option is tagged</p>
*/
public boolean isTagged(ConfigOption option) {
return Arrays.stream(taggedOptions).anyMatch((item) -> item == option);
}
/**
* Checks whether a given config option requires a "reload of colors" to take effect
*
* @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>
*/
public static boolean requiresColorReload(ConfigOption configOption) {
return (COLOR.isTagged(configOption) && configOption != ConfigOption.FREE_GATES_COLOR);
}
/**
* Checks whether a given config option requires a full reload to take effect
*
* @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>
*/
public static boolean requiresFullReload(ConfigOption option) {
return FOLDER.isTagged(option);
}
/**
* Checks whether a given config option requires a portal reload to take effect
*
* @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>
*/
public static boolean requiresPortalReload(ConfigOption option) {
return COLOR.isTagged(option) || FOLDER.isTagged(option) || option == ConfigOption.VERIFY_PORTALS;
}
/**
* Checks whether a given config option requires the language loader to be reloaded
*
* @param option <p>The config option to check</p>
* @return <p>True if the language loader requires a reload</p>
*/
public static boolean requiresLanguageReload(ConfigOption option) {
return option == ConfigOption.LANGUAGE;
}
/**
* Checks whether a given config option requires economy to be reloaded
*
* @param option <p>The config option to check</p>
* @return <p>True if economy requires a reload</p>
*/
public static boolean requiresEconomyReload(ConfigOption option) {
return option == ConfigOption.USE_ECONOMY;
}
}

View File

@ -1,42 +1,42 @@
package net.knarcraft.stargate.config;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Gate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalSignDrawer;
import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.md_5.bungee.api.ChatColor;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;
import java.util.UUID;
import java.util.Map;
/**
* The economy config keeps track of economy config values and performs economy actions such as payment for using a gate
*/
public final class EconomyConfig {
private boolean economyEnabled = false;
private Economy economy = null;
private Plugin vault = null;
private int useCost = 0;
private int createCost = 0;
private int destroyCost = 0;
private boolean toOwner = false;
private boolean chargeFreeDestination = true;
private boolean freeGatesGreen = false;
private final Map<ConfigOption, Object> configOptions;
/**
* Instantiates a new economy config
*
* @param newConfig <p>The file configuration to read values from</p>
* @param configOptions <p>The loaded config options to read</p>
*/
public EconomyConfig(FileConfiguration newConfig) {
loadEconomyConfig(newConfig);
public EconomyConfig(Map<ConfigOption, Object> configOptions) {
this.configOptions = configOptions;
try {
String freeColor = (String) configOptions.get(ConfigOption.FREE_GATES_COLOR);
PortalSignDrawer.setFreeColor(ChatColor.of(freeColor.toUpperCase()));
} catch (IllegalArgumentException | NullPointerException ignored) {
PortalSignDrawer.setFreeColor(ChatColor.DARK_GREEN);
}
}
/**
@ -45,7 +45,7 @@ public final class EconomyConfig {
* @return <p>The gate use cost</p>
*/
public int getDefaultUseCost() {
return useCost;
return (Integer) configOptions.get(ConfigOption.USE_COST);
}
/**
@ -54,7 +54,7 @@ public final class EconomyConfig {
* @return <p>Whether economy is enabled</p>
*/
public boolean isEconomyEnabled() {
return economyEnabled;
return (boolean) configOptions.get(ConfigOption.USE_ECONOMY);
}
/**
@ -84,12 +84,12 @@ public final class EconomyConfig {
}
/**
* Gets whether free portals should be marked with green coloring
* Gets whether free portals should be marked with a different coloring
*
* @return <p>Whether free portals should be green</p>
* @return <p>Whether free portals should be colored</p>
*/
public boolean drawFreePortalsGreen() {
return freeGatesGreen;
public boolean drawFreePortalsColored() {
return (boolean) configOptions.get(ConfigOption.FREE_GATES_COLORED);
}
/**
@ -101,8 +101,8 @@ public final class EconomyConfig {
*
* @return <p>Whether to charge for free destinations</p>
*/
public boolean chargeFreeDestination() {
return chargeFreeDestination;
public boolean freeIfFreeDestination() {
return !((boolean) configOptions.get(ConfigOption.CHARGE_FREE_DESTINATION));
}
/**
@ -111,21 +111,7 @@ public final class EconomyConfig {
* @return <p>Whether to send payments to the portal owner</p>
*/
public boolean sendPaymentToOwner() {
return toOwner;
}
/**
* Sets the cost of using a gate without a specified cost
*
* <p>The use cost cannot be negative.</p>
*
* @param useCost <p>The gate use cost</p>
*/
public void setDefaultUseCost(int useCost) {
if (useCost < 0) {
throw new IllegalArgumentException("Using a gate cannot cost a negative amount");
}
this.useCost = useCost;
return (boolean) configOptions.get(ConfigOption.TO_OWNER);
}
/**
@ -134,18 +120,7 @@ public final class EconomyConfig {
* @return <p>The gate creation cost</p>
*/
public int getDefaultCreateCost() {
return createCost;
}
/**
* Sets the cost of creating a gate without a specified cost
*
* <p>The gate create cost cannot be negative</p>
*
* @param createCost <p>The gate creation cost</p>
*/
public void setDefaultCreateCost(int createCost) {
this.createCost = createCost;
return (Integer) configOptions.get(ConfigOption.CREATE_COST);
}
/**
@ -154,31 +129,7 @@ public final class EconomyConfig {
* @return <p>The gate destruction cost</p>
*/
public int getDefaultDestroyCost() {
return destroyCost;
}
/**
* Sets the cost of destroying a gate without a specified cost
*
* @param destroyCost <p>The gate destruction cost</p>
*/
public void setDefaultDestroyCost(int destroyCost) {
this.destroyCost = destroyCost;
}
/**
* Charges the player for an action, if required
*
* @param player <p>The player to take money from</p>
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p>
*/
public boolean chargePlayerIfNecessary(Player player, int cost) {
if (skipPayment(cost)) {
return true;
}
//Charge player
return chargePlayer(player, cost);
return (Integer) configOptions.get(ConfigOption.DESTROY_COST);
}
/**
@ -192,22 +143,6 @@ public final class EconomyConfig {
return economy.getBalance(player) > cost;
}
/**
* Charges the player for an action, if required
*
* @param player <p>The player to take money from</p>
* @param target <p>The target to pay</p>
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p>
*/
public boolean chargePlayerIfNecessary(Player player, UUID target, int cost) {
if (skipPayment(cost)) {
return true;
}
//Charge player
return chargePlayer(player, target, cost);
}
/**
* Gets a formatted string for an amount, adding the name of the currency
*
@ -215,7 +150,7 @@ public final class EconomyConfig {
* @return <p>A formatted text string describing the amount</p>
*/
public String format(int amount) {
if (economyEnabled) {
if (isEconomyEnabled()) {
return economy.format(amount);
} else {
return "";
@ -229,7 +164,7 @@ public final class EconomyConfig {
* @return <p>True if economy was enabled</p>
*/
public boolean setupEconomy(PluginManager pluginManager) {
if (!economyEnabled) {
if (!isEconomyEnabled()) {
return false;
}
//Check if vault is loaded
@ -247,7 +182,7 @@ public final class EconomyConfig {
} else {
Stargate.logInfo(Stargate.getString("vaultLoadError"));
}
economyEnabled = false;
configOptions.put(ConfigOption.USE_ECONOMY, false);
return false;
}
@ -257,46 +192,7 @@ public final class EconomyConfig {
* @return <p>True if the user has turned on economy and economy is available</p>
*/
public boolean useEconomy() {
return economyEnabled && economy != null;
}
/**
* Checks whether a payment transaction should be skipped
*
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the transaction should be skipped</p>
*/
private boolean skipPayment(int cost) {
return cost == 0 || !useEconomy();
}
/**
* Determines the cost of using a gate
*
* @param player <p>The player trying to use the gate</p>
* @param source <p>The source/entry portal</p>
* @param destination <p>The destination portal</p>
* @return <p>The cost of using the portal</p>
*/
public int getUseCost(Player player, Portal source, Portal destination) {
//No payment required
if (!useEconomy() || source.getOptions().isFree()) {
return 0;
}
//Not charging for free destinations
if (destination != null && !chargeFreeDestination && destination.getOptions().isFree()) {
return 0;
}
//Cost is 0 if the player owns this gate and funds go to the owner
if (source.getGate().getToOwner() && source.isOwner(player)) {
return 0;
}
//Player gets free gate use
if (PermissionHelper.hasPermission(player, "stargate.free.use")) {
return 0;
}
return source.getGate().getUseCost();
return isEconomyEnabled() && economy != null;
}
/**
@ -329,21 +225,6 @@ public final class EconomyConfig {
}
}
/**
* Loads all config values related to economy
*
* @param newConfig <p>The configuration containing the values to read</p>
*/
private void loadEconomyConfig(FileConfiguration newConfig) {
economyEnabled = newConfig.getBoolean("economy.useEconomy");
setDefaultCreateCost(newConfig.getInt("economy.createCost"));
setDefaultDestroyCost(newConfig.getInt("economy.destroyCost"));
setDefaultUseCost(newConfig.getInt("economy.useCost"));
toOwner = newConfig.getBoolean("economy.toOwner");
chargeFreeDestination = newConfig.getBoolean("economy.chargeFreeDestination");
freeGatesGreen = newConfig.getBoolean("economy.freeGatesGreen");
}
/**
* Determines if a player can do a gate action for free
*
@ -355,41 +236,4 @@ public final class EconomyConfig {
return !useEconomy() || PermissionHelper.hasPermission(player, "stargate.free." + permissionNode);
}
/**
* Charges a player
*
* @param player <p>The player 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>
*/
private boolean chargePlayer(Player player, double amount) {
if (economyEnabled && economy != null) {
if (!economy.has(player, amount)) {
return false;
}
economy.withdrawPlayer(player, amount);
}
return true;
}
/**
* Charges a player, giving the charge to a target
*
* @param player <p>The player to charge</p>
* @param target <p>The UUID of the player to pay</p>
* @param amount <p>The amount to charge</p>
* @return <p>True if the payment succeeded, or if no payment was necessary</p>
*/
private boolean chargePlayer(Player player, UUID target, double amount) {
if (economyEnabled && player.getUniqueId().compareTo(target) != 0 && economy != null) {
if (!economy.has(player, amount)) {
return false;
}
//Take money from the user and give to the owner
economy.withdrawPlayer(player, amount);
economy.depositPlayer(Bukkit.getOfflinePlayer(target), amount);
}
return true;
}
}

View File

@ -1,6 +1,6 @@
package net.knarcraft.stargate.config;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
/**

View File

@ -0,0 +1,33 @@
package net.knarcraft.stargate.config;
/**
* An enum defining the different data types an option can have
*/
public enum OptionDataType {
/**
* The data type if the option is a String
*/
STRING,
/**
* The data type if the option is a Boolean
*/
BOOLEAN,
/**
* The data type if the option is a string list
*/
STRING_LIST,
/**
* The data type if the option is an Integer
*/
INTEGER,
/**
* The data type if the option is a double
*/
DOUBLE
}

View File

@ -3,10 +3,10 @@ package net.knarcraft.stargate.config;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.listener.BungeeCordListener;
import net.knarcraft.stargate.portal.GateHandler;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.thread.BlockChangeThread;
import net.knarcraft.stargate.utility.FileHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
@ -18,6 +18,7 @@ import org.bukkit.plugin.messaging.Messenger;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
@ -45,8 +46,7 @@ public final class StargateConfig {
private String portalFolder;
private String languageName = "en";
private boolean debuggingEnabled = false;
private boolean permissionDebuggingEnabled = false;
private final Map<ConfigOption, Object> configOptions;
/**
* Instantiates a new stargate config
@ -55,6 +55,7 @@ public final class StargateConfig {
*/
public StargateConfig(Logger logger) {
this.logger = logger;
configOptions = new HashMap<>();
dataFolderPath = Stargate.getInstance().getDataFolder().getPath().replaceAll("\\\\", "/");
portalFolder = dataFolderPath + "/portals/";
@ -62,6 +63,18 @@ public final class StargateConfig {
languageLoader = new LanguageLoader(dataFolderPath + "/lang/");
}
/**
* Gets a direct reference to the config option map
*
* <p>This reference can be used to alter the value of config options. Values should only be altered after it's
* been verified that the value is valid.</p>
*
* @return <p>A reference to the config options map</p>
*/
public Map<ConfigOption, Object> getConfigOptionsReference() {
return configOptions;
}
/**
* Finish the config setup by loading languages, gates and portals, and loading economy if vault is loaded
*/
@ -78,7 +91,7 @@ public final class StargateConfig {
languageLoader.reload();
messageSender = new MessageSender(languageLoader);
if (debuggingEnabled) {
if (isDebuggingEnabled()) {
languageLoader.debug();
}
@ -90,6 +103,15 @@ public final class StargateConfig {
setupVaultEconomy();
}
/**
* Gets a copy of all loaded config options with its values
*
* @return <p>The loaded config options</p>
*/
public Map<ConfigOption, Object> getConfigOptions() {
return new HashMap<>(configOptions);
}
/**
* Gets the queue of open portals
*
@ -118,7 +140,7 @@ public final class StargateConfig {
* @return <p>Whether debugging is enabled</p>
*/
public boolean isDebuggingEnabled() {
return debuggingEnabled;
return (boolean) configOptions.get(ConfigOption.DEBUG);
}
/**
@ -127,7 +149,7 @@ public final class StargateConfig {
* @return <p>Whether permission debugging is enabled</p>
*/
public boolean isPermissionDebuggingEnabled() {
return permissionDebuggingEnabled;
return (boolean) configOptions.get(ConfigOption.PERMISSION_DEBUG);
}
/**
@ -174,6 +196,17 @@ public final class StargateConfig {
* Un-loads all loaded data
*/
private void unload() {
//De-activate, close and unload all loaded portals
unloadAllPortals();
//Clear all loaded gates
GateHandler.clearGates();
}
/**
* Un-loads all loaded portals
*/
public void unloadAllPortals() {
//De-activate all currently active portals
for (Portal activePortal : activePortalsQueue) {
activePortal.getPortalActivator().deactivate();
@ -189,9 +222,6 @@ public final class StargateConfig {
//Clear all loaded portals
PortalRegistry.clearPortals();
//Clear all loaded gates
GateHandler.clearGates();
}
/**
@ -244,7 +274,7 @@ public final class StargateConfig {
//Update the language loader in case the loaded language changed
languageLoader.setChosenLanguage(languageName);
languageLoader.reload();
if (debuggingEnabled) {
if (isDebuggingEnabled()) {
languageLoader.debug();
}
@ -255,7 +285,7 @@ public final class StargateConfig {
/**
* Starts the listener for listening to BungeeCord messages
*/
private void startStopBungeeListener(boolean start) {
public void startStopBungeeListener(boolean start) {
Messenger messenger = Bukkit.getMessenger();
String bungeeChannel = "BungeeCord";
@ -271,7 +301,7 @@ public final class StargateConfig {
/**
* Reloads economy by enabling or disabling it as necessary
*/
private void reloadEconomy() {
public void reloadEconomy() {
EconomyConfig economyConfig = getEconomyConfig();
if (economyConfig.isEconomyEnabled() && economyConfig.getEconomy() == null) {
setupVaultEconomy();
@ -289,6 +319,15 @@ public final class StargateConfig {
}
}
/**
* Gets whether admins should be alerted about new plugin updates
*
* @return <p>Whether admins should be alerted about new updates</p>
*/
public boolean alertAdminsAboutUpdates() {
return (boolean) configOptions.get(ConfigOption.ADMIN_UPDATE_ALERT);
}
/**
* Loads all config values
*/
@ -297,9 +336,7 @@ public final class StargateConfig {
FileConfiguration newConfig = Stargate.getInstance().getConfig();
boolean isMigrating = false;
if (newConfig.getString("lang") != null ||
newConfig.getString("gates.integrity.ignoreEntrance") != null ||
newConfig.getString("ignoreEntrance") != null) {
if (newConfig.getString("lang") != null || newConfig.getString("economy.freeGatesGreen") != null) {
migrateConfig(newConfig);
isMigrating = true;
}
@ -307,16 +344,32 @@ public final class StargateConfig {
//Copy missing default values if any values are missing
newConfig.options().copyDefaults(true);
//Load all options
for (ConfigOption option : ConfigOption.values()) {
Object optionValue;
String configNode = option.getConfigNode();
//Load the option using its correct data type
switch (option.getDataType()) {
case STRING_LIST -> optionValue = newConfig.getStringList(configNode);
case STRING -> {
String value = newConfig.getString(configNode);
optionValue = value != null ? value.trim() : "";
}
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");
}
configOptions.put(option, optionValue);
}
//Get the language name from the config
languageName = newConfig.getString("language");
languageName = (String) configOptions.get(ConfigOption.LANGUAGE);
//Get important folders from the config
portalFolder = newConfig.getString("folders.portalFolder");
gateFolder = newConfig.getString("folders.gateFolder");
//Get enabled debug settings from the config
debuggingEnabled = newConfig.getBoolean("debugging.debug");
permissionDebuggingEnabled = newConfig.getBoolean("debugging.permissionDebug");
portalFolder = (String) configOptions.get(ConfigOption.PORTAL_FOLDER);
gateFolder = (String) configOptions.get(ConfigOption.GATE_FOLDER);
//If users have an outdated config, assume they also need to update their default gates
if (isMigrating) {
@ -324,10 +377,10 @@ public final class StargateConfig {
}
//Load all gate config values
stargateGateConfig = new StargateGateConfig(newConfig);
stargateGateConfig = new StargateGateConfig(configOptions);
//Load all economy config values
economyConfig = new EconomyConfig(newConfig);
economyConfig = new EconomyConfig(configOptions);
Stargate.getInstance().saveConfig();
}
@ -467,4 +520,5 @@ public final class StargateConfig {
public LanguageLoader getLanguageLoader() {
return languageLoader;
}
}

View File

@ -2,33 +2,33 @@ package net.knarcraft.stargate.config;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.PortalSignDrawer;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import net.knarcraft.stargate.utility.ColorHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Color;
import org.bukkit.Material;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The Stargate gate config keeps track of all global config values related to gates
*/
public final class StargateGateConfig {
private int maxGatesEachNetwork = 0;
private boolean rememberDestination = false;
private boolean handleVehicles = true;
private boolean sortNetworkDestinations = false;
private boolean protectEntrance = false;
private boolean enableBungee = true;
private boolean verifyPortals = true;
private boolean destroyExplosion = false;
private String defaultGateNetwork = "central";
private static final int activeTime = 10;
private static final int openTime = 10;
private final Map<ConfigOption, Object> configOptions;
/**
* Instantiates a new stargate config
*
* @param newConfig <p>The file configuration to read values from</p>
* @param configOptions <p>The loaded config options to use</p>
*/
public StargateGateConfig(FileConfiguration newConfig) {
loadGateConfig(newConfig);
public StargateGateConfig(Map<ConfigOption, Object> configOptions) {
this.configOptions = configOptions;
loadGateConfig();
}
/**
@ -55,7 +55,7 @@ public final class StargateGateConfig {
* @return <p>Maximum number of gates for each network</p>
*/
public int maxGatesEachNetwork() {
return maxGatesEachNetwork;
return (int) configOptions.get(ConfigOption.MAX_GATES_EACH_NETWORK);
}
/**
@ -64,7 +64,7 @@ public final class StargateGateConfig {
* @return <p>Whether a portal's lastly used destination should be remembered</p>
*/
public boolean rememberDestination() {
return rememberDestination;
return (boolean) configOptions.get(ConfigOption.REMEMBER_DESTINATION);
}
/**
@ -73,7 +73,80 @@ public final class StargateGateConfig {
* @return <p>Whether vehicle teleportation should be handled</p>
*/
public boolean handleVehicles() {
return handleVehicles;
return (boolean) configOptions.get(ConfigOption.HANDLE_VEHICLES);
}
/**
* Gets whether vehicles with no passengers should be handled
*
* <p>The handle vehicles option overrides this option if disabled. This option allows empty passenger
* minecarts/boats, but also chest/tnt/hopper/furnace minecarts to teleport through stargates.</p>
*
* @return <p>Whether vehicles without passengers should be handled</p>
*/
public boolean handleEmptyVehicles() {
return (boolean) configOptions.get(ConfigOption.HANDLE_EMPTY_VEHICLES);
}
/**
* Gets whether vehicles containing creatures should be handled
*
* <p>The handle vehicles option overrides this option if disabled. This option allows creatures (pigs, pandas,
* zombies, etc.) to teleport through stargates if in a vehicle.</p>
*
* @return <p>Whether vehicles with creatures should be handled</p>
*/
public boolean handleCreatureTransportation() {
return (boolean) configOptions.get(ConfigOption.HANDLE_CREATURE_TRANSPORTATION);
}
/**
* Gets whether vehicles containing a creature, but not a player should be handled
*
* <p>The handle vehicles option, and the handle creature transportation option, override this option if disabled.
* This option allows creatures (pigs, pandas, zombies, etc.) to teleport through stargates if in a vehicle, even
* if no player is in the vehicle.
* As it is not possible to check if a creature is allowed through a stargate, they will be able to go through
* regardless of whether the initiating player is allowed to enter the stargate. Enabling this is necessary to
* teleport creatures using minecarts, but only handleCreatureTransportation is required to teleport creatures
* using a boat manned by a player.</p>
*
* @return <p>Whether non-empty vehicles without a player should be handled</p>
*/
public boolean handleNonPlayerVehicles() {
return (boolean) configOptions.get(ConfigOption.HANDLE_NON_PLAYER_VEHICLES);
}
/**
* Gets whether leashed creatures should be teleported with a teleporting player
*
* @return <p>Whether leashed creatures should be handled</p>
*/
public boolean handleLeashedCreatures() {
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);
}
/**
@ -82,7 +155,7 @@ public final class StargateGateConfig {
* @return <p>Whether network destinations should be sorted</p>
*/
public boolean sortNetworkDestinations() {
return sortNetworkDestinations;
return (boolean) configOptions.get(ConfigOption.SORT_NETWORK_DESTINATIONS);
}
/**
@ -91,7 +164,7 @@ public final class StargateGateConfig {
* @return <p>Whether portal entrances should be protected</p>
*/
public boolean protectEntrance() {
return protectEntrance;
return (boolean) configOptions.get(ConfigOption.PROTECT_ENTRANCE);
}
/**
@ -100,7 +173,7 @@ public final class StargateGateConfig {
* @return <p>Whether bungee support is enabled</p>
*/
public boolean enableBungee() {
return enableBungee;
return (boolean) configOptions.get(ConfigOption.ENABLE_BUNGEE);
}
/**
@ -109,7 +182,7 @@ public final class StargateGateConfig {
* @return <p>Whether portals need to be verified</p>
*/
public boolean verifyPortals() {
return verifyPortals;
return (boolean) configOptions.get(ConfigOption.VERIFY_PORTALS);
}
/**
@ -118,7 +191,7 @@ public final class StargateGateConfig {
* @return <p>Whether portals should be destroyed by explosions</p>
*/
public boolean destroyedByExplosion() {
return destroyExplosion;
return (boolean) configOptions.get(ConfigOption.DESTROYED_BY_EXPLOSION);
}
/**
@ -127,33 +200,108 @@ public final class StargateGateConfig {
* @return <p>The default portal network</p>
*/
public String getDefaultPortalNetwork() {
return defaultGateNetwork;
return (String) configOptions.get(ConfigOption.DEFAULT_GATE_NETWORK);
}
/**
* Gets the exit velocity of players using stargates, relative to the entry velocity
*
* @return <p>The relative exit velocity</p>
*/
public double getExitVelocity() {
return (double) configOptions.get(ConfigOption.EXIT_VELOCITY);
}
/**
* Loads all config values related to gates
*
* @param newConfig <p>The configuration containing the values to read</p>
*/
private void loadGateConfig(FileConfiguration newConfig) {
String defaultNetwork = newConfig.getString("gates.defaultGateNetwork");
defaultGateNetwork = defaultNetwork != null ? defaultNetwork.trim() : null;
maxGatesEachNetwork = newConfig.getInt("gates.maxGatesEachNetwork");
private void loadGateConfig() {
//Load the sign colors
String mainSignColor = (String) configOptions.get(ConfigOption.MAIN_SIGN_COLOR);
String highlightSignColor = (String) configOptions.get(ConfigOption.HIGHLIGHT_SIGN_COLOR);
loadPerSignColor(mainSignColor, highlightSignColor);
loadPerSignColors();
}
//Functionality
handleVehicles = newConfig.getBoolean("gates.functionality.handleVehicles");
enableBungee = newConfig.getBoolean("gates.functionality.enableBungee");
/**
* Loads the per-sign colors specified in the config file
*/
public void loadPerSignColors() {
List<?> perSignColors = (List<?>) configOptions.get(ConfigOption.PER_SIGN_COLORS);
ChatColor[] defaultColors = new ChatColor[]{PortalSignDrawer.getMainColor(), PortalSignDrawer.getHighlightColor()};
List<Map<Material, ChatColor>> colorMaps = new ArrayList<>();
colorMaps.add(new HashMap<>());
colorMaps.add(new HashMap<>());
//Integrity
protectEntrance = newConfig.getBoolean("gates.integrity.protectEntrance");
verifyPortals = newConfig.getBoolean("gates.integrity.verifyPortals");
destroyExplosion = newConfig.getBoolean("gates.integrity.destroyedByExplosion");
for (Object signColorSpecification : perSignColors) {
parsePerSignColors(signColorSpecification, defaultColors, colorMaps);
}
//Cosmetic
sortNetworkDestinations = newConfig.getBoolean("gates.cosmetic.sortNetworkDestinations");
rememberDestination = newConfig.getBoolean("gates.cosmetic.rememberDestination");
loadSignColor(newConfig.getString("gates.cosmetic.mainSignColor"),
newConfig.getString("gates.cosmetic.highlightSignColor"));
PortalSignDrawer.setPerSignMainColors(colorMaps.get(0));
PortalSignDrawer.setPerSignHighlightColors(colorMaps.get(1));
}
/**
* Parses a per-sign color specification object and stores the result
*
* @param signColorSpecification <p>The sign color specification to parse</p>
* @param defaultColors <p>The specified default colors</p>
* @param colorMaps <p>The list of color maps to save the resulting colors to</p>
*/
private void parsePerSignColors(Object signColorSpecification, ChatColor[] defaultColors,
List<Map<Material, ChatColor>> colorMaps) {
String[] specificationData = String.valueOf(signColorSpecification).split(":");
Material[] signMaterials = new Material[]{Material.matchMaterial(specificationData[0] + "_SIGN"),
Material.matchMaterial(specificationData[0] + "_WALL_SIGN")};
if (specificationData.length != 2) {
Stargate.logWarning("You have an invalid per-sign line in your config.yml file. Please fix it!");
return;
}
String[] colors = specificationData[1].split(",");
if (colors.length != 2) {
Stargate.logWarning("You have an invalid per-sign line in your config.yml file. Please fix it!");
return;
}
for (int colorIndex = 0; colorIndex < 2; colorIndex++) {
if (colors[colorIndex].equalsIgnoreCase("default")) {
continue;
}
loadPerSignColor(colors, colorIndex, defaultColors, signMaterials, colorMaps);
}
}
/**
* Loads a per-sign color
*
* @param colors <p>The colors specified in the config file</p>
* @param colorIndex <p>The index of the color to load</p>
* @param defaultColors <p>The specified default colors</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>
*/
private void loadPerSignColor(String[] colors, int colorIndex, ChatColor[] defaultColors, Material[] signMaterials,
List<Map<Material, ChatColor>> colorMaps) {
ChatColor parsedColor;
if (colors[colorIndex].equalsIgnoreCase("inverted")) {
//Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor
java.awt.Color color = defaultColors[colorIndex].getColor();
parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), color.getGreen(),
color.getBlue())));
} else {
try {
parsedColor = ChatColor.of(colors[colorIndex]);
} catch (IllegalArgumentException | NullPointerException exception) {
Stargate.logWarning("You have specified an invalid per-sign color in your config.yml. Custom color for \"" +
signMaterials[0] + "\" disabled");
return;
}
}
if (parsedColor != null) {
for (Material signMaterial : signMaterials) {
colorMaps.get(colorIndex).put(signMaterial, parsedColor);
}
}
}
/**
@ -161,17 +309,20 @@ public final class StargateGateConfig {
*
* @param mainSignColor <p>A string representing the main sign color</p>
*/
private void loadSignColor(String mainSignColor, String highlightSignColor) {
if (mainSignColor != null) {
try {
PortalSignDrawer.setColors(ChatColor.valueOf(mainSignColor.toUpperCase()),
ChatColor.valueOf(highlightSignColor.toUpperCase()));
return;
} catch (IllegalArgumentException | NullPointerException ignored) {
}
private void loadPerSignColor(String mainSignColor, String highlightSignColor) {
try {
PortalSignDrawer.setMainColor(ChatColor.of(mainSignColor.toUpperCase()));
} catch (IllegalArgumentException | NullPointerException exception) {
Stargate.logWarning("You have specified an invalid main sign color in your config.yml. Defaulting to BLACK");
PortalSignDrawer.setMainColor(ChatColor.BLACK);
}
try {
PortalSignDrawer.setHighlightColor(ChatColor.of(highlightSignColor.toUpperCase()));
} catch (IllegalArgumentException | NullPointerException exception) {
Stargate.logWarning("You have specified an invalid highlighting sign color in your config.yml. Defaulting to WHITE");
PortalSignDrawer.setHighlightColor(ChatColor.WHITE);
}
Stargate.logWarning("You have specified an invalid color in your config.yml. Defaulting to BLACK and WHITE");
PortalSignDrawer.setColors(ChatColor.BLACK, ChatColor.WHITE);
}
}

View File

@ -58,16 +58,11 @@ public class RelativeBlockVector {
* @return <p>A new relative block vector with the property altered</p>
*/
public RelativeBlockVector addToVector(Property propertyToAddTo, int valueToAdd) {
switch (propertyToAddTo) {
case RIGHT:
return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out);
case DOWN:
return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out);
case OUT:
return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd);
default:
throw new IllegalArgumentException("Invalid relative block vector property given");
}
return switch (propertyToAddTo) {
case RIGHT -> new RelativeBlockVector(this.right + valueToAdd, this.down, this.out);
case DOWN -> new RelativeBlockVector(this.right, this.down + valueToAdd, this.out);
case OUT -> new RelativeBlockVector(this.right, this.down, this.out + valueToAdd);
};
}
/**

View File

@ -0,0 +1,67 @@
package net.knarcraft.stargate.container;
import net.knarcraft.stargate.utility.ColorHelper;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.block.Sign;
/**
* A class that keeps track of the sign colors for a given sign
*/
public class SignData {
private final Sign sign;
private final ChatColor mainSignColor;
private final ChatColor highlightSignColor;
private final DyeColor dyedColor;
/**
* Instantiates a new sign colors object
*
* @param sign <p>The sign the colors belong to</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>
*/
public SignData(Sign sign, ChatColor mainSignColor, ChatColor highlightSignColor) {
this.sign = sign;
this.mainSignColor = mainSignColor;
this.highlightSignColor = highlightSignColor;
this.dyedColor = sign.getColor();
}
/**
* Gets the sign of this sign colors object
*
* @return <p>The sign of this sign colors object</p>
*/
public Sign getSign() {
return sign;
}
/**
* Gets the main color of the sign
*
* @return <p>The main color of the sign</p>
*/
public ChatColor getMainSignColor() {
if (dyedColor != DyeColor.BLACK) {
return ColorHelper.fromColor(dyedColor.getColor());
} else {
return mainSignColor;
}
}
/**
* Gets the highlighting color of the sign
*
* @return <p>The highlighting color of the sign</p>
*/
public ChatColor getHighlightSignColor() {
if (dyedColor != DyeColor.BLACK) {
return ColorHelper.fromColor(ColorHelper.invert(dyedColor.getColor()));
} else {
return highlightSignColor;
}
}
}

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>
*/
@SuppressWarnings("unused")
public class StargateEntityPortalEvent extends StargateEvent {
public class StargateEntityPortalEvent extends StargateEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList();
final Entity travellingEntity;
@ -58,6 +58,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
*
* @return <p>Location of the exit point</p>
*/
@Override
public Location getExit() {
return exit;
}

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>
*/
@SuppressWarnings("unused")
public class StargatePlayerPortalEvent extends StargatePlayerEvent {
public class StargatePlayerPortalEvent extends StargatePlayerEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList();
private final Portal destination;
@ -47,6 +47,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
*
* @return <p>Location of the exit point</p>
*/
@Override
public Location getExit() {
return exit;
}

View File

@ -0,0 +1,18 @@
package net.knarcraft.stargate.event;
import org.bukkit.Location;
import org.bukkit.event.Cancellable;
/**
* 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>
*/
Location getExit();
}

View File

@ -1,6 +1,7 @@
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;
@ -9,6 +10,7 @@ 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;
@ -23,6 +25,7 @@ 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;
@ -81,12 +84,35 @@ public class BlockEventListener implements Listener {
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
*
@ -131,7 +157,9 @@ public class BlockEventListener implements Listener {
//Destroy denied
if (destroyEvent.getDeny()) {
Stargate.getMessageSender().sendErrorMessage(player, destroyEvent.getDenyReason());
if (!destroyEvent.getDenyReason().trim().isEmpty()) {
Stargate.getMessageSender().sendErrorMessage(player, destroyEvent.getDenyReason());
}
event.setCancelled(true);
return;
}
@ -160,7 +188,7 @@ public class BlockEventListener implements Listener {
if (cost != 0) {
String portalName = portal.getName();
//Cannot pay
if (!Stargate.getEconomyConfig().chargePlayerIfNecessary(player, cost)) {
if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) {
Stargate.debug("onBlockBreak", "Insufficient Funds");
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
event.setCancelled(true);

View File

@ -0,0 +1,25 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.PortalHandler;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
/**
* A listener that listens for any relevant events causing entities to spawn
*/
public class EntitySpawnListener implements Listener {
@EventHandler
public void onCreatureSpawn(CreatureSpawnEvent event) {
//Prevent Zombified Piglins and other creatures form spawning at stargates
if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NETHER_PORTAL) {
if (PortalHandler.getByEntrance(event.getLocation()) != null) {
event.setCancelled(true);
Stargate.debug("EntitySpawnListener", "Prevented creature from spawning at Stargate");
}
}
}
}

View File

@ -1,15 +1,21 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.MessageSender;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.portal.PlayerTeleporter;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalActivator;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.VehicleTeleporter;
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.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import net.knarcraft.stargate.utility.UUIDMigrationHelper;
import net.knarcraft.stargate.utility.UpdateChecker;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.WallSign;
@ -25,6 +31,12 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.Map;
/**
* This listener listens to any player-related events related to stargates
@ -32,8 +44,7 @@ import org.bukkit.event.player.PlayerMoveEvent;
@SuppressWarnings("unused")
public class PlayerEventListener implements Listener {
private static long eventTime;
private static PlayerInteractEvent previousEvent;
private static final Map<Player, Long> previousEventTimes = new HashMap<>();
/**
* This event handler handles detection of any player teleporting through a bungee gate
@ -42,11 +53,22 @@ public class PlayerEventListener implements Listener {
*/
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
//Migrate player name to UUID if necessary
UUIDMigrationHelper.migrateUUID(player);
//Notify joining admins about the available update
String availableUpdate = Stargate.getUpdateAvailable();
if (availableUpdate != null && Stargate.getStargateConfig().alertAdminsAboutUpdates() &&
player.hasPermission("stargate.admin")) {
String updateMessage = UpdateChecker.getUpdateAvailableString(availableUpdate, Stargate.getPluginVersion());
Stargate.getMessageSender().sendErrorMessage(player, updateMessage);
}
if (!Stargate.getGateConfig().enableBungee()) {
return;
}
Player player = event.getPlayer();
//Check if the player is waiting to be teleported to a stargate
String destination = BungeeHelper.removeFromQueue(player.getUniqueId());
if (destination == null) {
@ -86,7 +108,8 @@ public class PlayerEventListener implements Listener {
Entity playerVehicle = player.getVehicle();
//If the player is in a vehicle, but vehicle handling is disabled, just ignore the player
if (playerVehicle == null || Stargate.getGateConfig().handleVehicles()) {
if (playerVehicle == null || (playerVehicle instanceof LivingEntity &&
Stargate.getGateConfig().handleVehicles())) {
teleportPlayer(playerVehicle, player, entrancePortal, destination, event);
}
}
@ -109,12 +132,15 @@ public class PlayerEventListener implements Listener {
horse.setOwner(player);
}
//Teleport the player's vehicle
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleport(entrancePortal);
player.setVelocity(new Vector());
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleportEntity(entrancePortal);
} else {
//Just teleport the player like normal
new PlayerTeleporter(destination, player).teleport(entrancePortal, event);
new PlayerTeleporter(destination, player).teleportPlayer(entrancePortal, event);
}
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
entrancePortal.getPortalOpener().closePortal(false);
}
@ -154,12 +180,14 @@ public class PlayerEventListener implements Listener {
//Decide if the user should be teleported to another bungee server
if (entrancePortal.getOptions().isBungee()) {
if (bungeeTeleport(player, entrancePortal, event)) {
if (BungeeHelper.bungeeTeleport(player, entrancePortal, event) && !entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
return false;
}
return true;
//Make sure to check if the player has any leashed creatures, even though leashed teleportation is disabled
return TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
}
/**
@ -177,7 +205,7 @@ public class PlayerEventListener implements Listener {
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
handleRightClickBlock(event, player, block);
handleRightClickBlock(event, player, block, event.getHand());
} else if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getBlockData() instanceof WallSign) {
//Handle left click of a wall sign
handleSignClick(event, player, block, true);
@ -198,6 +226,18 @@ public class PlayerEventListener implements Listener {
return;
}
//Allow players with permissions to apply dye to signs
EquipmentSlot hand = event.getHand();
if (hand != null && (PermissionHelper.hasPermission(player, "stargate.admin.dye") ||
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);
if (leftClick) {
//Cancel event in creative mode to prevent breaking the sign
@ -233,10 +273,12 @@ public class PlayerEventListener implements Listener {
* @return <p>True if the player should be denied</p>
*/
private boolean cannotAccessPortal(Player player, Portal portal) {
boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getNetwork());
boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getCleanNetwork());
if (PermissionHelper.portalAccessDenied(player, portal, deny)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
return true;
}
return false;
@ -248,19 +290,20 @@ public class PlayerEventListener implements Listener {
* @param event <p>The event triggering the right-click</p>
* @param player <p>The player doing the right-click</p>
* @param block <p>The block the player clicked</p>
* @param hand <p>The hand the player used to interact with the stargate</p>
*/
private void handleRightClickBlock(PlayerInteractEvent event, Player player, Block block) {
private void handleRightClickBlock(PlayerInteractEvent event, Player player, Block block, EquipmentSlot hand) {
if (block.getBlockData() instanceof WallSign) {
handleSignClick(event, player, block, false);
return;
}
if (MaterialHelper.isButtonCompatible(block.getType())) {
//Prevent a double click caused by a Spigot bug
if (clickIsBug(event, block)) {
return;
}
//Prevent a double click caused by a Spigot bug
if (clickIsBug(event.getPlayer(), block)) {
return;
}
if (MaterialHelper.isButtonCompatible(block.getType())) {
Portal portal = PortalHandler.getByBlock(block);
if (portal == null) {
return;
@ -276,69 +319,69 @@ public class PlayerEventListener implements Listener {
}
PermissionHelper.openPortal(player, portal);
if (portal.getPortalOpener().isOpenFor(player)) {
if (portal.getPortalOpener().isOpenFor(player) && !MaterialHelper.isContainer(block.getType())) {
event.setUseInteractedBlock(Event.Result.ALLOW);
}
} else {
//Display information about the portal if it has no sign
ItemStack heldItem = player.getInventory().getItem(hand);
if (heldItem.getType().isAir() || !heldItem.getType().isBlock()) {
displayPortalInfo(block, player);
}
}
}
/**
* This function decides if a right click of a coral is caused by a Spigot bug
* Displays information about a clicked portal
*
* <p>The Spigot bug currently makes every right click of a coral trigger twice, causing the portal to close
* immediately. This fix should detect the bug without breaking wall coral buttons once the bug is fixed.</p>
* <p>This will only display portal info if the portal has no sign and is not silent.</p>
*
* @param event <p>The event causing the right click</p>
* @param block <p>The block to check</p>
* @param block <p>The clicked block</p>
* @param player <p>The player that clicked the block</p>
*/
private void displayPortalInfo(Block block, Player player) {
Portal portal = PortalHandler.getByBlock(block);
if (portal == null) {
return;
}
//Display portal information as a portal without a sign does not display any
if (portal.getOptions().hasNoSign() && (!portal.getOptions().isSilent() || player.isSneaking())) {
MessageSender sender = Stargate.getMessageSender();
sender.sendSuccessMessage(player, ChatColor.GOLD + Stargate.getString("portalInfoTitle"));
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoName"),
"%name%", portal.getName()));
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoDestination"),
"%destination%", portal.getDestinationName()));
if (portal.getOptions().isBungee()) {
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoServer"),
"%server%", portal.getNetwork()));
} else {
sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoNetwork"),
"%network%", portal.getNetwork()));
}
}
}
/**
* This function decides if a right click of a block is caused by a Spigot bug
*
* <p>The Spigot bug currently makes every right click of some blocks trigger twice, causing the portal to close
* immediately, or causing portal information printing twice. This fix should detect the bug without breaking
* clicking once the bug is fixed.</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>
*/
private boolean clickIsBug(PlayerInteractEvent event, Block block) {
if (MaterialHelper.isWallCoral(block.getType())) {
if (previousEvent != null &&
event.getPlayer() == previousEvent.getPlayer() && eventTime + 15 > System.currentTimeMillis()) {
previousEvent = null;
eventTime = 0;
return true;
}
previousEvent = event;
eventTime = System.currentTimeMillis();
private boolean clickIsBug(Player player, Block block) {
Long previousEventTime = previousEventTimes.get(player);
if (previousEventTime != null && previousEventTime + 50 > System.currentTimeMillis()) {
previousEventTimes.put(player, null);
return true;
}
previousEventTimes.put(player, System.currentTimeMillis());
return false;
}
/**
* Teleports a player to a bungee gate
*
* @param player <p>The player to teleport</p>
* @param entrancePortal <p>The gate the player is entering from</p>
* @param event <p>The event causing the teleportation</p>
* @return <p>True if the teleportation was successful</p>
*/
private boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) {
//Check if bungee is actually enabled
if (!Stargate.getGateConfig().enableBungee()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Teleport the player back to this gate, for sanity's sake
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
//Send the SGBungee packet first, it will be queued by BC if required
if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to send teleportation message");
return false;
}
//Send the connect-message to make the player change server
if (!BungeeHelper.changeServer(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to change server");
return false;
}
Stargate.debug("bungeeTeleport", "Teleported player to another server");
return true;
}
}

View File

@ -49,4 +49,5 @@ public class PluginEventListener implements Listener {
Stargate.logInfo("Vault plugin lost.");
}
}
}

View File

@ -2,9 +2,9 @@ package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.FromTheEndTeleportation;
import net.knarcraft.stargate.portal.PlayerTeleporter;
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;
@ -95,8 +95,7 @@ public class PortalEventListener implements Listener {
Portal exitPortal = teleportation.getExit();
//Overwrite respawn location to respawn in front of the portal
event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit(respawningPlayer,
respawningPlayer.getLocation()));
event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit());
//Properly close the portal to prevent it from staying in a locked state until it times out
exitPortal.getPortalOpener().closePortal(false);
}

View File

@ -3,10 +3,10 @@ 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.VehicleTeleporter;
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 net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
@ -61,11 +61,11 @@ public class VehicleEventListener implements Listener {
private static void teleportVehicle(List<Entity> passengers, Portal entrancePortal, Vehicle vehicle) {
String route = "VehicleEventListener::teleportVehicle";
if (!passengers.isEmpty() && passengers.get(0) instanceof Player) {
if (!passengers.isEmpty() && TeleportHelper.containsPlayer(passengers)) {
Stargate.debug(route, "Found passenger vehicle");
teleportPlayerAndVehicle(entrancePortal, vehicle, passengers);
teleportPlayerAndVehicle(entrancePortal, vehicle);
} else {
Stargate.debug(route, "Found empty vehicle");
Stargate.debug(route, "Found vehicle without players");
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination();
if (destinationPortal == null) {
Stargate.debug(route, "Unable to find portal destination");
@ -73,7 +73,7 @@ public class VehicleEventListener implements Listener {
}
Stargate.debug("vehicleTeleport", destinationPortal.getWorld() + " " +
destinationPortal.getSignLocation());
new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal);
new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal);
}
}
@ -82,81 +82,66 @@ public class VehicleEventListener implements Listener {
*
* @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)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle) {
Entity rootEntity = vehicle;
while (rootEntity.getVehicle() != null) {
rootEntity = rootEntity.getVehicle();
}
List<Player> players = TeleportHelper.getPlayers(rootEntity.getPassengers());
Portal destinationPortal = null;
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) {
destinationPortal = possibleDestinationPortal;
}
}
//Cancel the teleport if no players activated the portal, or if any players are denied access
boolean cancelTeleport = false;
for (Player player : players) {
if (destinationPortal == null) {
cancelTeleport = true;
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg"));
}
} else if (!TeleportHelper.playerCanTeleport(player, entrancePortal, destinationPortal)) {
cancelTeleport = true;
}
}
if (cancelTeleport) {
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;
//Take payment from all players
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
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
if (cost > 0) {
if (EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost)) {
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 = Stargate.getEconomyConfig().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).teleportEntity(entrancePortal);
if (teleported) {
if (!entrancePortal.getOptions().isSilent()) {
for (Player player : players) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
}
}
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal);
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)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Transfer payment if necessary
int cost = Stargate.getEconomyConfig().getUseCost(player, entrancePortal, destinationPortal);
return cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
}
}

View File

@ -46,4 +46,5 @@ public class WorldEventListener implements Listener {
PortalRegistry.clearPortals(world);
}
}
}

View File

@ -1,79 +0,0 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargatePlayerPortalEvent;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
/**
* The portal teleporter takes care of the actual portal teleportation for any players
*/
public class PlayerTeleporter extends Teleporter {
private final Player player;
/**
* Instantiates a new player teleporter
*
* @param portal <p>The portal which is the target of the teleportation</p>
* @param player <p>The teleporting player</p>
*/
public PlayerTeleporter(Portal portal, Player player) {
super(portal);
this.player = player;
}
/**
* Teleports a player to this teleporter's portal
*
* @param origin <p>The portal the player teleports from</p>
* @param event <p>The player move event triggering the event</p>
*/
public void teleport(Portal origin, PlayerMoveEvent event) {
Location traveller = player.getLocation();
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
if (!origin.equals(portal)) {
exit = triggerPlayerPortalEvent(origin, exit, event);
if (exit == null) {
return;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
//If no event is passed in, assume it's a teleport, and act as such
if (event == null) {
player.teleport(exit);
} else {
//The new method to teleport in a move event is set the "to" field.
event.setTo(exit);
}
}
/**
* 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

@ -2,6 +2,13 @@ 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.PortalOption;
import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.PortalStructure;
import net.knarcraft.stargate.portal.property.gate.Gate;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -13,8 +20,12 @@ import java.util.Map;
public class Portal {
private final String name;
private final String cleanName;
private final String network;
private final String cleanNetwork;
private final PortalOwner portalOwner;
private boolean isRegistered;
private final PortalOptions options;
private final PortalOpener portalOpener;
@ -46,6 +57,26 @@ public class Portal {
this.portalOpener = new PortalOpener(this, destination);
this.structure = new PortalStructure(this, gate, button);
this.portalActivator = portalOpener.getPortalActivator();
this.cleanName = cleanString(name);
this.cleanNetwork = cleanString(network);
}
/**
* Checks if this portal is registered
*
* @return <p>True if this portal is registered</p>
*/
public boolean isRegistered() {
return isRegistered;
}
/**
* Sets whether this portal is registered
*
* @param isRegistered <p>True if this portal is registered</p>
*/
public void setRegistered(boolean isRegistered) {
this.isRegistered = isRegistered;
}
/**
@ -124,6 +155,15 @@ public class Portal {
return network;
}
/**
* Gets the clean name of the network this portal belongs to
*
* @return <p>The clean network name</p>
*/
public String getCleanNetwork() {
return cleanNetwork;
}
/**
* Gets the time this portal was triggered (activated/opened)
*
@ -145,6 +185,15 @@ public class Portal {
return name;
}
/**
* Gets the clean name of this portal
*
* @return <p>The clean name of this portal</p>
*/
public String getCleanName() {
return cleanName;
}
/**
* Gets the portal opener used by this portal
*
@ -248,6 +297,16 @@ public class Portal {
return getTopLeft().getRelativeLocation(vector, getYaw());
}
/**
* Cleans a string by removing color codes, lower-casing and replacing spaces with underscores
*
* @param string <p>The string to clean</p>
* @return <p>The clean string</p>
*/
public static String cleanString(String string) {
return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)).toLowerCase();
}
@Override
public String toString() {
return String.format("Portal [id=%s, network=%s name=%s, type=%s]", getSignLocation(), network, name,
@ -258,8 +317,8 @@ public class Portal {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((network == null) ? 0 : network.hashCode());
result = prime * result + ((cleanName == null) ? 0 : cleanName.hashCode());
result = prime * result + ((cleanNetwork == null) ? 0 : cleanNetwork.hashCode());
return result;
}
@ -272,18 +331,19 @@ public class Portal {
return false;
}
Portal other = (Portal) object;
if (name == null) {
if (other.name != null) {
if (cleanName == null) {
if (other.cleanName != null) {
return false;
}
} else if (!name.equalsIgnoreCase(other.name)) {
} else if (!cleanName.equalsIgnoreCase(other.cleanName)) {
return false;
}
//If none of the portals have a name, check if the network is the same
if (network == null) {
return other.network == null;
if (cleanNetwork == null) {
return other.cleanNetwork == null;
} else {
return network.equalsIgnoreCase(other.network);
return cleanNetwork.equalsIgnoreCase(other.cleanNetwork);
}
}
}

View File

@ -6,7 +6,7 @@ import net.knarcraft.stargate.event.StargateDeactivateEvent;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
@ -64,7 +64,7 @@ public class PortalActivator {
* @return <p>The destination portal the player should teleport to</p>
*/
public Portal getDestination(Player player) {
String portalNetwork = portal.getNetwork();
String portalNetwork = portal.getCleanNetwork();
if (portal.getOptions().isRandom()) {
//Find possible destinations
List<String> destinations = PortalHandler.getDestinations(portal, player, portalNetwork);
@ -73,10 +73,10 @@ public class PortalActivator {
}
//Get one random destination
String destination = destinations.get((new Random()).nextInt(destinations.size()));
return PortalHandler.getByName(destination, portalNetwork);
return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork);
} else {
//Just return the normal fixed destination
return PortalHandler.getByName(destination, portalNetwork);
return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork);
}
}
@ -136,12 +136,12 @@ public class PortalActivator {
//Set the given player as the active player
activePlayer = player;
String network = portal.getNetwork();
String network = portal.getCleanNetwork();
destinations = PortalHandler.getDestinations(portal, player, network);
//Sort destinations if enabled
if (Stargate.getGateConfig().sortNetworkDestinations()) {
Collections.sort(destinations);
destinations.sort(Comparator.comparing(Portal::cleanString));
}
//Select last used destination if remember destination is enabled
@ -242,13 +242,15 @@ public class PortalActivator {
activate = true;
Stargate.debug("cycleDestination", "Network Size: " +
PortalHandler.getNetwork(portal.getNetwork()).size());
PortalHandler.getNetwork(portal.getCleanNetwork()).size());
Stargate.debug("cycleDestination", "Player has access to: " + destinations.size());
}
//If no destinations are available, just tell the player and quit
if (destinations.size() == 0) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("destEmpty"));
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("destEmpty"));
}
return;
}

View File

@ -4,20 +4,26 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.event.StargateCreateEvent;
import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.Player;
import org.bukkit.event.block.SignChangeEvent;
import java.util.List;
import java.util.Map;
import static net.knarcraft.stargate.Stargate.getMaxNameNetworkLength;
/**
* The portal creator can create and validate a new portal
*/
@ -45,16 +51,16 @@ public class PortalCreator {
*/
public Portal createPortal() {
BlockLocation signLocation = new BlockLocation(event.getBlock());
Block idParent = signLocation.getParent();
Block signControlBlock = signLocation.getParent();
//Return early if the sign is not placed on a block, or the block is not a control block
if (idParent == null || GateHandler.getGatesByControlBlock(idParent).length == 0) {
if (signControlBlock == null || GateHandler.getGatesByControlBlock(signControlBlock).length == 0) {
Stargate.debug("createPortal", "Control block not registered");
return null;
}
//The control block is already part of another portal
if (PortalHandler.getByBlock(idParent) != null) {
if (PortalHandler.getByBlock(signControlBlock) != null) {
Stargate.debug("createPortal", "idParent belongs to existing stargate");
return null;
}
@ -69,7 +75,8 @@ public class PortalCreator {
Map<PortalOption, Boolean> portalOptions = PortalHandler.getPortalOptions(player, destinationName, options);
//Get the yaw
float yaw = DirectionHelper.getYawFromLocationDifference(idParent.getLocation(), signLocation.getLocation());
float yaw = DirectionHelper.getYawFromLocationDifference(signControlBlock.getLocation(),
signLocation.getLocation());
//Get the direction the button should be facing
BlockFace buttonFacing = DirectionHelper.getBlockFaceFromYaw(yaw);
@ -80,7 +87,7 @@ public class PortalCreator {
Stargate.debug("createPortal", "Finished getting all portal info");
//Try and find a gate matching the new portal
Gate gate = PortalHandler.findMatchingGate(portalLocation, player);
Gate gate = PortalHandler.findMatchingGate(portalLocation, player.getWorld());
if ((gate == null) || (portalLocation.getButtonVector() == null)) {
Stargate.debug("createPortal", "Could not find matching gate layout");
return null;
@ -100,7 +107,8 @@ public class PortalCreator {
Stargate.debug("createPortal", builder.toString());
//Use default network if a proper alternative is not set
if (!portalOptions.get(PortalOption.BUNGEE) && (network.length() < 1 || network.length() > 11)) {
if (!portalOptions.get(PortalOption.BUNGEE) && (network.length() < 1 || network.length() >
getMaxNameNetworkLength())) {
network = Stargate.getDefaultNetwork();
}
@ -112,8 +120,8 @@ public class PortalCreator {
Stargate.debug("createPortal", "Player doesn't have create permissions on network. Trying personal");
if (PermissionHelper.canCreatePersonalPortal(player)) {
network = player.getName();
if (network.length() > 11) {
network = network.substring(0, 11);
if (network.length() > getMaxNameNetworkLength()) {
network = network.substring(0, getMaxNameNetworkLength());
}
Stargate.debug("createPortal", "Creating personal portal");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createPersonal"));
@ -184,7 +192,9 @@ public class PortalCreator {
//Tell the user why it was denied from creating the portal
if (stargateCreateEvent.getDeny()) {
Stargate.getMessageSender().sendErrorMessage(player, stargateCreateEvent.getDenyReason());
if (!stargateCreateEvent.getDenyReason().trim().isEmpty()) {
Stargate.getMessageSender().sendErrorMessage(player, stargateCreateEvent.getDenyReason());
}
return null;
}
@ -196,9 +206,8 @@ public class PortalCreator {
}
//Add button if the portal is not always on
if (!portalOptions.isAlwaysOn() && !portalOptions.isBungee()) {
generatePortalButton(portalLocation.getTopLeft(), portalLocation.getButtonVector(),
portalLocation.getButtonFacing());
if (!portalOptions.isAlwaysOn()) {
PortalFileHelper.generatePortalButton(portal, portalLocation.getButtonFacing());
}
//Register the new portal
@ -224,29 +233,30 @@ public class PortalCreator {
*/
private boolean checkIfNewPortalIsValid(int cost, String portalName) {
//Check if the portal name can fit on the sign with padding (>name<)
if (portal.getName().length() < 1 || portal.getName().length() > 11) {
Stargate.debug("createPortal", "Name length error");
if (portal.getCleanName().length() < 1 || portal.getCleanName().length() > getMaxNameNetworkLength()) {
Stargate.debug("createPortal", String.format("Name length error. %s is too long.",
portal.getCleanName()));
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createNameLength"));
return false;
}
if (portal.getOptions().isBungee()) {
//Check if the bungee portal's name has been duplicated
if (PortalHandler.getBungeePortals().get(portal.getName().toLowerCase()) != null) {
if (PortalHandler.getBungeePortals().get(portal.getCleanName()) != null) {
Stargate.debug("createPortal::Bungee", "Gate name duplicate");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists"));
return false;
}
} else {
//Check if the portal name has been duplicated on the network
if (PortalHandler.getByName(portal.getName(), portal.getNetwork()) != null) {
if (PortalHandler.getByName(portal.getCleanName(), portal.getCleanNetwork()) != null) {
Stargate.debug("createPortal", "Gate name duplicate");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists"));
return false;
}
//Check if the number of portals in the network has been surpassed
List<String> networkList = PortalHandler.getAllPortalNetworks().get(portal.getNetwork().toLowerCase());
List<String> networkList = PortalHandler.getAllPortalNetworks().get(portal.getCleanNetwork());
int maxGates = Stargate.getGateConfig().maxGatesEachNetwork();
if (maxGates > 0 && networkList != null && networkList.size() >= maxGates) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createFull"));
@ -256,7 +266,7 @@ public class PortalCreator {
if (cost > 0) {
//Deduct the required fee from the player
if (!Stargate.getEconomyConfig().chargePlayerIfNecessary(player, cost)) {
if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) {
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
Stargate.debug("createPortal", "Insufficient Funds");
return false;
@ -267,25 +277,6 @@ public class PortalCreator {
return true;
}
/**
* Generates a button for a portal
*
* @param topLeft <p>The top-left block of the portal</p>
* @param buttonVector <p>A relative vector pointing at the button</p>
* @param buttonFacing <p>The direction the button should be facing</p>
*/
private void generatePortalButton(BlockLocation topLeft, RelativeBlockVector buttonVector,
BlockFace buttonFacing) {
//Go one block outwards to find the button's location rather than the control block's location
BlockLocation button = topLeft.getRelativeLocation(buttonVector.addToVector(
RelativeBlockVector.Property.OUT, 1), portal.getYaw());
Directional buttonData = (Directional) Bukkit.createBlockData(portal.getGate().getPortalButton());
buttonData.setFacing(buttonFacing);
button.getBlock().setBlockData(buttonData);
portal.getStructure().setButton(button);
}
/**
* Updates the open state of the newly created portal
*
@ -298,7 +289,7 @@ public class PortalCreator {
portal.getPortalOpener().openPortal(true);
} else if (portal.getOptions().isAlwaysOn()) {
//For a normal always-on portal, open both the portal and the destination
Portal destinationPortal = PortalHandler.getByName(destinationName, portal.getNetwork());
Portal destinationPortal = PortalHandler.getByName(destinationName, portal.getCleanNetwork());
if (destinationPortal != null) {
portal.getPortalOpener().openPortal(true);
destinationPortal.drawSign();

View File

@ -3,8 +3,14 @@ package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
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.PortalOption;
import net.knarcraft.stargate.portal.property.PortalStructure;
import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
@ -60,7 +66,7 @@ public class PortalHandler {
*/
public static List<String> getDestinations(Portal entrancePortal, Player player, String network) {
List<String> destinations = new ArrayList<>();
for (String destination : PortalRegistry.getAllPortalNetworks().get(network.toLowerCase())) {
for (String destination : PortalRegistry.getAllPortalNetworks().get(network)) {
Portal portal = getByName(destination, network);
if (portal == null) {
continue;
@ -74,11 +80,12 @@ public class PortalHandler {
continue;
}
//Check if destination is this portal
if (destination.equalsIgnoreCase(entrancePortal.getName())) {
if (destination.equals(entrancePortal.getCleanName())) {
continue;
}
//Check if destination is a fixed portal not pointing to this portal
if (portal.getOptions().isFixed() && !portal.getDestinationName().equalsIgnoreCase(entrancePortal.getName())) {
if (portal.getOptions().isFixed() &&
!Portal.cleanString(portal.getDestinationName()).equals(entrancePortal.getCleanName())) {
continue;
}
//Allow random use by non-players (Minecarts)
@ -137,12 +144,12 @@ public class PortalHandler {
* 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 player <p>The player trying to create 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>
*/
static Gate findMatchingGate(PortalLocation portalLocation, Player player) {
static Gate findMatchingGate(PortalLocation portalLocation, World world) {
Block signParent = portalLocation.getSignLocation().getParent();
BlockLocation parent = new BlockLocation(player.getWorld(), signParent.getX(), signParent.getY(),
BlockLocation parent = new BlockLocation(world, signParent.getX(), signParent.getY(),
signParent.getZ());
//Get all gates with the used type of control blocks
@ -176,10 +183,10 @@ public class PortalHandler {
* @param portal <p>The newly created portal</p>
*/
static void updatePortalsPointingAtNewPortal(Portal portal) {
for (String originName : PortalRegistry.getAllPortalNetworks().get(portal.getNetwork().toLowerCase())) {
Portal origin = getByName(originName, portal.getNetwork());
for (String originName : PortalRegistry.getAllPortalNetworks().get(portal.getCleanNetwork())) {
Portal origin = getByName(originName, portal.getCleanNetwork());
if (origin == null ||
!origin.getDestinationName().equalsIgnoreCase(portal.getName()) ||
!Portal.cleanString(origin.getDestinationName()).equals(portal.getCleanName()) ||
!origin.getStructure().isVerified()) {
continue;
}
@ -441,4 +448,5 @@ public class PortalHandler {
}
return input.replaceAll("[|:#]", "").trim();
}
}

View File

@ -5,6 +5,7 @@ import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.event.StargateCloseEvent;
import net.knarcraft.stargate.event.StargateOpenEvent;
import net.knarcraft.stargate.portal.property.PortalOptions;
import org.bukkit.Axis;
import org.bukkit.Material;
import org.bukkit.block.data.Orientable;
@ -125,7 +126,7 @@ public class PortalOpener {
return;
}
boolean thisIsDestination = destination.getDestinationName().equalsIgnoreCase(portal.getName());
boolean thisIsDestination = Portal.cleanString(destination.getDestinationName()).equals(portal.getCleanName());
//Only open destination if it's not-fixed or points at this portal, and is not already open
if (!options.isRandom() && (!destination.getOptions().isFixed() || thisIsDestination) && !destination.isOpen()) {
//Open the destination portal

View File

@ -4,8 +4,6 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.World;
import org.bukkit.block.Sign;
import org.bukkit.block.data.type.WallSign;
import java.util.ArrayList;
import java.util.HashMap;
@ -37,6 +35,7 @@ public class PortalRegistry {
lookupControls.clear();
allPortals.clear();
allPortalNetworks.clear();
bungeePortals.clear();
}
/**
@ -64,7 +63,7 @@ public class PortalRegistry {
private static void clearPortals(List<Portal> portalsToRemove) {
//Store the names of the portals to remove as some maps require the name, not the object
List<String> portalNames = new ArrayList<>();
portalsToRemove.forEach((portal) -> portalNames.add(portal.getName()));
portalsToRemove.forEach((portal) -> portalNames.add(portal.getCleanName()));
//Clear all the lookup locations for the portals
lookupBlocks.keySet().removeIf((key) -> portalsToRemove.contains(lookupBlocks.get(key)));
@ -163,10 +162,11 @@ public class PortalRegistry {
*/
public static void unregisterPortal(Portal portal, boolean removeAll) {
Stargate.debug("Unregister", "Unregistering gate " + portal.getName());
portal.getPortalActivator().deactivate();
portal.getPortalOpener().closePortal(true);
String portalName = portal.getName().toLowerCase();
String networkName = portal.getNetwork().toLowerCase();
String portalName = portal.getCleanName();
String networkName = portal.getCleanNetwork();
//Remove portal from lookup blocks
for (BlockLocation block : portal.getStructure().getFrame()) {
@ -203,7 +203,7 @@ public class PortalRegistry {
//Update all portals in the same network with this portal as its destination
for (String originName : allPortalNetworks.get(networkName)) {
Portal origin = PortalHandler.getByName(originName, portal.getNetwork());
Portal origin = PortalHandler.getByName(originName, portal.getCleanNetwork());
if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) ||
!origin.getStructure().isVerified()) {
continue;
@ -219,17 +219,11 @@ public class PortalRegistry {
}
}
//Clear sign data
if (portal.getSignLocation().getBlock().getBlockData() instanceof WallSign) {
Sign sign = (Sign) portal.getSignLocation().getBlock().getState();
sign.setLine(0, portal.getName());
sign.setLine(1, "");
sign.setLine(2, "");
sign.setLine(3, "");
sign.update();
}
//Mark the portal's sign as unregistered
new PortalSignDrawer(portal).drawUnregisteredSign();
PortalFileHelper.saveAllPortals(portal.getWorld());
portal.setRegistered(false);
}
/**
@ -241,8 +235,8 @@ public class PortalRegistry {
portal.getOptions().setFixed(portal.getDestinationName().length() > 0 || portal.getOptions().isRandom() ||
portal.getOptions().isBungee());
String portalName = portal.getName().toLowerCase();
String networkName = portal.getNetwork().toLowerCase();
String portalName = portal.getCleanName();
String networkName = portal.getCleanNetwork();
//Bungee portals are stored in their own list
if (portal.getOptions().isBungee()) {
@ -250,14 +244,14 @@ public class PortalRegistry {
} else {
//Check if network exists in the lookup list. If not, register the new network
if (!portalLookupByNetwork.containsKey(networkName)) {
Stargate.debug("register", "Network " + portal.getNetwork() +
" not in lookupNamesNet, adding");
Stargate.debug("register", String.format("Network %s not in lookupNamesNet, adding",
portal.getNetwork()));
portalLookupByNetwork.put(networkName, new HashMap<>());
}
//Check if this network exists in the network list. If not, register the network
if (!allPortalNetworks.containsKey(networkName)) {
Stargate.debug("register", "Network " + portal.getNetwork() +
" not in allPortalsNet, adding");
Stargate.debug("register", String.format("Network %s not in allPortalsNet, adding",
portal.getNetwork()));
allPortalNetworks.put(networkName, new ArrayList<>());
}
@ -277,8 +271,10 @@ public class PortalRegistry {
lookupBlocks.put(block, portal);
}
//Register the sign and button to the lookup lists
lookupBlocks.put(portal.getSignLocation(), portal);
lookupControls.put(portal.getSignLocation(), portal);
if (!portal.getOptions().hasNoSign()) {
lookupBlocks.put(portal.getSignLocation(), portal);
lookupControls.put(portal.getSignLocation(), portal);
}
BlockLocation button = portal.getStructure().getButton();
if (button != null) {
@ -292,6 +288,7 @@ public class PortalRegistry {
}
allPortals.add(portal);
portal.setRegistered(true);
}
}

View File

@ -1,12 +1,19 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.SignData;
import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import java.util.Map;
import static net.knarcraft.stargate.utility.ColorHelper.translateAllColorCodes;
/**
* The portal sign drawer draws the sing of a given portal
*/
@ -14,9 +21,11 @@ public class PortalSignDrawer {
private final Portal portal;
private final static ChatColor errorColor = ChatColor.DARK_RED;
private final static ChatColor freeColor = ChatColor.DARK_GREEN;
private static ChatColor freeColor;
private static ChatColor mainColor;
private static ChatColor highlightColor;
private static Map<Material, ChatColor> perSignMainColors;
private static Map<Material, ChatColor> perSignHighlightColors;
/**
* Instantiates a new portal sign drawer
@ -28,197 +37,311 @@ public class PortalSignDrawer {
}
/**
* Sets the main sign color
* Sets the highlighting sign color
*
* <p>The main sign color is used for most text on the sign, while the highlighting color is used for the markings
* around portal names and network names ('>','<','-',')','(')</p>
* <p>The highlighting color is used for the markings around portal names and network names ('>','<','-',')','(').</p>
*
* @param newMainColor <p>The new main sign color</p>
* @param newHighlightColor <p>The new highlight color</p>
*/
public static void setColors(ChatColor newMainColor, ChatColor newHighlightColor) {
mainColor = newMainColor;
public static void setHighlightColor(ChatColor newHighlightColor) {
highlightColor = newHighlightColor;
}
/**
* Sets the main sign color
*
* <p>The main sign color is used for most text on the sign.</p>
*
* @param newMainColor <p>The new main sign color</p>
*/
public static void setMainColor(ChatColor newMainColor) {
mainColor = newMainColor;
}
/**
* Sets the color to use for marking free stargates
*
* @param freeColor <p>The new color to use for marking free stargates</p>
*/
public static void setFreeColor(ChatColor freeColor) {
PortalSignDrawer.freeColor = freeColor;
}
/**
* Sets the per-sign main colors
*
* @param signMainColors <p>The per-sign main colors</p>
*/
public static void setPerSignMainColors(Map<Material, ChatColor> signMainColors) {
PortalSignDrawer.perSignMainColors = signMainColors;
}
/**
* Sets the per-sign highlight colors
*
* @param signHighlightColors <p>The per-sign highlight colors</p>
*/
public static void setPerSignHighlightColors(Map<Material, ChatColor> signHighlightColors) {
PortalSignDrawer.perSignHighlightColors = signHighlightColors;
}
/**
* Gets the currently used main sign color
*
* @return <p>The currently used main sign color</p>
*/
public static ChatColor getMainColor() {
return mainColor;
}
/**
* Gets the currently used highlighting sign color
*
* @return <p>The currently used highlighting sign color</p>
*/
public static ChatColor getHighlightColor() {
return highlightColor;
}
/**
* Draws the sign of the portal this sign drawer is responsible for
*/
public void drawSign() {
Block signBlock = portal.getSignLocation().getBlock();
BlockState state = signBlock.getState();
if (!(state instanceof Sign sign)) {
Stargate.logWarning("Sign block is not a Sign object");
Stargate.debug("Portal::drawSign", String.format("Block: %s @ %s", signBlock.getType(),
signBlock.getLocation()));
Sign sign = getSign();
if (sign == null) {
return;
}
drawSign(sign);
SignData signData = new SignData(sign, getMainColor(sign.getType()), getHighlightColor(sign.getType()));
drawSign(signData);
}
/**
* Gets the sign for this sign drawer's portal
*
* @return <p>The sign of this sign drawer's portal</p>
*/
private Sign getSign() {
Block signBlock = portal.getSignLocation().getBlock();
BlockState state = signBlock.getState();
if (!(state instanceof Sign sign)) {
if (!portal.getOptions().hasNoSign()) {
Stargate.logWarning("Sign block is not a Sign object");
Stargate.debug("Portal::drawSign", String.format("Block: %s @ %s", signBlock.getType(),
signBlock.getLocation()));
}
return null;
}
return sign;
}
/**
* Draws the sign of the portal this sign drawer is responsible for
*
* @param sign <p>The sign re-draw</p>
* @param signData <p>All necessary sign information</p>
*/
private void drawSign(Sign sign) {
private void drawSign(SignData signData) {
Sign sign = signData.getSign();
ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor();
//Clear sign
for (int index = 0; index <= 3; index++) {
sign.setLine(index, "");
}
setLine(sign, 0, highlightColor + "-" + mainColor +
portal.getName() + highlightColor + "-");
clearSign(sign);
setLine(signData, 0, highlightColor + "-" + mainColor + translateAllColorCodes(portal.getName()) +
highlightColor + "-");
if (!portal.getPortalActivator().isActive()) {
//Default sign text
drawInactiveSign(sign);
drawInactiveSign(signData);
} else {
if (portal.getOptions().isBungee()) {
//Bungee sign
drawBungeeSign(sign);
drawBungeeSign(signData);
} else if (portal.getOptions().isFixed()) {
//Sign pointing at one other portal
drawFixedSign(sign);
drawFixedSign(signData);
} else {
//Networking stuff
drawNetworkSign(sign);
drawNetworkSign(signData);
}
}
sign.update();
}
/**
* Clears all lines of a sign, but does not update the sign
*
* @param sign <p>The sign to clear</p>
*/
private void clearSign(Sign sign) {
for (int index = 0; index <= 3; index++) {
sign.setLine(index, "");
}
}
/**
* Marks this sign drawer's portal as unregistered
*/
public void drawUnregisteredSign() {
Sign sign = getSign();
if (sign == null) {
return;
}
clearSign(sign);
sign.setLine(0, translateAllColorCodes(portal.getName()));
sign.update();
}
/**
* Draws a sign with choose-able network locations
*
* @param sign <p>The sign to re-draw</p>
* @param signData <p>All necessary sign information</p>
*/
private void drawNetworkSign(Sign sign) {
private void drawNetworkSign(SignData signData) {
PortalActivator destinations = portal.getPortalActivator();
int maxIndex = destinations.getDestinations().size() - 1;
int signLineIndex = 0;
int destinationIndex = destinations.getDestinations().indexOf(portal.getDestinationName());
boolean freeGatesGreen = Stargate.getEconomyConfig().useEconomy() &&
Stargate.getEconomyConfig().drawFreePortalsGreen();
boolean freeGatesColored = Stargate.getEconomyConfig().useEconomy() &&
Stargate.getEconomyConfig().drawFreePortalsColored();
//Last, and not only entry. Draw the entry two back
if ((destinationIndex == maxIndex) && (maxIndex > 1)) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex - 2);
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 2);
}
//Not first entry. Draw the previous entry
if (destinationIndex > 0) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex - 1);
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 1);
}
//Draw the chosen entry (line 2 or 3)
drawNetworkSignChosenLine(freeGatesGreen, sign, ++signLineIndex);
drawNetworkSignChosenLine(signData, freeGatesColored, ++signLineIndex);
//Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 1)) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex + 1);
drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex + 1);
}
//Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 2) && (++signLineIndex <= 3)) {
drawNetworkSignLine(freeGatesGreen, sign, signLineIndex, destinationIndex + 2);
drawNetworkSignLine(signData, freeGatesColored, signLineIndex, destinationIndex + 2);
}
}
/**
* Draws the chosen destination on one sign line
*
* @param freeGatesGreen <p>Whether to display free gates in a green color</p>
* @param sign <p>The sign to draw on</p>
* @param signLineIndex <p>The line to draw on</p>
* @param signData <p>All necessary sign information</p>
* @param freeGatesColored <p>Whether to display free gates in a different color</p>
* @param signLineIndex <p>The line to draw on</p>
*/
private void drawNetworkSignChosenLine(boolean freeGatesGreen, Sign sign, int signLineIndex) {
if (freeGatesGreen) {
private void drawNetworkSignChosenLine(SignData signData, boolean freeGatesColored, int signLineIndex) {
ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor();
if (freeGatesColored) {
Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork());
boolean green = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination);
ChatColor nameColor = (green ? freeColor : highlightColor);
setLine(sign, signLineIndex, nameColor + ">" + (green ? freeColor : mainColor) +
portal.getDestinationName() + nameColor + "<");
boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination);
ChatColor nameColor = (free ? freeColor : highlightColor);
setLine(signData, signLineIndex, nameColor + ">" + (free ? freeColor : mainColor) +
translateAllColorCodes(portal.getDestinationName()) + nameColor + "<");
} else {
setLine(sign, signLineIndex, highlightColor + ">" + mainColor + portal.getDestinationName() +
highlightColor + "<");
setLine(signData, signLineIndex, highlightColor + ">" + mainColor +
translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<");
}
}
/**
* Sets a line on a sign, adding the chosen sign color
*
* @param sign <p>The sign to update</p>
* @param index <p>The index of the sign line to change</p>
* @param text <p>The new text on the sign</p>
* @param signData <p>All necessary sign information</p>
* @param index <p>The index of the sign line to change</p>
* @param text <p>The new text on the sign</p>
*/
public void setLine(Sign sign, int index, String text) {
sign.setLine(index, mainColor + text);
public void setLine(SignData signData, int index, String text) {
ChatColor mainColor = signData.getMainSignColor();
signData.getSign().setLine(index, mainColor + text);
}
/**
* Draws one network destination on one sign line
*
* @param freeGatesGreen <p>Whether to display free gates in a green color</p>
* @param sign <p>The sign to draw on</p>
* @param signData <p>All necessary sign information</p>
* @param freeGatesColored <p>Whether to display free gates in a different color</p>
* @param signLineIndex <p>The line to draw on</p>
* @param destinationIndex <p>The index of the destination to draw</p>
*/
private void drawNetworkSignLine(boolean freeGatesGreen, Sign sign, int signLineIndex, int destinationIndex) {
private void drawNetworkSignLine(SignData signData, boolean freeGatesColored, int signLineIndex, int destinationIndex) {
ChatColor mainColor = signData.getMainSignColor();
PortalActivator destinations = portal.getPortalActivator();
String destinationName = destinations.getDestinations().get(destinationIndex);
if (freeGatesGreen) {
if (freeGatesColored) {
Portal destination = PortalHandler.getByName(destinationName, portal.getNetwork());
boolean green = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination);
setLine(sign, signLineIndex, (green ? freeColor : mainColor) + destinationName);
boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination);
setLine(signData, signLineIndex, (free ? freeColor : mainColor) + translateAllColorCodes(destinationName));
} else {
setLine(sign, signLineIndex, mainColor + destinationName);
setLine(signData, signLineIndex, mainColor + translateAllColorCodes(destinationName));
}
}
/**
* Draws a bungee sign
* Draws the sign of a BungeeCord portal
*
* @param sign <p>The sign to re-draw</p>
* @param signData <p>All necessary sign information</p>
*/
private void drawBungeeSign(Sign sign) {
setLine(sign, 1, Stargate.getString("bungeeSign"));
setLine(sign, 2, highlightColor + ">" + mainColor + portal.getDestinationName() + highlightColor + "<");
setLine(sign, 3, highlightColor + "[" + mainColor + portal.getNetwork() + highlightColor + "]");
private void drawBungeeSign(SignData signData) {
ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor();
setLine(signData, 1, Stargate.getString("bungeeSign"));
setLine(signData, 2, highlightColor + ">" + mainColor + translateAllColorCodes(portal.getDestinationName()) +
highlightColor + "<");
setLine(signData, 3, highlightColor + "[" + mainColor + translateAllColorCodes(portal.getNetwork()) +
highlightColor + "]");
}
/**
* Draws an inactive sign
* Draws the sign of an in-active portal
*
* @param sign <p>The sign to re-draw</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>
*/
private void drawInactiveSign(Sign sign) {
setLine(sign, 1, Stargate.getString("signRightClick"));
setLine(sign, 2, Stargate.getString("signToUse"));
private void drawInactiveSign(SignData signData) {
ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor();
setLine(signData, 1, Stargate.getString("signRightClick"));
setLine(signData, 2, Stargate.getString("signToUse"));
if (!portal.getOptions().isNoNetwork()) {
setLine(sign, 3, highlightColor + "(" + mainColor + portal.getNetwork() + highlightColor + ")");
setLine(signData, 3, highlightColor + "(" + mainColor + translateAllColorCodes(portal.getNetwork()) +
highlightColor + ")");
} else {
setLine(sign, 3, "");
setLine(signData, 3, "");
}
}
/**
* Draws a sign pointing to a fixed location
*
* @param sign <p>The sign to re-draw</p>
* @param signData <p>All necessary sign information</p>
*/
private void drawFixedSign(Sign sign) {
private void drawFixedSign(SignData signData) {
ChatColor highlightColor = signData.getHighlightSignColor();
ChatColor mainColor = signData.getMainSignColor();
Portal destinationPortal = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()),
portal.getCleanNetwork());
String destinationName = portal.getOptions().isRandom() ? Stargate.getString("signRandom") :
portal.getDestinationName();
setLine(sign, 1, highlightColor + ">" + mainColor + destinationName + highlightColor + "<");
(destinationPortal != null ? destinationPortal.getName() : portal.getDestinationName());
setLine(signData, 1, highlightColor + ">" + mainColor + translateAllColorCodes(destinationName) +
highlightColor + "<");
if (portal.getOptions().isNoNetwork()) {
setLine(sign, 2, "");
setLine(signData, 2, "");
} else {
setLine(sign, 2, highlightColor + "(" + mainColor + portal.getNetwork() + highlightColor + ")");
setLine(signData, 2, highlightColor + "(" + mainColor +
translateAllColorCodes(portal.getNetwork()) + highlightColor + ")");
}
Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork());
Portal destination = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()),
portal.getNetwork());
if (destination == null && !portal.getOptions().isRandom()) {
setLine(sign, 3, errorColor + Stargate.getString("signDisconnected"));
setLine(signData, 3, errorColor + Stargate.getString("signDisconnected"));
} else {
setLine(sign, 3, "");
setLine(signData, 3, "");
}
}
@ -230,11 +353,44 @@ public class PortalSignDrawer {
* @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) {
Sign sign = (Sign) portalLocation.getSignLocation().getBlock().getState();
BlockState blockState = portalLocation.getSignLocation().getBlock().getState();
if (!(blockState instanceof Sign sign)) {
return;
}
sign.setLine(3, errorColor + Stargate.getString("signInvalidGate"));
sign.update();
Stargate.logInfo(String.format("Gate layout on line %d does not exist [%s]", lineIndex, gateName));
}
/**
* Gets the main color to use for the given sign type
*
* @param signType <p>The sign type to get the main color for</p>
* @return <p>The main color for the given sign type</p>
*/
private ChatColor getMainColor(Material signType) {
ChatColor signColor = perSignMainColors.get(signType);
if (signColor == null) {
return mainColor;
} else {
return signColor;
}
}
/**
* Gets the highlight color to use for the given sign type
*
* @param signType <p>The sign type to get the highlight color for</p>
* @return <p>The highlight color for the given sign type</p>
*/
private ChatColor getHighlightColor(Material signType) {
ChatColor signColor = perSignHighlightColors.get(signType);
if (signColor == null) {
return highlightColor;
} else {
return signColor;
}
}
}

View File

@ -1,189 +0,0 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.utility.DirectionHelper;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Vehicle;
import org.bukkit.util.Vector;
import java.util.List;
/**
* The portal teleporter takes care of the actual portal teleportation for any vehicles
*/
public class VehicleTeleporter extends Teleporter {
private final Vehicle teleportingVehicle;
/**
* Instantiates a new vehicle teleporter
*
* @param portal <p>The portal which is the target of the teleportation</p>
* @param teleportingVehicle <p>The teleporting vehicle</p>
*/
public VehicleTeleporter(Portal portal, Vehicle teleportingVehicle) {
super(portal);
this.teleportingVehicle = teleportingVehicle;
}
/**
* Teleports a vehicle to this teleporter's portal
*
* <p>It is assumed that if a vehicle contains any players, their permissions have already been validated before
* calling this method.</p>
*
* @param origin <p>The portal the vehicle teleports from</p>
*/
public void teleport(Portal origin) {
Location traveller = teleportingVehicle.getLocation();
Location exit = getExit(teleportingVehicle, traveller);
double velocity = teleportingVehicle.getVelocity().length();
//Stop and teleport
teleportingVehicle.setVelocity(new Vector());
//Get new velocity
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
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
if (!origin.equals(portal)) {
exit = triggerEntityPortalEvent(origin, exit);
if (exit == null) {
return;
}
}
//Teleport the vehicle
teleportVehicle(exit, newVelocity);
}
/**
* Teleports a vehicle with any passengers to the given location
*
* @param exit <p>The location the vehicle should be teleported to</p>
* @param newVelocity <p>The velocity to give the vehicle right after teleportation</p>
*/
private void teleportVehicle(Location exit, Vector newVelocity) {
//Load chunks to make sure not to teleport to the void
loadChunks();
List<Entity> passengers = teleportingVehicle.getPassengers();
if (!passengers.isEmpty()) {
if (!(teleportingVehicle instanceof LivingEntity)) {
//Teleport a normal vehicle with passengers (minecart or boat)
putPassengersInNewVehicle(passengers, exit, newVelocity);
} else {
//Teleport a living vehicle with passengers (pig, horse, donkey, strider)
teleportLivingVehicle(exit, passengers);
}
} else {
//Teleport an empty vehicle
teleportingVehicle.teleport(exit);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(),
() -> teleportingVehicle.setVelocity(newVelocity), 1);
}
}
/**
* 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 vehicle to</p>
* @return <p>The location the vehicle should be teleported to, or null if the event was cancelled</p>
*/
private Location triggerEntityPortalEvent(Portal origin, Location exit) {
StargateEntityPortalEvent stargateEntityPortalEvent = new StargateEntityPortalEvent(teleportingVehicle, 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 VehicleTeleporter(origin, teleportingVehicle).teleport(origin);
return null;
}
return stargateEntityPortalEvent.getExit();
}
/**
* 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>
*/
private void teleportLivingVehicle(Location exit, List<Entity> passengers) {
teleportingVehicle.eject();
teleportingVehicle.teleport(exit);
handleVehiclePassengers(passengers, teleportingVehicle, 2);
}
/**
* Creates a new vehicle equal to the player's previous vehicle and puts any passengers inside
*
* <p>While it is possible to teleport boats and minecarts using the same methods as "teleportLivingVehicle", this
* method works better with CraftBook with minecart options enabled. Using normal teleportation, CraftBook destroys
* the minecart once the player is ejected, causing the minecart to disappear and the player to teleport without it.</p>
*
* @param passengers <p>A list of all passengers in the vehicle</p>
* @param exit <p>The exit location to spawn the new vehicle on</p>
* @param newVelocity <p>The new velocity of the new vehicle</p>
*/
private void putPassengersInNewVehicle(List<Entity> passengers, Location exit,
Vector newVelocity) {
World vehicleWorld = exit.getWorld();
if (vehicleWorld == null) {
Stargate.logWarning("Unable to get the world to teleport the vehicle to");
return;
}
//Spawn a new vehicle
Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass());
//Remove the old vehicle
teleportingVehicle.eject();
teleportingVehicle.remove();
//Set rotation, add passengers and restore velocity
newVehicle.setRotation(exit.getYaw(), exit.getPitch());
handleVehiclePassengers(passengers, newVehicle, 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>
*/
private void handleVehiclePassengers(List<Entity> passengers, Vehicle vehicle, long delay) {
for (Entity passenger : passengers) {
passenger.eject();
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> teleportAndAddPassenger(vehicle, passenger),
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>
*/
private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger) {
if (!passenger.teleport(targetVehicle.getLocation())) {
Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger");
}
if (!targetVehicle.addPassenger(passenger)) {
Stargate.debug("handleVehiclePassengers", "Failed to add passenger");
}
}
}

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property;
/**
* Each enum value represents one option a portal can have/use
@ -48,7 +48,17 @@ public enum PortalOption {
/**
* This option allows a portal to teleport to another server connected through BungeeCord
*/
BUNGEE('u', "stargate.admin.bungee", 20);
BUNGEE('u', "stargate.admin.bungee", 20),
/**
* This option allows a portal which does not display a teleportation message, for better immersion
*/
SILENT('i', "stargate.option.silent", 21),
/**
* This option causes a fixed portal's sign to be removed after creation
*/
NO_SIGN('e', "stargate.option.nosign", 22);
private final char characterRepresentation;
private final String permissionString;

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.Stargate;
@ -25,12 +25,17 @@ public class PortalOptions {
if (this.isAlwaysOn() && !isFixed) {
this.options.put(PortalOption.ALWAYS_ON, false);
Stargate.debug("Portal", "Can not create a non-fixed always-on gate. Setting AlwaysOn = false");
Stargate.debug("PortalOptions", "Can not create a non-fixed always-on gate. Setting AlwaysOn = false");
}
if (this.isRandom() && !this.isAlwaysOn()) {
if ((this.isRandom() || this.isBungee()) && !this.isAlwaysOn()) {
this.options.put(PortalOption.ALWAYS_ON, true);
Stargate.debug("Portal", "Gate marked as random, set to always-on");
Stargate.debug("PortalOptions", "Gate marked as random or bungee, set to always-on");
}
if (this.hasNoSign() && !this.isFixed) {
this.options.put(PortalOption.NO_SIGN, false);
Stargate.debug("PortalOptions", "Gate marked with no sign, but not fixed. Setting NoSign = false");
}
}
@ -165,4 +170,27 @@ public class PortalOptions {
return this.options.get(PortalOption.BUNGEE);
}
/**
* Gets whether this portal is silent
*
* <p>A silent 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>
*
* @return <p>Whether this portal is silent</p>
*/
public boolean isSilent() {
return this.options.get(PortalOption.SILENT);
}
/**
* Gets whether this portal has no sign
*
* <p>An always-on portal is allowed to not have a sign as it will never be interacted with anyway.</p>
*
* @return <p>Whether this portal has no sign</p>
*/
public boolean hasNoSign() {
return this.options.get(PortalOption.NO_SIGN);
}
}

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property;
import net.knarcraft.stargate.Stargate;
import org.bukkit.Bukkit;
@ -43,6 +43,22 @@ public class PortalOwner {
return ownerUUID;
}
/**
* Sets the unique id for a portal owner without one
*
* <p>This method is only meant to be used to set the unique id for an owner without one. If the owner already has
* an unique id, an exception will be thrown.</p>
*
* @param uniqueId <p>The new unique id for the portal owner</p>
*/
public void setUUID(UUID uniqueId) {
if (ownerUUID == null) {
ownerUUID = uniqueId;
} else {
throw new IllegalArgumentException("An existing UUID cannot be overwritten.");
}
}
/**
* Gets the name of this owner
*

View File

@ -1,8 +1,10 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property;
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.property.gate.Gate;
/**
* The portal structure is responsible for the physical properties of a portal

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
@ -69,6 +69,15 @@ public class Gate {
return layout;
}
/**
* Gets a copy of the character to material mapping for this gate
*
* @return <p>The character to material map</p>
*/
public Map<Character, Material> getCharacterMaterialMap() {
return new HashMap<>(characterMaterialMap);
}
/**
* Gets the material type used for this gate's control blocks
*
@ -226,7 +235,7 @@ public class Gate {
Material type = topLeft.getRelativeLocation(entranceVector, yaw).getType();
//Ignore entrance if it's air or water, and we're creating a new gate
if (onCreate && (type == Material.AIR || type == Material.WATER)) {
if (onCreate && (type.isAir() || type == Material.WATER)) {
continue;
}

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.GateReader;
@ -188,16 +188,35 @@ public class GateHandler {
* @return <p>True if the gate is valid. False otherwise</p>
*/
private static boolean validateGate(Gate gate, String fileName) {
String failString = String.format("Could not load Gate %s", fileName) + " - %s";
if (gate.getLayout().getControls().length != 2) {
Stargate.logSevere(String.format("Could not load Gate %s - Gates must have exactly 2 control points.",
fileName));
Stargate.logSevere(String.format(failString, "Gates must have exactly 2 control points."));
return false;
}
if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) {
Stargate.logSevere(String.format("Could not load Gate %s - Gate button must be a type of button.", fileName));
Stargate.logSevere(String.format(failString, "Gate button must be a type of button."));
return false;
}
if (!gate.getPortalOpenBlock().isBlock()) {
Stargate.logSevere(String.format(failString, "Gate open block must be a type of block."));
return false;
}
if (!gate.getPortalClosedBlock().isBlock()) {
Stargate.logSevere(String.format(failString, "Gate closed block must be a type of block."));
return false;
}
for (Material material : gate.getCharacterMaterialMap().values()) {
if (!material.isBlock()) {
Stargate.logSevere(String.format(failString, "Every gate border block must be a type of block."));
return false;
}
}
return true;
}
@ -243,6 +262,7 @@ public class GateHandler {
loadGateFromJar("nethergate.gate", gateFolder);
loadGateFromJar("watergate.gate", gateFolder);
loadGateFromJar("endgate.gate", gateFolder);
loadGateFromJar("squarenetherglowstonegate.gate", gateFolder);
}
/**

View File

@ -1,4 +1,4 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.property.gate;
import net.knarcraft.stargate.container.RelativeBlockVector;

View File

@ -0,0 +1,34 @@
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Entity;
/**
* The portal teleporter takes care of the actual portal teleportation for any entities
*/
public class EntityTeleporter extends Teleporter {
private final Entity teleportingEntity;
/**
* Instantiates a new portal teleporter
*
* @param targetPortal <p>The portal which is the target of the teleportation</p>
*/
public EntityTeleporter(Portal targetPortal, Entity teleportingEntity) {
super(targetPortal, teleportingEntity);
this.teleportingEntity = teleportingEntity;
}
/**
* Teleports an entity to this teleporter's portal
*
* @param origin <p>The portal the entity is teleporting from</p>
* @return <p>True if the entity was teleported. False otherwise</p>
*/
public boolean teleportEntity(Portal origin) {
return teleport(origin, new StargateEntityPortalEvent(teleportingEntity, origin, portal, exit));
}
}

View File

@ -0,0 +1,78 @@
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargatePlayerPortalEvent;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
import java.util.List;
/**
* The portal teleporter takes care of the actual portal teleportation for any players
*/
public class PlayerTeleporter extends Teleporter {
private final Player player;
/**
* Instantiates a new player teleporter
*
* @param targetPortal <p>The portal which is the target of the teleportation</p>
* @param player <p>The teleporting player</p>
*/
public PlayerTeleporter(Portal targetPortal, Player player) {
super(targetPortal, player);
this.player = player;
}
/**
* Teleports a player to this teleporter's portal
*
* @param origin <p>The portal the player teleports from</p>
* @param event <p>The player move event triggering the event</p>
*/
public void teleportPlayer(Portal origin, PlayerMoveEvent event) {
double velocity = player.getVelocity().length();
List<Entity> passengers = player.getPassengers();
//Call the StargatePlayerPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
exit = triggerPortalEvent(origin, new StargatePlayerPortalEvent(player, origin, portal, exit));
if (exit == null) {
return;
}
}
//Calculate the exit velocity of the player
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity * Stargate.getGateConfig().getExitVelocity());
//Load chunks to make sure not to teleport to the void
loadChunks();
//Teleport any creatures leashed by the player in a 15-block range
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 (event == null) {
player.teleport(exit);
} else {
//Set the exit location of the event
event.setTo(exit);
}
//Set the velocity of the teleported player after the teleportation is finished
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> player.setVelocity(newVelocity), 1);
}
}

View File

@ -1,11 +1,14 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.event.StargateTeleportEvent;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
@ -14,7 +17,9 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Slab;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
@ -28,71 +33,114 @@ public abstract class Teleporter {
* The portal the entity is teleporting to
*/
protected final Portal portal;
/**
* The scheduler to use for delaying tasks
*/
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
*
* @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) {
public Teleporter(Portal portal, Entity teleportedEntity) {
this.portal = portal;
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(Portal origin, StargateTeleportEvent stargateTeleportEvent) {
List<Entity> passengers = teleportedEntity.getPassengers();
//Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
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>
*/
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>
*/
protected Location triggerPortalEvent(Portal origin, 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
*
* @param exit <p>The location the entity will exit from</p>
*/
protected void adjustRotation(Location exit) {
protected void adjustExitLocationRotation(Location exit) {
int adjust = 0;
if (portal.getOptions().isBackwards()) {
adjust = 180;
}
float newYaw = (portal.getYaw() + adjust) % 360;
Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw);
exit.setYaw(newYaw);
exit.setDirection(DirectionHelper.getDirectionVectorFromYaw(newYaw));
}
/**
* 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>
* @param traveller <p>The location of the entity travelling</p>
* @return <p>The location the entity should be teleported to.</p>
* Loads the chunks outside the portal's entrance
*/
public Location getExit(Entity entity, Location traveller) {
Location exitLocation = null;
RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit();
if (relativeExit != null) {
BlockLocation exit = portal.getBlockAt(relativeExit);
//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()));
protected void loadChunks() {
for (Chunk chunk : getChunksToLoad()) {
chunk.addPluginChunkTicket(Stargate.getInstance());
//Allow the chunk to unload after 10 seconds
Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L));
}
//Adjust pitch and height
return adjustExitLocation(traveller, exitLocation);
}
/**
@ -138,7 +186,7 @@ public abstract class Teleporter {
if (entitySize > 1) {
double entityOffset;
if (portal.getOptions().isAlwaysOn()) {
entityOffset = entityBoxSize / 2D;
entityOffset = (entityBoxSize / 2D);
} else {
entityOffset = (entitySize / 2D) - 1;
}
@ -182,41 +230,62 @@ public abstract class Teleporter {
* 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>
*
* @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>
* @return <p>The location the travelling entity should be teleported to</p>
*/
private Location adjustExitLocation(Location traveller, Location exitLocation) {
private Location adjustExitLocationHeight(Entity entity, Location exitLocation) {
if (exitLocation != null) {
BlockData blockData = portal.getWorld().getBlockAt(exitLocation).getBlockData();
if ((blockData instanceof Bisected && ((Bisected) blockData).getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab) && ((Slab) blockData).getType() == Slab.Type.BOTTOM) {
//Prevent traveller from spawning inside a slab
Stargate.debug("adjustExitLocation", "Added a block to get above a slab");
exitLocation.add(0, 1, 0);
} 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");
BlockData blockData = exitLocation.getBlock().getBlockData();
if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM) ||
blockData.getMaterial() == Material.WATER) {
//Prevent traveller from spawning inside a slab, or a boat from spawning inside water
Stargate.debug("adjustExitLocation", "Added a block to get above a slab or a block of water");
exitLocation.add(0, 1, 0);
}
exitLocation.setPitch(traveller.getPitch());
return exitLocation;
} else {
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() {
for (Chunk chunk : getChunksToLoad()) {
chunk.addPluginChunkTicket(Stargate.getInstance());
//Allow the chunk to unload after 3 seconds
Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 3000L));
private Location getExit(Entity entity) {
Location exitLocation = null;
RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit();
if (relativeExit != null) {
BlockLocation exit = portal.getBlockAt(relativeExit);
//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 height and rotation
Location adjusted = adjustExitLocationHeight(entity, exitLocation);
adjustExitLocationRotation(adjusted);
return adjusted;
}
/**

View File

@ -0,0 +1,183 @@
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.util.Vector;
import java.util.List;
/**
* The portal teleporter takes care of the actual portal teleportation for any vehicles
*/
public class VehicleTeleporter extends EntityTeleporter {
private final Vehicle teleportingVehicle;
/**
* Instantiates a new vehicle teleporter
*
* @param targetPortal <p>The targetPortal which is the target of the teleportation</p>
* @param teleportingVehicle <p>The teleporting vehicle</p>
*/
public VehicleTeleporter(Portal targetPortal, Vehicle teleportingVehicle) {
super(targetPortal, teleportingVehicle);
this.teleportingVehicle = teleportingVehicle;
}
/**
* Teleports a vehicle to this teleporter's portal
*
* <p>It is assumed that if a vehicle contains any players, their permissions have already been validated before
* calling this method.</p>
*
* @param origin <p>The portal the vehicle is teleporting from</p>
* @return <p>True if the vehicle was teleported. False otherwise</p>
*/
@Override
public boolean teleportEntity(Portal origin) {
Stargate.debug("VehicleTeleporter::teleport", "Preparing to teleport: " + teleportingVehicle);
double velocity = teleportingVehicle.getVelocity().length();
//Stop the vehicle before teleporting
teleportingVehicle.setVelocity(new Vector());
//Get new velocity
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity);
//Call the StargateEntityPortalEvent to allow plugins to change destination
exit = triggerPortalEvent(origin, new StargateEntityPortalEvent(teleportingVehicle, origin, portal, exit));
if (exit == null) {
return false;
}
//Teleport the vehicle
return teleportVehicle(exit, newVelocity, origin);
}
/**
* Teleports a vehicle with any passengers to the given location
*
* @param exit <p>The location the vehicle should be teleported to</p>
* @param newVelocity <p>The velocity to give the vehicle right after teleportation</p>
* @param origin <p>The portal the vehicle teleported from</p>
* @return <p>True if the vehicle was teleported. False otherwise</p>
*/
private boolean teleportVehicle(Location exit, Vector newVelocity, Portal origin) {
//Load chunks to make sure not to teleport to the void
loadChunks();
List<Entity> passengers = teleportingVehicle.getPassengers();
if (!passengers.isEmpty()) {
//Check if the passengers are allowed according to current config settings
if (!vehiclePassengersAllowed(passengers)) {
return false;
}
if (!(teleportingVehicle instanceof LivingEntity) &&
Stargate.getGateConfig().enableCraftBookRemoveOnEjectFix()) {
//Teleport a normal vehicle with passengers (minecart or boat)
putPassengersInNewVehicle(passengers, exit, newVelocity, origin);
} else {
//Teleport a living vehicle with passengers (pig, horse, donkey, strider)
teleportVehicle(passengers, exit, newVelocity, origin);
}
} else {
//Check if teleportation of empty vehicles is enabled
if (!Stargate.getGateConfig().handleEmptyVehicles()) {
return false;
}
//Teleport an empty vehicle
teleportingVehicle.teleport(exit);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(),
() -> teleportingVehicle.setVelocity(newVelocity), 1);
}
return true;
}
/**
* Checks whether current config values allow the teleportation of the given passengers
*
* @param passengers <p>The passengers to teleport</p>
* @return <p>True if the passengers are allowed to teleport</p>
*/
private boolean vehiclePassengersAllowed(List<Entity> passengers) {
StargateGateConfig config = Stargate.getGateConfig();
//Don't teleport if the vehicle contains a creature and creature transportation is disabled
if (TeleportHelper.containsNonPlayer(passengers) && !config.handleCreatureTransportation()) {
return false;
}
//Don't teleport if the player does not contain a player and non-player vehicles is disabled
return TeleportHelper.containsPlayer(passengers) || config.handleNonPlayerVehicles();
}
/**
* Teleport a vehicle which is not a minecart or a boat
*
* @param passengers <p>The passengers of the vehicle</p>
* @param exit <p>The location the vehicle will exit</p>
* @param newVelocity <p>The new velocity of the teleported vehicle</p>
* @param origin <p>The portal the vehicle teleported from</p>
*/
private void teleportVehicle(List<Entity> passengers, Location exit, Vector newVelocity, Portal origin) {
if (teleportingVehicle.eject()) {
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);
}
/**
* Creates a new vehicle equal to the player's previous vehicle and puts any passengers inside
*
* <p>While it is possible to teleport boats and minecarts using the same methods as "teleportLivingVehicle", this
* method works better with CraftBook with minecart options enabled. Using normal teleportation, CraftBook destroys
* the minecart once the player is ejected, causing the minecart to disappear and the player to teleport without it.</p>
*
* @param passengers <p>A list of all passengers in the vehicle</p>
* @param exit <p>The exit location to spawn the new vehicle on</p>
* @param newVelocity <p>The new velocity of the new vehicle</p>
* @param origin <p>The portal the vehicle teleported from</p>
*/
private void putPassengersInNewVehicle(List<Entity> passengers, Location exit,
Vector newVelocity, Portal origin) {
World vehicleWorld = exit.getWorld();
if (vehicleWorld == null) {
Stargate.logWarning("Unable to get the world to teleport the vehicle to");
return;
}
//Spawn a new vehicle
Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass());
if (teleportingVehicle instanceof Boat boat) {
((Boat) newVehicle).setWoodType(boat.getWoodType());
}
//Remove the old vehicle
if (teleportingVehicle.eject()) {
TeleportHelper.handleEntityPassengers(passengers, newVehicle, origin, portal, exit.getDirection(),
newVelocity);
}
teleportingVehicle.remove();
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1);
}
}

View File

@ -21,32 +21,36 @@ public class BlockChangeThread implements Runnable {
long sTime = System.nanoTime();
//Repeat for at most 0.025 seconds
while (System.nanoTime() - sTime < 25000000) {
pollQueue();
if (pollQueue()) {
break;
}
}
}
/**
* 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
BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll();
if (blockChangeRequest == null) {
return;
return true;
}
//Change the material of the pulled block
Block block = blockChangeRequest.getBlockLocation().getBlock();
block.setType(blockChangeRequest.getMaterial(), false);
if (blockChangeRequest.getMaterial() == Material.END_GATEWAY &&
block.getWorld().getEnvironment() == World.Environment.THE_END) {
if (blockChangeRequest.getMaterial() == Material.END_GATEWAY) {
//Force a specific location to prevent exit gateway generation
fixEndGatewayGate(block);
} else if (blockChangeRequest.getAxis() != null) {
//If orientation is relevant, adjust the block's orientation
orientBlock(block, blockChangeRequest.getAxis());
}
return false;
}
/**
@ -56,8 +60,11 @@ public class BlockChangeThread implements Runnable {
*/
private static void fixEndGatewayGate(Block block) {
EndGateway gateway = (EndGateway) block.getState();
gateway.setExitLocation(block.getLocation());
gateway.setExactTeleport(true);
gateway.setAge(Long.MIN_VALUE);
if (block.getWorld().getEnvironment() == World.Environment.THE_END) {
gateway.setExitLocation(block.getLocation());
gateway.setExactTeleport(true);
}
gateway.update(false, false);
}

View File

@ -30,8 +30,7 @@ public class StarGateThread implements Runnable {
for (Portal portal : openPortalsQueue) {
//Skip always open and non-open gates
if (portal.getOptions().isAlwaysOn() || portal.getOptions().isRandom() || portal.getOptions().isBungee() ||
!portal.isOpen()) {
if (portal.getOptions().isAlwaysOn() || !portal.isOpen()) {
continue;
}
if (time > portal.getTriggeredTime() + Stargate.getGateConfig().getOpenTime()) {

View File

@ -1,10 +1,12 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.PlayerTeleporter;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -70,7 +72,7 @@ public final class BungeeHelper {
//Build the message data and send it over the SGBungee BungeeCord channel
dataOutputStream.writeUTF("Forward");
//Send the message to the server defined in the entrance portal's network line
dataOutputStream.writeUTF(entrancePortal.getNetwork());
dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork()));
//Specify the sub-channel/tag to make it recognizable on arrival
dataOutputStream.writeUTF(bungeeSubChannel);
//Write the length of the message
@ -101,7 +103,7 @@ public final class BungeeHelper {
//Send a connect-message to connect the player to the server defined in the entrance portal's network line
dataOutputStream.writeUTF("Connect");
dataOutputStream.writeUTF(entrancePortal.getNetwork());
dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork()));
//Send the plugin message
player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray());
@ -169,4 +171,51 @@ public final class BungeeHelper {
}
}
/**
* Teleports a player to a bungee gate
*
* @param player <p>The player to teleport</p>
* @param entrancePortal <p>The gate the player is entering from</p>
* @param event <p>The event causing the teleportation</p>
* @return <p>True if the teleportation was successful</p>
*/
public static boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) {
//Check if bungee is actually enabled
if (!Stargate.getGateConfig().enableBungee()) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Teleport the player back to this gate, for sanity's sake
new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
//Send the SGBungee packet first, it will be queued by BC if required
if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to send teleportation message");
return false;
}
//Send the connect-message to make the player change server
if (!BungeeHelper.changeServer(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to change server");
return false;
}
Stargate.debug("bungeeTeleport", "Teleported player to another server");
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

@ -0,0 +1,54 @@
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;
/**
* A helper class for dealing with colors
*/
public final class ColorHelper {
private 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

@ -113,16 +113,21 @@ public final class DirectionHelper {
* @return <p>A normal vector</p>
*/
public static Vector getCoordinateVectorFromRelativeVector(double right, double down, double out, double yaw) {
Vector distanceVector = DirectionHelper.getDirectionVectorFromYaw(yaw);
distanceVector.multiply(out);
Vector rightVector = DirectionHelper.getDirectionVectorFromYaw(yaw - 90);
rightVector.multiply(right);
Vector depthVector = new Vector(0, -1, 0);
depthVector.multiply(down);
return distanceVector.add(rightVector).add(depthVector);
if (yaw == 0) {
//South
return new Vector(right, -down, out);
} else if (yaw == 90) {
//West
return new Vector(-out, -down, right);
} else if (yaw == 180) {
//North
return new Vector(-right, -down, -out);
} else if (yaw == 270) {
//East
return new Vector(out, -down, -right);
} else {
throw new IllegalArgumentException(String.format("Invalid yaw %f given", yaw));
}
}
/**

View File

@ -1,8 +1,11 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.EconomyConfig;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalOwner;
import net.knarcraft.stargate.portal.property.PortalOwner;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.UUID;
@ -34,9 +37,9 @@ public final class EconomyHelper {
"was therefore not possible. Make the owner re-create the portal to fix this.", entrancePortal));
}
if (entrancePortal.getGate().getToOwner() && ownerUUID != null) {
success = Stargate.getEconomyConfig().chargePlayerIfNecessary(player, ownerUUID, cost);
success = chargePlayerIfNecessary(player, ownerUUID, cost);
} else {
success = Stargate.getEconomyConfig().chargePlayerIfNecessary(player, cost);
success = chargePlayerIfNecessary(player, cost);
}
//Send the insufficient funds message
@ -118,6 +121,116 @@ public final class EconomyHelper {
Stargate.getMessageSender().sendSuccessMessage(player, refundMsg);
}
/**
* Determines the cost of using a gate
*
* @param player <p>The player trying to use the gate</p>
* @param source <p>The source/entry portal</p>
* @param destination <p>The destination portal</p>
* @return <p>The cost of using the portal</p>
*/
public static int getUseCost(Player player, Portal source, Portal destination) {
EconomyConfig config = Stargate.getEconomyConfig();
//No payment required
if (!config.useEconomy() || source.getOptions().isFree()) {
return 0;
}
//Not charging for free destinations
if (destination != null && config.freeIfFreeDestination() && destination.getOptions().isFree()) {
return 0;
}
//Cost is 0 if the player owns this gate and funds go to the owner
if (source.getGate().getToOwner() && source.isOwner(player)) {
return 0;
}
//Player gets free gate use
if (PermissionHelper.hasPermission(player, "stargate.free.use")) {
return 0;
}
return source.getGate().getUseCost();
}
/**
* Charges the player for an action, if required
*
* @param player <p>The player to take money from</p>
* @param target <p>The target to pay</p>
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p>
*/
public static boolean chargePlayerIfNecessary(Player player, UUID target, int cost) {
if (skipPayment(cost)) {
return true;
}
//Charge player
return chargePlayer(player, target, cost);
}
/**
* Charges a player
*
* @param player <p>The player 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>
*/
private static boolean chargePlayer(Player player, double amount) {
Economy economy = Stargate.getEconomyConfig().getEconomy();
if (Stargate.getEconomyConfig().isEconomyEnabled() && economy != null) {
if (!economy.has(player, amount)) {
return false;
}
economy.withdrawPlayer(player, amount);
}
return true;
}
/**
* Charges the player for an action, if required
*
* @param player <p>The player to take money from</p>
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the player was charged successfully</p>
*/
public static boolean chargePlayerIfNecessary(Player player, int cost) {
if (skipPayment(cost)) {
return true;
}
//Charge player
return chargePlayer(player, cost);
}
/**
* Checks whether a payment transaction should be skipped
*
* @param cost <p>The cost of the transaction</p>
* @return <p>True if the transaction should be skipped</p>
*/
private static boolean skipPayment(int cost) {
return cost == 0 || !Stargate.getEconomyConfig().useEconomy();
}
/**
* Charges a player, giving the charge to a target
*
* @param player <p>The player to charge</p>
* @param target <p>The UUID of the player to pay</p>
* @param amount <p>The amount to charge</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) {
Economy economy = Stargate.getEconomyConfig().getEconomy();
if (Stargate.getEconomyConfig().isEconomyEnabled() && player.getUniqueId().compareTo(target) != 0 && economy != null) {
if (!economy.has(player, amount)) {
return false;
}
//Take money from the user and give to the owner
economy.withdrawPlayer(player, amount);
economy.depositPlayer(Bukkit.getOfflinePlayer(target), amount);
}
return true;
}
/**
* Replaces the cost and portal variables in a string
*

View File

@ -1,6 +1,6 @@
package net.knarcraft.stargate.utility;
import org.bukkit.ChatColor;
import net.md_5.bungee.api.ChatColor;
import java.io.BufferedReader;
import java.io.BufferedWriter;

View File

@ -28,6 +28,17 @@ public final class MaterialHelper {
material.equals(Material.DEAD_TUBE_CORAL_WALL_FAN);
}
/**
* Checks whether the given material is a container
*
* @param material <p>The material to check</p>
* @return <p>True if the material is a container</p>
*/
public static boolean isContainer(Material material) {
return Tag.SHULKER_BOXES.isTagged(material) || material == Material.CHEST ||
material == Material.TRAPPED_CHEST || material == Material.ENDER_CHEST;
}
/**
* Checks whether the given material can be used as a button
*
@ -35,8 +46,7 @@ public final class MaterialHelper {
* @return <p>True if the material can be used as a button</p>
*/
public static boolean isButtonCompatible(Material material) {
return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || Tag.SHULKER_BOXES.isTagged(material) ||
material == Material.CHEST || material == Material.TRAPPED_CHEST || material == Material.ENDER_CHEST;
return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || isContainer(material);
}
}

View File

@ -2,12 +2,14 @@ package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateAccessEvent;
import net.knarcraft.stargate.portal.PlayerTeleporter;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalOption;
import net.knarcraft.stargate.portal.property.PortalOption;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
import static net.knarcraft.stargate.Stargate.getMaxNameNetworkLength;
/**
* Helper class for deciding which actions a player is allowed to perform
*/
@ -27,13 +29,15 @@ public final class PermissionHelper {
Portal destination = portal.getPortalActivator().getDestination();
//For an always open portal, no action is necessary
if (portal.getOptions().isAlwaysOn() || portal.getOptions().isRandom() || portal.getOptions().isBungee()) {
if (portal.getOptions().isAlwaysOn()) {
return;
}
//Destination is invalid or the same portal. Send an error message
if (destination == null || destination == portal) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg"));
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg"));
}
return;
}
@ -49,19 +53,25 @@ public final class PermissionHelper {
//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) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
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)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
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()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("blockMsg"));
if (!portal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("blockMsg"));
}
return;
}
@ -97,12 +107,12 @@ public final class PermissionHelper {
boolean deny = false;
if (entrancePortal.getOptions().isBungee()) {
if (!PermissionHelper.canAccessServer(player, entrancePortal.getNetwork())) {
if (!PermissionHelper.canAccessServer(player, entrancePortal.getCleanNetwork())) {
//If the portal is a bungee portal, and the player cannot access the server, deny
Stargate.debug("cannotAccessPortal", "Cannot access server");
deny = true;
}
} else if (PermissionHelper.cannotAccessNetwork(player, entrancePortal.getNetwork())) {
} else if (PermissionHelper.cannotAccessNetwork(player, entrancePortal.getCleanNetwork())) {
//If the player does not have access to the network, deny
Stargate.debug("cannotAccessPortal", "Cannot access network");
deny = true;
@ -192,8 +202,8 @@ public final class PermissionHelper {
}
//Is able to create personal gates (Assumption is made they can also access them)
String playerName = player.getName();
if (playerName.length() > 11) {
playerName = playerName.substring(0, 11);
if (playerName.length() > getMaxNameNetworkLength()) {
playerName = playerName.substring(0, getMaxNameNetworkLength());
}
return !network.equals(playerName) || !hasPermission(player, "stargate.create.personal");
}
@ -233,7 +243,7 @@ public final class PermissionHelper {
return true;
}
//Don't charge for free destinations unless specified in the config
return dest != null && !Stargate.getEconomyConfig().chargeFreeDestination() && dest.getOptions().isFree();
return dest != null && Stargate.getEconomyConfig().freeIfFreeDestination() && dest.getOptions().isFree();
}
/**
@ -337,7 +347,7 @@ public final class PermissionHelper {
* @return <p>True if the player is allowed to destroy the portal</p>
*/
public static boolean canDestroyPortal(Player player, Portal portal) {
String network = portal.getNetwork();
String network = portal.getCleanNetwork();
//Use a special check for bungee portals
if (portal.getOptions().isBungee()) {
@ -374,8 +384,10 @@ public final class PermissionHelper {
//Not open for this player
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
return true;
}
@ -386,14 +398,16 @@ public final class PermissionHelper {
//Player cannot access portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
entrancePortal.getPortalOpener().closePortal(false);
return true;
}
//Player cannot pay for teleportation
int cost = Stargate.getEconomyConfig().getUseCost(player, entrancePortal, destination);
int cost = EconomyHelper.getUseCost(player, entrancePortal, destination);
if (cost > 0) {
return EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost);
}

View File

@ -1,16 +1,25 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.portal.Gate;
import net.knarcraft.stargate.portal.GateHandler;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalLocation;
import net.knarcraft.stargate.portal.PortalOptions;
import net.knarcraft.stargate.portal.PortalOwner;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalLocation;
import net.knarcraft.stargate.portal.property.PortalOptions;
import net.knarcraft.stargate.portal.property.PortalOwner;
import net.knarcraft.stargate.portal.property.gate.Gate;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Waterlogged;
import java.io.BufferedWriter;
import java.io.File;
@ -114,7 +123,9 @@ public final class PortalFileHelper {
builder.append(options.isShown()).append(':');
builder.append(options.isNoNetwork()).append(':');
builder.append(options.isRandom()).append(':');
builder.append(options.isBungee());
builder.append(options.isBungee()).append(':');
builder.append(options.isSilent()).append(':');
builder.append(options.hasNoSign());
}
/**
@ -147,14 +158,17 @@ public final class PortalFileHelper {
int lineIndex = 0;
try {
Scanner scanner = new Scanner(database);
boolean needsToSaveDatabase = false;
while (scanner.hasNextLine()) {
//Read the line and do whatever needs to be done
readPortalLine(scanner, ++lineIndex, world);
needsToSaveDatabase = readPortalLine(scanner, ++lineIndex, world) || needsToSaveDatabase;
}
scanner.close();
//Do necessary tasks after all portals have loaded
doPostLoadTasks(world);
Stargate.debug("PortalFileHelper::loadPortals", String.format("Finished loading portals for %s. " +
"Starting post loading tasks", world));
doPostLoadTasks(world, needsToSaveDatabase);
return true;
} catch (Exception e) {
Stargate.logSevere(String.format("Exception while reading stargates from %s: %d", database.getName(),
@ -170,24 +184,25 @@ public final class PortalFileHelper {
* @param scanner <p>The scanner to read</p>
* @param lineIndex <p>The index of the read line</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>
*/
private static void readPortalLine(Scanner scanner, int lineIndex, World world) {
private static boolean readPortalLine(Scanner scanner, int lineIndex, World world) {
String line = scanner.nextLine().trim();
//Ignore empty and comment lines
if (line.startsWith("#") || line.isEmpty()) {
return;
return false;
}
//Check if the min. required portal data is present
String[] portalData = line.split(":");
if (portalData.length < 8) {
Stargate.logInfo(String.format("Invalid line - %s", lineIndex));
return;
return false;
}
//Load the portal defined in the current line
loadPortal(portalData, world, lineIndex);
return loadPortal(portalData, world, lineIndex);
}
/**
@ -196,9 +211,10 @@ public final class PortalFileHelper {
* <p>This will open always on portals, print info about loaded stargates and re-draw portal signs for loaded
* portals.</p>
*
* @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>
*/
private static void doPostLoadTasks(World world) {
private static void doPostLoadTasks(World world, boolean needsToSaveDatabase) {
//Open any always-on portals. Do this here as it should be more efficient than in the loop.
PortalHandler.verifyAllPortals();
int portalCount = PortalRegistry.getAllPortals().size();
@ -210,7 +226,15 @@ public final class PortalFileHelper {
//Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since
for (Portal portal : PortalRegistry.getAllPortals()) {
portal.drawSign();
if (portal.isRegistered()) {
portal.drawSign();
updatePortalButton(portal);
}
}
//Save the portals to disk to update with any changes
Stargate.debug("PortalFileHelper::doPostLoadTasks", String.format("Saving database for world %s", world));
if (needsToSaveDatabase) {
saveAllPortals(world);
}
}
@ -220,8 +244,9 @@ public final class PortalFileHelper {
* @param portalData <p>The array describing the portal</p>
* @param world <p>The world to create the portal in</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>
*/
private static void loadPortal(String[] portalData, World world, int lineIndex) {
private static boolean loadPortal(String[] portalData, World world, int lineIndex) {
//Load min. required portal data
String name = portalData[0];
BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null;
@ -237,12 +262,13 @@ public final class PortalFileHelper {
if (gate == null) {
//Mark the sign as invalid to reduce some player confusion
markPortalWithInvalidGate(portalLocation, portalData[7], lineIndex);
return;
return false;
}
//Load extra portal data
String destination = (portalData.length > 8) ? portalData[8] : "";
String network = (portalData.length > 9 && !portalData[9].isEmpty()) ? portalData[9] : Stargate.getDefaultNetwork();
String network = (portalData.length > 9 && !portalData[9].isEmpty()) ? portalData[9] :
Stargate.getDefaultNetwork();
String ownerString = (portalData.length > 10) ? portalData[10] : "";
//Get the owner from the owner string
@ -253,8 +279,123 @@ public final class PortalFileHelper {
PortalHandler.getPortalOptions(portalData));
//Register the portal, and close it in case it wasn't properly closed when the server stopped
boolean buttonLocationChanged = updateButtonVector(portal);
PortalHandler.registerPortal(portal);
portal.getPortalOpener().closePortal(true);
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
*
* @param location <p>The location of the button/sign to replace</p>
* @param portal <p>The portal the button/sign belongs to</p>
* @return <p>The material to use for removing the button/sign</p>
*/
public static Material decideRemovalMaterial(BlockLocation location, Portal portal) {
//Get the blocks to each side of the location
Location leftLocation = location.getRelativeLocation(-1, 0, 0, portal.getYaw());
Location rightLocation = location.getRelativeLocation(1, 0, 0, portal.getYaw());
//If the block is water or is waterlogged, assume the portal is underwater
if (isUnderwater(leftLocation) || isUnderwater(rightLocation)) {
return Material.WATER;
} else {
return Material.AIR;
}
}
/**
* Checks whether the given location is underwater
*
* <p>If the location has a water block, or a block which is waterlogged, it will be considered underwater.</p>
*
* @param location <p>The location to check</p>
* @return <p>True if the location is underwater</p>
*/
private static boolean isUnderwater(Location location) {
BlockData blockData = location.getBlock().getBlockData();
return blockData.getMaterial() == Material.WATER ||
(blockData instanceof Waterlogged waterlogged && waterlogged.isWaterlogged());
}
/**
* Updates the button vector for the given portal
*
* <p>As the button vector isn't saved, it is null when the portal is loaded. This method allows it to be
* explicitly set when necessary.</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>
*/
private static boolean updateButtonVector(Portal portal) {
for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) {
BlockLocation controlLocation = portal.getLocation().getTopLeft().getRelativeLocation(control,
portal.getYaw());
BlockLocation buttonLocation = controlLocation.getRelativeLocation(
new RelativeBlockVector(0, 0, 1), portal.getYaw());
if (!buttonLocation.equals(portal.getLocation().getSignLocation())) {
portal.getLocation().setButtonVector(control);
BlockLocation oldButtonLocation = portal.getStructure().getButton();
if (oldButtonLocation != null && !oldButtonLocation.equals(buttonLocation)) {
Stargate.addBlockChangeRequest(new BlockChangeRequest(oldButtonLocation, Material.AIR, null));
portal.getStructure().setButton(buttonLocation);
return true;
}
}
}
return false;
}
/**
* Generates a button for a portal
*
* @param portal <p>The portal to generate button for</p>
* @param buttonFacing <p>The direction the button should be facing</p>
*/
public static void generatePortalButton(Portal portal, BlockFace buttonFacing) {
//Go one block outwards to find the button's location rather than the control block's location
BlockLocation button = getButtonLocation(portal);
Directional buttonData = (Directional) Bukkit.createBlockData(portal.getGate().getPortalButton());
buttonData.setFacing(buttonFacing);
button.getBlock().setBlockData(buttonData);
portal.getStructure().setButton(button);
}
/**
* Gets the location of a portal's button
*
* @param portal <p>The portal to find the button for</p>
* @return <p>The location of the portal's button</p>
*/
private static BlockLocation getButtonLocation(Portal portal) {
BlockLocation topLeft = portal.getTopLeft();
RelativeBlockVector buttonVector = portal.getLocation().getButtonVector();
return topLeft.getRelativeLocation(buttonVector.addToVector(RelativeBlockVector.Property.OUT, 1),
portal.getYaw());
}
}

View File

@ -0,0 +1,247 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
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 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(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;
}
}
//TODO: Improve this to account for any players sitting on any of the lead creatures
//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>
*/
public 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;
}
/**
* 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(Entity targetVehicle, Entity passenger, Vector exitDirection,
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(List<Entity> passengers, Entity entity, Portal origin, Portal target,
Vector exitRotation, 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(Player player, Portal origin, 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(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(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>
*/
public static List<Player> getPlayers(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(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 TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
}
}

View File

@ -0,0 +1,115 @@
package net.knarcraft.stargate.utility;
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.portal.property.PortalOwner;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Helps migrate player names to UUID where necessary
*/
public final class UUIDMigrationHelper {
private UUIDMigrationHelper() {
}
private static Map<String, List<Portal>> playerNamesToMigrate;
/**
* Migrates the player's name to a UUID
*
* <p>If any portals are missing a UUID for their owner, and the given player is the owner of those portals, the
* given player's UUID will be used as UUID for the portals' owner.</p>
*
* @param player <p>The player to migrate</p>
*/
public static void migrateUUID(OfflinePlayer player) {
Map<String, List<Portal>> playersToMigrate = getPlayersToMigrate();
String playerName = player.getName();
//Nothing to do
if (!playersToMigrate.containsKey(playerName)) {
return;
}
Stargate.debug("UUIDMigrationHelper::migrateUUID", String.format("Migrating name to UUID for player %s",
playerName));
List<Portal> portalsOwned = playersToMigrate.get(playerName);
if (portalsOwned == null) {
return;
}
migratePortalsToUUID(portalsOwned, player.getUniqueId());
//Remove the player to prevent the migration to happen every time the player joins
playersToMigrate.remove(playerName);
}
/**
* Migrates a list of portals to use UUID instead of only player name
*
* @param portals <p>The portals to migrate</p>
* @param uniqueId <p>The unique ID of the portals' owner</p>
*/
private static void migratePortalsToUUID(List<Portal> portals, UUID uniqueId) {
Set<World> worldsToSave = new HashSet<>();
//Get the real portal from the copy and set UUID
for (Portal portalCopy : portals) {
Portal portal = PortalHandler.getByName(portalCopy.getCleanName(), portalCopy.getCleanNetwork());
if (portal != null) {
portal.getOwner().setUUID(uniqueId);
worldsToSave.add(portal.getWorld());
}
}
//Need to make sure the changes are saved
for (World world : worldsToSave) {
PortalFileHelper.saveAllPortals(world);
}
}
/**
* Gets all player names which need to be migrated to UUIDs
*
* @return <p>The player names to migrate</p>
*/
private static Map<String, List<Portal>> getPlayersToMigrate() {
//Make sure to only go through portals once
if (playerNamesToMigrate != null) {
return playerNamesToMigrate;
}
playerNamesToMigrate = new HashMap<>();
for (Portal portal : PortalRegistry.getAllPortals()) {
PortalOwner owner = portal.getOwner();
String ownerName = owner.getName();
//If a UUID is missing, add the portal to the list owned by the player
if (owner.getUUID() == null) {
List<Portal> portalList = playerNamesToMigrate.get(ownerName);
if (portalList == null) {
List<Portal> newList = new ArrayList<>();
newList.add(portal);
playerNamesToMigrate.put(ownerName, newList);
} else {
portalList.add(portal);
}
}
}
return playerNamesToMigrate;
}
}

View File

@ -0,0 +1,87 @@
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;
}
}

View File

@ -24,4 +24,5 @@ usecost=economy.useCost
toowner=economy.toOwner
chargefreedestination=economy.chargeFreeDestination
freegatesgreen=economy.freeGatesGreen
CheckUpdates=
CheckUpdates=
economy.freeGatesGreen=economy.freeGatesColored

View File

@ -1,62 +1,91 @@
# stargate Configuration File
# Main stargate config
# I----------I----------I #
# portalFolder - The folder for storing portals
# gateFolder - The folder for storing gate layouts
# defaultGateNetwork - The default gate network
# destroyedByExplosion - Whether to destroy gates with explosions (Creeper, TNT, etc.)
# maxGatesEachNetwork - The maximum number of gates allowed on a network - 0 for unlimited
# language - The language file to load for messages
# rememberDestination - Whether to remember the cursor location between uses
# handleVehicles - Whether to allow vehicles through gates
# sortNetworkDestinations - Whether to sort network lists alphabetically
# protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material)
# mainSignColor - The color used for drawing signs (Default: BLACK).
# highlightSignColor - The color used for sign markings (Default: WHITE)
# verifyPortals - Whether all the non-sign blocks are checked to match the gate layout when a stargate is loaded.
# I------------I-------------I #
# stargate economy options #
# I------------I-------------I #
# useEconomy - Whether to use an economy plugin
# createCost - The cost to create a gate
# destroyCost - The cost to destroy a gate
# useCost - The cost to use a gate
# toOwner - Whether the charge for using a gate goes to the gate's owner
# chargeFreeDestination - Whether a gate whose destination is a free gate is still charged
# freeGatesGreen - Whether a free gate in the destination list is drawn green
# I-------I-------I #
# Debug options #
# I-------I-------I #
# debug - Debug -- Only enable if you have issues, massive console output
# permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
# language - The language file to load for messages (de,en,es,fr,hu,it,nb-no,nl,nn-no,pt-br,ru,zh_cn)
language: en
# adminUpdateAlert - Whether to alert admins about new plugin updates
adminUpdateAlert: true
folders:
# portalFolder - The folder for storing portals
portalFolder: plugins/Stargate/portals/
# gateFolder - The folder for storing gate layouts
gateFolder: plugins/Stargate/gates/
gates:
# maxGatesEachNetwork - The maximum number of gates allowed on a network - 0 for unlimited
maxGatesEachNetwork: 0
# defaultGateNetwork - The default gate network
defaultGateNetwork: central
# exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity
exitVelocity: 0.1
cosmetic:
# rememberDestination - Whether to remember the cursor location between uses
rememberDestination: false
# sortNetworkDestinations - Whether to sort network lists alphabetically
sortNetworkDestinations: false
# mainSignColor - The color used for drawing signs (Default: BLACK).
mainSignColor: BLACK
# highlightSignColor - The color used for sign markings (Default: WHITE)
highlightSignColor: WHITE
perSignColors:
- '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'
integrity:
# destroyedByExplosion - Whether to destroy gates with explosions (Creeper, TNT, etc.)
destroyedByExplosion: false
# verifyPortals - Whether all the non-sign blocks are checked to match the gate layout when a stargate is loaded.
verifyPortals: false
# protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material)
protectEntrance: false
functionality:
enableBungee: false
# handleVehicles - Whether to allow vehicles through gates. This overrides other vehicle settings
handleVehicles: true
# handleEmptyVehicles - Whether to allow empty vehicles through gates (chest/hopper/tnt/furnace minecarts included)
handleEmptyVehicles: true
# handleCreatureTransportation - Whether to allow players to transport creatures by sending vehicles (minecarts, boats) through gates
handleCreatureTransportation: true
# handleNonPlayerVehicles - Whether to allow vehicles with a passenger which is not a player through gates. handleCreatureTransportation must be enabled
handleNonPlayerVehicles: true
# handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player
handleLeashedCreatures: true
# 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
enableCraftBookRemoveOnEjectFix: false
# I------------I-------------I #
# stargate economy options #
# I------------I-------------I #
economy:
# useEconomy - Whether to use an economy plugin
useEconomy: false
# createCost - The cost to create a gate
createCost: 0
# destroyCost - The cost to destroy a gate
destroyCost: 0
# useCost - The cost to use a gate
useCost: 0
# toOwner - Whether the charge for using a gate goes to the gate's owner
toOwner: false
# chargeFreeDestination - Whether a gate whose destination is a free gate is still charged
chargeFreeDestination: true
freeGatesGreen: false
# freeGatesColored - Whether a free gate in the destination list is marked with a color
freeGatesColored: false
# freeGatesColor - The color to use for marking free gates
freeGatesColor: DARK_GREEN
# I-------I-------I #
# Debug options #
# I-------I-------I #
debugging:
# debug - Debug -- Only enable if you have issues, massive console output
debug: false
permissionDebug: false
# permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
permissionDebug: false
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.
waitForPlayerAfterTeleportDelay: 6

View File

@ -0,0 +1,13 @@
portal-open=NETHER_PORTAL
portal-closed=AIR
button=OAK_BUTTON
toowner=false
X=OBSIDIAN
-=GLOWSTONE
A=GLOWSTONE
XAX
X...X
-...-
X.*.X
XAX

View File

@ -35,3 +35,9 @@ bungeeDisabled=BungeeCord support is disabled.
bungeeDeny=You do not have permission to create BungeeCord gates.
bungeeEmpty=BungeeCord gates require both a destination and network.
bungeeSign=Teleport to
portalInfoTitle=[STARGATE INFO]
portalInfoName=Name: %name%
portalInfoDestination=Destination: %destination%
portalInfoNetwork=Network: %network%
portalInfoServer=Server: %server%

View File

@ -35,3 +35,9 @@ bungeeDisabled=BungeeCord støtte er slått av.
bungeeDeny=Du har ikke tillatelse til å opprette BungeeCord porter.
bungeeEmpty=BungeeCord porter behøver bade en destinasjon og et nettverk.
bungeeSign=Teleporter til
portalInfoTitle=[STJERNEPORT INFO]
portalInfoName=Navn: %name%
portalInfoDestination=Destinasjon: %destination%
portalInfoNetwork=Nettverk: %network%
portalInfoServer=Server: %server%

View File

@ -35,3 +35,9 @@ bungeeDisabled=BungeeCord støtte er slått av.
bungeeDeny=Du har ikkje løyve til å opprette BungeeCord portar.
bungeeEmpty=BungeeCord portar treng bade ein destinasjon og eit nettverk.
bungeeSign=Teleporter til
portalInfoTitle=[STJERNEPORT INFO]
portalInfoName=Namn: %name%
portalInfoDestination=Destinasjon: %destination%
portalInfoNetwork=Nettverk: %network%
portalInfoServer=Server: %server%

View File

@ -0,0 +1,39 @@
author=YKDZ
signRightClick=右键
ecoLoadError=Vault 已加载, 但未检测到合适的经济插件
createConflict=星门与现有星门冲突
invalidMsg=无效的目的地
prefix=[星门]
ecoObtain=从星门 %portal% 收取了 %cost%
vaultLoaded=检测到 Vault v%version%
reloaded=星门插件已重载
bungeeDeny=你没有创建跨服星门的权限.
signToUse=以使用星门
signInvalidGate=未知星门
bungeeEmpty=跨服星门需要提供目的地和网络.
createMsg=星门已创建
bungeeDisabled=跨服功能已被禁用.
blockMsg=目的地被阻挡
ecoInFunds=余额不足
createNameLength=名称过短或过长.
vaultLoadError=未检测到Vault. 经济模块已禁用
denyMsg=访问被拒
ecoDeduct=花费 %cost%
signDisconnected=已取消链接
createNetDeny=你没有这个星门网络的许可
bungeeSign=传送到
portalInfoName=名称: %name%
destroyMsg=星门已被破坏
portalInfoTitle=[星门信息]
createExists=与已有星门重名
teleportMsg=已传送
createGateDeny=你没有使用这个星门结构的权限
signRandom=随机
portalInfoServer=服务器: %server%
createWorldDeny=你没有链接这个世界的权限
portalInfoDestination=目的地: %destination%
portalInfoNetwork=星门网络: %network%
destEmpty=目的地列表为空
createPersonal=在私人网络中创建星门
ecoRefund=退款 %cost%
createFull=此星门网络已满

View File

@ -1,18 +1,18 @@
name: Stargate
main: net.knarcraft.stargate.Stargate
version: 0.9.0.2
version: 0.9.3.6
description: Stargate mod for Bukkit Revived
author: EpicKnarvik97
authors: [ Drakia, PseudoKnight, EpicKnarvik97 ]
website: https://git.knarcraft.net/EpicKnarvik97/Stargate
api-version: 1.17
api-version: 1.18
softdepend: [ Vault ]
commands:
stargate:
aliases:
- sg
description: Used to see stargate info
usage: /<command> <reload/about> - Used to see stargate info or reload the plugin
description: Used to see stargate info, reloading and editing config values
usage: /<command> <reload/about/config> - Used to see stargate info, reload the plugin or change config values
permissions:
stargate.*:
description: Wildcard permission which gives all Stargate permissions
@ -97,6 +97,8 @@ permissions:
stargate.option.show: true
stargate.option.nonetwork: true
stargate.option.random: true
stargate.option.silent: true
stargate.option.nosign: true
stargate.option.hidden:
description: Allows the creation of a hidden stargate
default: false
@ -121,6 +123,12 @@ permissions:
stargate.option.random:
description: Allows the creation of a stargate with a random destination
default: false
stargate.option.silent:
description: Allows the creation of a stargate which does not output anything to the chat
default: false
stargate.option.nosign:
description: Allows the creation of a stargate which has no sign
default: false
stargate.admin.hidden:
description: Allows this player to see all hidden stargates
default: false
@ -130,14 +138,22 @@ permissions:
stargate.admin.bungee:
description: Allows the creation and destruction of a stargate between BungeeCord servers
default: false
stargate.admin.config:
description: Allows the player to change config values from the chat
default: false
stargate.admin.dye:
description: Allows this player to change the dye of any stargate's sign
default: false
stargate.server:
description: Allows the creation of a BungeeCord stargate going to any server
default: false
stargate.admin:
description: Allow all admin features and commands (Hidden/Private bypass, BungeeCord, Reload)
description: Allow all admin features and commands (Hidden/Private bypass, BungeeCord, Reload, Config)
default: op
children:
stargate.admin.reload: true
stargate.admin.hidden: true
stargate.admin.private: true
stargate.admin.bungee: true
stargate.admin.bungee: true
stargate.admin.config: true
stargate.admin.dye: true

View File

@ -5,6 +5,8 @@ import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.WorldMock;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.portal.property.gate.GateHandler;
import net.knarcraft.stargate.portal.property.gate.GateLayout;
import org.bukkit.Material;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@ -30,6 +32,7 @@ public class GateLayoutTest {
@AfterAll
public static void tearDown() {
MockBukkit.getMock().getPluginManager().disablePlugins();
MockBukkit.unmock();
}