104 Commits

Author SHA1 Message Date
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
59 changed files with 2899 additions and 677 deletions

168
README.md
View File

@ -3,17 +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
- 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)
- **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)
- **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
@ -35,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
@ -59,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}
@ -78,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
@ -115,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
@ -127,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:
@ -200,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.
@ -284,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.
@ -313,7 +331,8 @@ 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
@ -368,10 +387,105 @@ 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.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

16
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>net.knarcraft</groupId>
<artifactId>Stargate</artifactId>
<version>0.9.0.5</version>
<version>0.9.3.2</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>
@ -49,8 +49,8 @@
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.17</artifactId>
<version>1.7.0</version>
<artifactId>MockBukkit-v1.18</artifactId>
<version>1.14.0</version>
<scope>test</scope>
</dependency>
<dependency>
@ -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

@ -21,6 +21,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;
@ -55,6 +56,8 @@ public class Stargate extends JavaPlugin {
private static PluginManager pluginManager;
private static StargateConfig stargateConfig;
private static String updateAvailable = null;
/**
* Empty constructor necessary for Spigot
*/
@ -74,6 +77,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 +162,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 +339,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 +360,9 @@ public class Stargate extends JavaPlugin {
runThreads();
this.registerCommands();
//Check for any available updates
UpdateChecker.checkForUpdate();
}
/**

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;

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 {

View File

@ -0,0 +1,261 @@
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<>();
chatColors.add(ChatColor.WHITE);
chatColors.add(ChatColor.BLUE);
chatColors.add(ChatColor.DARK_BLUE);
chatColors.add(ChatColor.DARK_PURPLE);
chatColors.add(ChatColor.LIGHT_PURPLE);
chatColors.add(ChatColor.GOLD);
chatColors.add(ChatColor.GREEN);
chatColors.add(ChatColor.BLACK);
chatColors.add(ChatColor.DARK_GREEN);
chatColors.add(ChatColor.DARK_RED);
chatColors.add(ChatColor.RED);
chatColors.add(ChatColor.AQUA);
chatColors.add(ChatColor.DARK_AQUA);
chatColors.add(ChatColor.DARK_GRAY);
chatColors.add(ChatColor.GRAY);
chatColors.add(ChatColor.YELLOW);
chatColors.add(ChatColor.of("#ed76d9"));
chatColors.add(ChatColor.of("#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");
//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,262 @@
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"),
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 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();
}

View File

@ -2,37 +2,32 @@ 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 handleEmptyVehicles = true;
private boolean handleCreatureTransportation = true;
private boolean handleNonPlayerVehicles = true;
private boolean handleLeashedCreatures = 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();
}
/**
@ -59,7 +54,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);
}
/**
@ -68,7 +63,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);
}
/**
@ -77,7 +72,7 @@ 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);
}
/**
@ -89,7 +84,7 @@ public final class StargateGateConfig {
* @return <p>Whether vehicles without passengers should be handled</p>
*/
public boolean handleEmptyVehicles() {
return handleEmptyVehicles;
return (boolean) configOptions.get(ConfigOption.HANDLE_EMPTY_VEHICLES);
}
/**
@ -101,7 +96,7 @@ public final class StargateGateConfig {
* @return <p>Whether vehicles with creatures should be handled</p>
*/
public boolean handleCreatureTransportation() {
return handleCreatureTransportation;
return (boolean) configOptions.get(ConfigOption.HANDLE_CREATURE_TRANSPORTATION);
}
/**
@ -118,7 +113,7 @@ public final class StargateGateConfig {
* @return <p>Whether non-empty vehicles without a player should be handled</p>
*/
public boolean handleNonPlayerVehicles() {
return handleNonPlayerVehicles;
return (boolean) configOptions.get(ConfigOption.HANDLE_NON_PLAYER_VEHICLES);
}
/**
@ -127,7 +122,7 @@ public final class StargateGateConfig {
* @return <p>Whether leashed creatures should be handled</p>
*/
public boolean handleLeashedCreatures() {
return handleLeashedCreatures;
return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES);
}
/**
@ -136,7 +131,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);
}
/**
@ -145,7 +140,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);
}
/**
@ -154,7 +149,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);
}
/**
@ -163,7 +158,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);
}
/**
@ -172,7 +167,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);
}
/**
@ -181,37 +176,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");
handleEmptyVehicles = newConfig.getBoolean("gates.functionality.handleEmptyVehicles");
handleCreatureTransportation = newConfig.getBoolean("gates.functionality.handleCreatureTransportation");
handleNonPlayerVehicles = newConfig.getBoolean("gates.functionality.handleNonPlayerVehicles");
handleLeashedCreatures = newConfig.getBoolean("gates.functionality.handleLeashedCreatures");
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);
}
}
}
/**
@ -219,16 +285,19 @@ 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

@ -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

@ -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

@ -1,16 +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.Teleporter;
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.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;
@ -26,6 +31,11 @@ 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 java.util.HashMap;
import java.util.Map;
/**
* This listener listens to any player-related events related to stargates
@ -33,8 +43,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
@ -43,14 +52,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(event.getPlayer());
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) {
@ -119,7 +136,9 @@ public class PlayerEventListener implements Listener {
//Just teleport the player like normal
new PlayerTeleporter(destination, player).teleport(entrancePortal, event);
}
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
}
@ -159,12 +178,14 @@ public class PlayerEventListener implements Listener {
//Decide if the user should be teleported to another bungee server
if (entrancePortal.getOptions().isBungee()) {
if (BungeeHelper.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 Teleporter.noLeashedCreaturesPreventTeleportation(player);
}
/**
@ -182,7 +203,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);
@ -203,6 +224,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
@ -238,10 +271,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;
@ -253,19 +288,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;
@ -281,33 +317,68 @@ 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;
}

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;

View File

@ -3,7 +3,8 @@ 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.Teleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
@ -89,7 +90,9 @@ public class VehicleEventListener implements Listener {
//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"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
return;
}
@ -108,7 +111,7 @@ public class VehicleEventListener implements Listener {
//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);
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
if (cost > 0) {
if (!takePlayerPayment(passengers, entrancePortal, cost)) {
return;
@ -118,7 +121,9 @@ public class VehicleEventListener implements Listener {
//Teleport the vehicle and inform the user if the vehicle was teleported
boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal);
if (teleported) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
}
}
@ -152,18 +157,24 @@ public class VehicleEventListener implements Listener {
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"));
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 = Stargate.getEconomyConfig().getUseCost(player, entrancePortal, destinationPortal);
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
if (!canAffordFee) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds"));
}
return false;
}
return canAffordFee;
return Teleporter.noLeashedCreaturesPreventTeleportation(player);
}
}

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,18 @@ 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;
}

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,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;

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

@ -1,7 +1,8 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.Location;
import org.bukkit.entity.Entity;

View File

@ -1,10 +1,14 @@
package net.knarcraft.stargate.portal;
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 org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
/**
* The portal teleporter takes care of the actual portal teleportation for any players
@ -31,6 +35,7 @@ public class PlayerTeleporter extends Teleporter {
* @param event <p>The player move event triggering the event</p>
*/
public void teleport(Portal origin, PlayerMoveEvent event) {
double velocity = player.getVelocity().length();
Location traveller = player.getLocation();
Location exit = getExit(player, traveller);
@ -55,9 +60,16 @@ public class PlayerTeleporter extends Teleporter {
if (event == null) {
player.teleport(exit);
} else {
//The new method to teleport in a move event is set the "to" field.
//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(), () -> {
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity * Stargate.getGateConfig().getExitVelocity());
player.setVelocity(newVelocity);
}, 1);
}
/**

View File

@ -1,9 +1,10 @@
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.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import org.bukkit.Chunk;
@ -58,7 +59,7 @@ public abstract class Teleporter {
}
float newYaw = (portal.getYaw() + adjust) % 360;
Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw);
exit.setYaw(newYaw);
exit.setDirection(DirectionHelper.getDirectionVectorFromYaw(newYaw));
}
/**
@ -140,7 +141,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;
}
@ -190,9 +191,9 @@ public abstract class Teleporter {
*/
private Location adjustExitLocation(Location traveller, 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) {
BlockData blockData = exitLocation.getBlock().getBlockData();
if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab slab && slab.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);
@ -216,8 +217,8 @@ public abstract class Teleporter {
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));
//Allow the chunk to unload after 10 seconds
Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L));
}
}
@ -249,9 +250,37 @@ public abstract class Teleporter {
return chunksToLoad;
}
/**
* Checks whether a player has leashed creatures that block the teleportation
*
* @param player <p>The player trying to teleport</p>
* @return <p>False if the player has leashed any creatures that cannot go through the portal</p>
*/
public static boolean noLeashedCreaturesPreventTeleportation(Player player) {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyCreatures = getLeashedCreatures(player);
//Disallow creatures with passengers to prevent smuggling
for (Creature creature : nearbyCreatures) {
if (!creature.getPassengers().isEmpty()) {
return false;
}
}
//If it's enabled, there is no problem
if (Stargate.getGateConfig().handleLeashedCreatures()) {
return true;
} else {
return nearbyCreatures.isEmpty();
}
}
/**
* Teleports any creatures leashed by the player
*
* <p>Will return false if the teleportation should be aborted because the player has leashed creatures that
* aren't allowed to be teleported with the player.</p>
*
* @param player <p>The player which is teleported</p>
* @param origin <p>The portal the player is teleporting from</p>
*/
@ -263,12 +292,13 @@ public abstract class Teleporter {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyEntities = getLeashedCreatures(player);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Creature creature : nearbyEntities) {
creature.setLeashHolder(null);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
new EntityTeleporter(portal, creature).teleport(origin);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player), 3);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player), 6);
}, 2);
}
}
@ -279,7 +309,7 @@ public abstract class Teleporter {
* @param player <p>The player to check</p>
* @return <p>A list of all creatures the player is holding in a leash (lead)</p>
*/
protected List<Creature> getLeashedCreatures(Player player) {
protected static List<Creature> getLeashedCreatures(Player player) {
List<Creature> leashedCreatures = new ArrayList<>();
//Find any nearby leashed entities to teleport with the player
List<Entity> nearbyEntities = player.getNearbyEntities(15, 15, 15);

View File

@ -1,7 +1,8 @@
package net.knarcraft.stargate.portal;
package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper;
import org.bukkit.Location;
import org.bukkit.World;
@ -164,7 +165,7 @@ public class VehicleTeleporter extends EntityTeleporter {
private void teleportLivingVehicle(Location exit, List<Entity> passengers, Portal origin) {
teleportingVehicle.eject();
teleportingVehicle.teleport(exit);
handleVehiclePassengers(passengers, teleportingVehicle, 2, origin);
handleVehiclePassengers(passengers, teleportingVehicle, 2, origin, exit.getDirection());
}
/**
@ -193,19 +194,20 @@ public class VehicleTeleporter extends EntityTeleporter {
teleportingVehicle.remove();
//Set rotation, add passengers and restore velocity
newVehicle.setRotation(exit.getYaw(), exit.getPitch());
handleVehiclePassengers(passengers, newVehicle, 1, origin);
handleVehiclePassengers(passengers, newVehicle, 1, origin, exit.getDirection());
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1);
}
/**
* Ejects, teleports and adds all passengers to the target vehicle
*
* @param passengers <p>The passengers to handle</p>
* @param vehicle <p>The vehicle the passengers should be put into</p>
* @param delay <p>The amount of milliseconds to wait before adding the vehicle passengers</p>
* @param origin <p>The portal the vehicle teleported from</p>
* @param passengers <p>The passengers to handle</p>
* @param vehicle <p>The vehicle the passengers should be put into</p>
* @param delay <p>The amount of milliseconds to wait before adding the vehicle passengers</p>
* @param origin <p>The portal the vehicle teleported from</p>
* @param exitRotation <p>The rotation of any passengers exiting the stargate</p>
*/
private void handleVehiclePassengers(List<Entity> passengers, Vehicle vehicle, long delay, Portal origin) {
private void handleVehiclePassengers(List<Entity> passengers, Vehicle vehicle, long delay, Portal origin, Vector exitRotation) {
for (Entity passenger : passengers) {
passenger.eject();
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
@ -213,7 +215,7 @@ public class VehicleTeleporter extends EntityTeleporter {
//Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin);
}
teleportAndAddPassenger(vehicle, passenger);
teleportAndAddPassenger(vehicle, passenger, exitRotation);
}, delay);
}
}
@ -226,9 +228,10 @@ public class VehicleTeleporter extends EntityTeleporter {
*
* @param targetVehicle <p>The vehicle to add the passenger to</p>
* @param passenger <p>The passenger to teleport and add</p>
* @param exitDirection <p>The direction of any passengers exiting the stargate</p>
*/
private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger) {
if (!passenger.teleport(targetVehicle.getLocation())) {
private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger, Vector exitDirection) {
if (!passenger.teleport(targetVehicle.getLocation().clone().setDirection(exitDirection))) {
Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger");
}
if (!targetVehicle.addPassenger(passenger)) {

View File

@ -39,8 +39,7 @@ public class BlockChangeThread implements Runnable {
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) {
@ -56,8 +55,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,9 +1,10 @@
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;
@ -71,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
@ -102,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());
@ -181,7 +182,9 @@ public final class BungeeHelper {
public static 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"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
@ -205,4 +208,14 @@ public final class BungeeHelper {
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,47 @@
package net.knarcraft.stargate.utility;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ColorHelper {
/**
* Inverts the given color
*
* @param color <p>The color to invert</p>
* @return <p>The inverted color</p>
*/
public static Color invert(Color color) {
return color.setRed(255 - color.getRed()).setGreen(255 - color.getGreen()).setBlue(255 - color.getBlue());
}
/**
* Gets the chat color corresponding to the given color
*
* @param color <p>The color to convert into a chat color</p>
* @return <p>The resulting chat color</p>
*/
public static ChatColor fromColor(Color color) {
return ChatColor.of(String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()));
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
}

View File

@ -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,7 +384,9 @@ public final class PermissionHelper {
//Not open for this player
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
new PlayerTeleporter(entrancePortal, player).teleport(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"));
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
new PlayerTeleporter(entrancePortal, player).teleport(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

@ -3,8 +3,8 @@ 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.PortalOwner;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.portal.property.PortalOwner;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
@ -19,7 +19,11 @@ import java.util.UUID;
/**
* Helps migrate player names to UUID where necessary
*/
public class UUIDMigrationHelper {
public final class UUIDMigrationHelper {
private UUIDMigrationHelper() {
}
private static Map<String, List<Portal>> playerNamesToMigrate;
@ -40,7 +44,7 @@ public class UUIDMigrationHelper {
return;
}
Stargate.debug("PlayerEventListener::migrateUUID", String.format("Migrating name to UUID for player %s",
Stargate.debug("UUIDMigrationHelper::migrateUUID", String.format("Migrating name to UUID for player %s",
playerName));
List<Portal> portalsOwned = playersToMigrate.get(playerName);
if (portalsOwned == null) {
@ -64,7 +68,7 @@ public class UUIDMigrationHelper {
//Get the real portal from the copy and set UUID
for (Portal portalCopy : portals) {
Portal portal = PortalHandler.getByName(portalCopy.getName(), portalCopy.getNetwork());
Portal portal = PortalHandler.getByName(portalCopy.getCleanName(), portalCopy.getCleanNetwork());
if (portal != null) {
portal.getOwner().setUUID(uniqueId);
worldsToSave.add(portal.getWorld());

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,70 +1,86 @@
# 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. This overrides other vehicle settings
# handleEmptyVehicles - Whether to allow empty vehicles through gates (chest/hopper/tnt/furnace minecarts included)
# handleCreatureTransportation - Whether to allow players to transport creatures by sending vehicles (minecarts, boats) through gates
# handleNonPlayerVehicles - Whether to allow vehicles with a passenger which is not a player through gates. handleCreatureTransportation must be enabled
# handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player
# 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
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
# 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 - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
permissionDebug: false

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

@ -1,18 +1,18 @@
name: Stargate
main: net.knarcraft.stargate.Stargate
version: 0.9.0.5
version: 0.9.3.2
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();
}