218 Commits

Author SHA1 Message Date
b98aec4a20 0.9.0.2 Fixes a bug in the code to prevent nether portals from generating in the nether 2021-11-01 00:38:36 +01:00
89956cf359 Updates the plugin version in plugin.yml 2021-10-31 19:51:33 +01:00
0655d85356 Updates README with the changes in 0.9.0.1 2021-10-31 18:10:19 +01:00
06cb99de44 Bumps version to 0.9.0.1 2021-10-31 18:09:56 +01:00
99f8a92857 Adds a highlightSignColor option and fixes some color inconsistencies
Makes all characters used for highlighting/marking use the highlighting color
Adds a new config option for highlighting color
Renames signColor to mainSignColor and adds the change to the config-migrations.txt file
2021-10-31 18:08:58 +01:00
01df8db0bf Moves config loading to finisSetup() to prevent a NullPointerException 2021-10-31 18:05:04 +01:00
d754247455 Makes some smaller changes
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
Replaces the mystery code in sendMessage with translateAlternateColorCodes and removes the TODO
Removes the TODO in deactivate() as it is actually necessary because fixed portals are always active
Moves conflictsWithExistingGate to PortalCreator and removes the TODO for now
Removes the extra spaces in >Random<
Adjusts some comments
Adds missing information about Economy and the requirement of UUIDs
2021-10-31 14:05:06 +01:00
1f1fef3054 Changes Jenkins to JDK 16 as Maven doesn't like Java 17. Ugh.
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-10-30 15:33:58 +02:00
40f0a99e5a Forces Jenkins to use a compatible JDK
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-10-30 15:17:15 +02:00
8fa0cca5ce Adds Jenkinsfile for automated building
Some checks failed
EpicKnarvik97/Stargate/pipeline/head There was a failure building this commit
2021-10-30 14:48:11 +02:00
a4075363ec Fixes some slightly incorrect information about economy gate configuration 2021-10-30 06:14:24 +02:00
260211b677 Adds missing chest buttons to the readme 2021-10-30 05:59:12 +02:00
5d41b2114b Adds migration information and some missing information to the description 2021-10-29 22:08:54 +02:00
50c446f9d9 Adds package JavaDoc to help anyone who wants to use the Stargate API 2021-10-29 21:19:24 +02:00
56bf51f370 Adds missing JavaDoc 2021-10-29 18:43:22 +02:00
56ed0495b0 Removes the last of the unprotected variables from Stargate
Makes all classes use getInstance() to get a Stargate instance
Removes the server variable as it's not needed
2021-10-29 18:35:20 +02:00
0237f45046 Renames activatedTime to triggeredTime and makes some public fields private
Makes the active portals queue and open portals queue and languageLoader fields private
Cleans the StargateThread a bit
Renames activatedTime to triggeredTime as the dual use (open and activate) made the name confusing
2021-10-29 17:22:58 +02:00
5d84e1d78a Changes some sign coloring and adds some missing info to the readme
Makes the Disconnected message red
Makes the highlighting characters (><) white when cycling stargate destinations
Moves markPortalWithInvalidGate to PortalSignDrawer
Adds missing translation strings for reloaded and signInvalidGate to the readme
Moves some extra spacing around the >< characters
2021-10-29 16:05:23 +02:00
f52ba79ae9 Splits the preventExitSuffocation method and improves some more comments 2021-10-29 15:08:44 +02:00
945798916b Removes an unnecessary portal close statement after teleporting a player through BungeeCord 2021-10-29 15:07:45 +02:00
f0e5cd45a4 Adds some minor style improvements 2021-10-29 01:46:15 +02:00
487cb3ad9e Adds missing info about Bungee-related permissions to the readme and adjusts permissions a bit
Adds stargate.server and stargate.admin.bungee permissions to the readme
Renames stargate.reload to stargate.admin.reload for better consistency
Makes the stargate.admin permission only give the reload, hidden, private and bungee permissions while the wildcard permission gives all permissions
2021-10-29 01:45:50 +02:00
544384f69b Cleans up permissions a lot and adds missing permissions to plugin.yml
Removes the checking of both parent and child by adding the child permissions to plugin.yml
Cleans comments in the PermissionHelper class
Renames hasPermDeep to hasPermissionImplicit as I finally understand how it's supposed to work
Adds missing unmock() to prevent test errors
Adds a ton of permissions which were mentioned in the code, but did not exist in the plugin.yml
2021-10-28 18:29:33 +02:00
3fc0e6963a Adds tests for the material helper and adds all chest variations as button compatible 2021-10-27 20:56:56 +02:00
5c730eb613 Fixes some portal closing bugs, a NullPointerException and a typo
Fixes a typo in nn-no
Fixes a bug causing always-on portals not to be closed properly
Fixes a possible NullPointerException in onEntityPortalEnter
Waits for block change request on reload which makes it possible to change the open-material with just a reload
2021-10-26 16:22:20 +02:00
1c906528f2 Cleans up message logging quite a bit
Adds methods to Stargate for easier logging and less redundancy
Loads the language loader in two parts to make it available while loading
Adds a translated string to the reload-message
Uses String.format to make long messages more readable
Makes it possible to get strings directly from the backup language to make debugging easier
2021-10-26 15:05:05 +02:00
eaf7596014 Adds information about the new default gates to the Building a gate section 2021-10-26 12:51:05 +02:00
669767ef89 Renames RelativeBlockVector's right, depth and distance to right, down and out respectively 2021-10-24 22:48:13 +02:00
822f8fb2b5 Changes BungeeCord messages to use UUID instead of player name 2021-10-24 21:15:43 +02:00
3367d4bb76 Disables the generation of buttons on bungee portals 2021-10-23 22:17:02 +02:00
a100ad3fea Fixes a check which broke all bungee portals 2021-10-23 22:16:36 +02:00
2541391d3e Moves all config related code from Stargate to StargateConfig 2021-10-23 18:34:31 +02:00
b7998023f5 Moves activeTime and openTime into the StargateGateConfig 2021-10-23 14:35:07 +02:00
deba2e5c2c Makes the stargate logger private 2021-10-23 14:25:46 +02:00
7cc8685e26 Moves all methods for sending messages to players to the MessageSender class 2021-10-23 14:10:33 +02:00
50084c40f9 Moves the Bungee Queue to BungeeHelper 2021-10-23 12:58:31 +02:00
2196d5b99e Moves some config values from StarGate and into the StarGateConfig class
Renames EconomyHandler to EconomyConfig and puts it in the new config package
Moves the LanguageLoader to the config package
Fixes the explanation of chargeFreeDestination in the README
2021-10-23 03:56:59 +02:00
070e7250df Re-draws every portal sign each time a new world is loaded to prevent weird states 2021-10-22 21:56:35 +02:00
9a0f16e558 Moves the portal owner name and owner UUID to the PortalOwner class which makes the TwoTuple unnecessary 2021-10-22 19:51:46 +02:00
485ca284b0 Extracts portal saving and loading to the PortalFileHelper class 2021-10-22 16:18:35 +02:00
4e09b44c7c Extracts portal registration and all portal lookup to the PortalRegistry class 2021-10-21 23:59:16 +02:00
593d528bcd Extracts the portal creation and validation part of the PortalHandler into the PortalCreator 2021-10-21 20:15:29 +02:00
79a43b82e5 Fixes the readme's max sign character limits as the real limit is 11, not 12 2021-10-21 20:13:34 +02:00
cb2f7443f5 Improves comments for PortalOptions 2021-10-21 16:39:35 +02:00
8eff9ae5e6 Fixes comments for the PortalOpener class 2021-10-21 00:27:42 +02:00
4b42d4914a Renames openTime to activatedTime, though it's kind of used for both opening and activation 2021-10-20 20:57:00 +02:00
2650e31d97 Fixes comments for the portal activator 2021-10-20 20:55:51 +02:00
d2e8c81a5a Splits the portal class into Portal, PortalActivator, PortalOpener and PortalStructure
PortalStructure now contains information about the gate's physical structure, such as the location of border blocks, the location of entrances, the gate type and the button location.
PortalActivator is responsible for activating/de-activating portals, destination toggling and getting information about available destinations.
PortalOpener is responsible for opening/closing a portal. It's also the place to go for checking if the portal is open for a given player.
Comments of the Portal class have been improved, but the comments of the three new classes need fixing.
2021-10-20 16:09:35 +02:00
635d08b1b3 Makes the SignHelper helper class into the proper PortalSignDrawer which each Portal now has one instance of 2021-10-20 01:33:36 +02:00
1d4b988ca4 Moves the rest of the sign drawing code from Portal to SignHelper 2021-10-18 19:12:30 +02:00
82ed28bba0 Removes the DirectionHelper's getBlockAt method as it only increased complexity 2021-10-18 18:52:24 +02:00
0506cf1b61 Splits the PortalTeleporter into a PlayerTeleporter and a VehicleTeleporter for better structuring
Moves player-related teleportation code to PlayerTeleporter, moves vehicle-related teleportation code to VehicleTeleporter and makes them both extend the Teleporter which now contains all common teleportation code.
2021-10-18 18:38:36 +02:00
f4ec5e05d6 Improves sign drawing during portal loading
Adds "Invalid gate" to the last line of portals with an invalid or unloaded gate type
Re-draws all portals' signs on load in case some invalid portals becomes valid. This also updates any formatting
2021-10-18 18:34:35 +02:00
8c16ddbed5 Adds some tests to the relative block vector 2021-10-18 15:22:55 +02:00
27b1f0641e Adds some block location tests 2021-10-18 14:57:12 +02:00
ac045fa7db Fixes a severe bug caused by trying to simplify GateLayout's saveLayout method 2021-10-18 04:00:18 +02:00
982d8abf65 Extracts teleportation code into the PortalTeleporter class to improve readability 2021-10-18 03:41:16 +02:00
f96e8ed2da Adds a method for checking if a player can afford a given fee 2021-10-18 03:36:56 +02:00
d9ae5456cc Improves permission checking for vehicles with multiple passengers
Changes some log messages into debug messages
Makes sure that all player passengers of a vehicle have their permissions verified and fee paid. This ensures passengers won't get a free ride, or be allowed to access restricted areas by playing stowaway.
2021-10-18 03:35:59 +02:00
fabe0dda80 Fixes inconsistencies in coloring of the portal name when drawing signs. The - is now white. 2021-10-17 22:47:27 +02:00
3de785d5ab Improves and fixes comments for the gate layout class 2021-10-16 16:44:11 +02:00
59069d1423 Improves comments in the GateHandler class and extracts some code into GateReader
Moves code for reading .gate files into the GateReader helper class
Improves all comments in GateHandler where possible
Adds more helper comments
2021-10-15 22:16:02 +02:00
5299efaa86 Renames types to characterMaterialMap inside the GateHandler class 2021-10-15 19:46:25 +02:00
e2c91c1feb Changes EconomyHandler method names to be more consistent 2021-10-15 19:25:31 +02:00
d45af537cd Removes the unused getCorners method 2021-10-15 19:24:15 +02:00
6e658003e0 Improves Gate comments where possible
Renames types to characterMaterialMap
Simplifies writeConfig to a single method
2021-10-15 19:23:17 +02:00
382156a719 Adds missing information about gate economy config values 2021-10-15 18:52:02 +02:00
44325eeb6a Improves and fixes comments for listeners
Removes the enableBungee check in the BungeeCordListener as it should only be listening if the option is enabled anyway
Improves the checking for players teleporting from the end
2021-10-13 16:46:30 +02:00
bf7a10636e Improves comments for Stargate events, and adds a new event for teleporting entities
Adds information about what events can be used for
Tries to clarify event comments where possible
Renames The StargatePortalEvent to StargatePlayerPortalEvent
Adds StargateEntityPortalEvent
Makes the StargateEntityPortalEvent trigger whenever a vehicle is teleported
Removes the unused event name for all events
2021-10-13 15:45:15 +02:00
0ab6cb52c0 Makes some small comment adjustments 2021-10-13 14:08:38 +02:00
f16a7089f4 Updates the comments for BlockLocation 2021-10-13 13:35:56 +02:00
4bdc5b6bd9 Populates default gates after migrating config to update default gates for old installations 2021-10-12 20:41:45 +02:00
5b6e3f81a6 Updates readme to mark end portals as functional 2021-10-12 04:18:58 +02:00
0709c18e30 Adjusts height to get above slabs to an entire block as empty minecarts clipped through single slab layers 2021-10-12 03:48:13 +02:00
e14007380f Adds proper checking and odd case catching before teleporting players through artificial end portals
Adds a proper permission check to make sure players are allowed to teleport through the artificial portal
Adds a teleportation back to the entrance as the teleportation event cannot be properly cancelled
Adds a proper class for storing info about the teleportation
2021-10-12 02:47:09 +02:00
53cd55938b Fixes teleportation of players using end portals to and from the end 2021-10-12 01:11:52 +02:00
51afa1527f Updates the API version used as 1.16 is no longer the target version 2021-10-11 20:16:36 +02:00
72c1b5a239 Updates About information 2021-10-11 20:13:50 +02:00
b0c350a140 Updates README to reflect vehicle teleportation capabilities 2021-10-11 01:35:12 +02:00
06757ef9ee Removes debug output for chunk unloading 2021-10-11 01:20:50 +02:00
9efc960696 Makes sure to check entrance blocks when, and only when, protectEntrance is enabled 2021-10-11 00:11:04 +02:00
1bf9914c39 Generifies another vehicle check 2021-10-11 00:03:49 +02:00
05123d54bd Generifies the check for non-living vehicles just in case 2021-10-10 23:38:20 +02:00
2e4d545955 Changes the vehicle check to prevent players in boats from leaving their boat to teleport 2021-10-10 23:17:29 +02:00
f8fae1fbf1 Makes sure to ignore mounted players if handleVehicles is disabled 2021-10-10 23:11:52 +02:00
964eb0f898 Adds a new The End- inspired gate for more default diversity 2021-10-10 23:03:39 +02:00
38ea543b80 Improves chunk unloading
Adds all chunk unloading to a queue
Adds a thread which unloads chunks
Updates chunk unload requests such that a chunk won't be unloaded twice,
and an old unloading request cannot unload a chunk too soon
2021-10-10 22:33:30 +02:00
69a62c921c Fixes the color inconsistency for the portal selection "arrows" for gates with colored names 2021-10-10 17:21:19 +02:00
b847002617 Adds some small changes which seem to completely fix all horse teleportation bugs. Fixes #1 2021-10-10 17:15:00 +02:00
6ad7fa4cb9 Removes the IllegalStateException Removing entity while ticking! TODO as it seems to have been fixed by preventing portal creation 2021-10-10 16:14:51 +02:00
2abe10bcde Improves the way chunks are loaded, and decreases the wait time before players are put into minecarts 2021-10-10 15:10:36 +02:00
7a9dbb8046 Implements some Java 14 code migrations 2021-10-09 23:41:19 +02:00
3a8943baef Configures the maven-compiler-plugin to also compile the source code as Java 16 2021-10-09 23:38:55 +02:00
be8de83bcc Forces a teleported horse to become tamed 2021-10-09 18:48:59 +02:00
ba3304a716 Fixes a minecart rotation bug caused by changing the rotation of the deleted vehicle instead of the new one 2021-10-09 17:03:19 +02:00
051a6b8f98 Removes the temporary ignoreEntrances option and replaces it with proper snowman blocking. Fixes #3
Allows new gates to contain water as underwater gates are a thing now
Adds a check to prevent snowmen from placing snow inside a portal's entrance
Removes the ignoreEntrances option everywhere
2021-10-09 15:09:14 +02:00
f87ffc906c Minor comment and formatting cleanup 2021-10-09 03:57:24 +02:00
336c3c4bfb Updates information about plugin settings 2021-10-09 03:02:00 +02:00
2fec641d9d Fixes typos 2021-10-08 23:23:06 +02:00
0c29788a31 Fixes the behavior of backwards portals
Fixes a bug where backwards portals only rotated the player
Fixes the rotation being wrong when teleporting from a backwards portal
2021-10-08 18:59:14 +02:00
fff4d8d78b Gets rid of the rest of the modX and modY usages, and removes some unused code 2021-10-08 18:23:42 +02:00
a68dc4b464 Updates the README with new info and fixes a ton of gramatical errors 2021-10-08 18:21:30 +02:00
6d5c4802bc Creates some new methods to get a location from a relative location which I can actually understand 2021-10-08 15:28:12 +02:00
e7fc1daafe Moves functionality to the PortalOptions and PortalLocation classes 2021-10-08 01:26:12 +02:00
60c543e52a Adds a new class for keeping track of portal options 2021-10-08 01:25:25 +02:00
76b2aab057 Tries to improve readability of the portal creation code 2021-10-06 19:46:34 +02:00
201f7eaf15 Adds a class for storing a portal's location data 2021-10-06 19:45:49 +02:00
d86aae87f3 Extracts portal creation validation into its own method 2021-09-25 13:52:00 +02:00
e4f71f1b71 Extracts some portal creation code into separate methods 2021-09-25 13:22:50 +02:00
7dcf050d96 Removes a function for checking if a control block is powered as it's never used 2021-09-25 12:46:59 +02:00
8ada84ddb3 Improves an error message 2021-09-23 18:21:15 +02:00
98cee192aa Fixes some behavior when a language is valid, but the language file does not exist 2021-09-23 18:12:57 +02:00
650a26402a Moves all config migration mappings to its own file 2021-09-23 17:50:43 +02:00
461202503e Adds migration for useiconomy and CheckUpdates which were found in old config files 2021-09-22 13:52:22 +02:00
dd7176fa12 Adds config migration to reduce annoyance and improve backwards compatibility 2021-09-22 13:42:21 +02:00
b7c7252fad Improves the differentiation between portals (stargates) and gates 2021-09-21 18:28:18 +02:00
24af26324a Renames some methods to prevent confusion 2021-09-20 19:23:57 +02:00
f2579c4b12 Adds back default constructor as removing it caused it to no longer load 2021-09-20 18:52:16 +02:00
1e29db58b9 Improves some variable names and adds some comments 2021-09-20 18:22:20 +02:00
d24f35375a Changes the default folders to prevent problems on Unix systems 2021-09-20 18:21:26 +02:00
f681db629f Refactors a lot of code, and extracts permission-related functions to the PermissionHelper class 2021-09-20 13:56:30 +02:00
b57f988b62 Improves formatting for some files 2021-09-20 13:48:03 +02:00
f12306426b Renames strings in onCommand to args for consistency 2021-09-20 13:46:20 +02:00
8ff30ed03f Improves config readability, but breaks backwards compatibility 2021-09-19 17:46:20 +02:00
d5e6f1145c Removes unused getBalance method 2021-09-19 15:06:41 +02:00
8835e69e3c Moves some code from Stargate to EconomyHandler 2021-09-19 15:05:19 +02:00
b191ac1de5 Moves some classes to the new container package, and improves some code 2021-09-18 21:51:29 +02:00
4851a0b5e2 Only gets vehicle exit world when it's actually used 2021-09-16 21:31:32 +02:00
e253e95cec Minor function cleaning 2021-09-12 15:23:22 +02:00
c35378cfe0 Improves pre-teleport chunk loading 2021-09-12 06:18:20 +02:00
319849fd96 Prevents suffocation when teleporting on a horse 2021-09-12 06:02:10 +02:00
abd48b646d Fixes code for slab checking to prevent the player from teleporting underneath the block 2021-09-12 02:21:13 +02:00
f2332badb6 Adds some missing information about creating bungee gates 2021-09-12 01:31:21 +02:00
19018e46b8 Fixes some bugs regarding bungee teleportation
Fixes the server being teleported normally after it's teleported to another server
Fixes a nullpointerexception
2021-09-12 01:23:16 +02:00
ec4ed1e086 Fixes some more warnings 2021-09-11 17:02:43 +02:00
5c601710e7 Removes the unused frameBlocks variable and isGateBlock 2021-09-11 16:44:55 +02:00
a6fb7dcb62 Fixes some warnings 2021-09-11 16:43:31 +02:00
6005c2c6d1 Adds some information about usable portal open/closed materials to the readme 2021-09-11 16:37:55 +02:00
1c3dbbe81d Renames the blox populator and block populator thread as I finally understand what they actually do 2021-09-11 15:33:45 +02:00
87735e4935 Adds some helper functions to make getting direction-related values easier
Adds a function for getting the yaw given two locations
Adds a function for getting a block face given a yaw
2021-09-11 15:04:55 +02:00
93f8f715e5 Fixes some old bugs and renames rotX to yaw
Fixes the direction of minecarts sent through a portal. This prevent the minecarts to go back through the portal
and causing a lot of confusion
2021-09-10 23:38:56 +02:00
b4059dd169 Adds an event listener to prevent the nether portal stargates from creating actual nether portals 2021-09-10 23:35:27 +02:00
a86a5de8c3 Fixes the bug with teleporting horses, but introduces a bug with teleporting minecarts 2021-09-10 21:32:58 +02:00
7b83b2440c Sets junit version 2021-09-09 15:43:50 +02:00
8ae4ac3fc7 Makes sure to only try and create the portal folder if it does not exist 2021-09-09 15:42:30 +02:00
3ac6270897 Merge branch 'master' into vehicles 2021-09-09 15:26:25 +02:00
daa3c6f868 Cleans up a bit and changes to compile for java 16 and spigot 1.17 2021-09-09 15:25:08 +02:00
75fbd44af7 Removes an s in class name of PlayerEventsListener 2021-09-02 00:31:03 +02:00
0fe2a5b380 Adds some more fixes and improvements for vehicle teleportation 2021-06-11 20:46:14 +02:00
b1aa53c1a9 Adds missing comments to BlockPopulatorThread and make end gateways teleport entities back to itself to prevent strange behavior
Because of the teleport change, end gateways work to teleport player,
and end gateways work to the end for vehicles, but vehicles cannot teleport back from the end
2021-03-02 17:55:14 +01:00
44dfa2a10d Greatly refactors gate loading 2021-02-28 21:53:27 +01:00
504ef1b52f Adds remaining missing comments to Portal 2021-02-27 22:50:44 +01:00
da32cf11d1 Fixes some things regarding vehicle teleportation
Adds extra space between the portal and the vehicle if the destination portal is always on
Fixes a bug causing vehicles not being detected soon enough
Fixes boats facing into the portal rather than out from the portal
Fixes boats spawning inside water rather than on top of it if water is in front of a portal
2021-02-27 22:34:10 +01:00
ba64572254 Adds more comments, simplifies some code and improves positioning of teleported large entities, like horses 2021-02-27 21:17:36 +01:00
79703e49af Adds a class which helps with modZ and modX calculations 2021-02-27 21:15:39 +01:00
496b5d9779 Moves sign drawing to a helper class to reduce the complexity of the portal class 2021-02-24 18:12:26 +01:00
378a59586d Heavily simplifies sign drawing and cleans up vehicle teleportation code 2021-02-24 17:48:01 +01:00
2b52759e00 Makes sure teleportation not from a plugin does not trigger vehicle teleportation 2021-02-24 17:45:53 +01:00
4acea17ba3 Fixes boats sometimes not detecting the portal before the player detects the portal 2021-02-23 19:43:49 +01:00
5f685b2460 Fixes some oddities regarding vehicle teleportation
Accounts for size when blocking an entity near a portal from teleporting to the nether
Ignores boats and minecarts when teleporting a vehicle after the player
Makes it easy to get a portal by adjacent entrance for any given range
2021-02-23 19:17:05 +01:00
e42da6d6bd Updates the README with some of the recent changes 2021-02-23 00:41:40 +01:00
681014a431 Improves some code formatting in the portal open method 2021-02-23 00:35:48 +01:00
af693bddd2 Fixes the timing of the block populator thread which caused a delay between opening a gate and it displaying as open 2021-02-23 00:35:18 +01:00
151c242e69 Changes names of some variables and one method to increase readability 2021-02-22 20:36:37 +01:00
e5fef0b16a Moves the EconomyHandler to utility and adds some encapsulation for three of its variables 2021-02-22 20:26:10 +01:00
e665a49f03 Adds missing comments to Gate and changes the matches function from n^2 to n execution time 2021-02-22 20:25:07 +01:00
279ea9d8f0 Fixes some nullpointerexceptions in PlayerEventsListener's onPlayerMove 2021-02-22 20:23:12 +01:00
d26196b8aa Adds some extra explanations to gate layout's description 2021-02-22 18:34:23 +01:00
fb70b8bc75 Splits Gate into Gate, GateLayout and GateHandler, and creates a new portal package with portal related classes 2021-02-22 17:01:47 +01:00
c422cb9ea9 Overrides toString and equals methods of the relative block vector to make it testable 2021-02-22 15:49:44 +01:00
a475e8d8b1 Adds missing comments to the world event listener and adds faster gate unloading
Replaces the clear all + load all with a method which just removes all
relevant portals directly. This should be faster, especially with many gates and worlds
2021-02-20 16:21:18 +01:00
1d642bfcf2 Adds missing comments to the vehicle event listener 2021-02-20 14:59:59 +01:00
1da0f4eddc Adds comments to the plugin event listener 2021-02-20 14:55:23 +01:00
889a9d2cbc Finishes commenting and refactoring the player events listener 2021-02-20 14:42:41 +01:00
1721750aa1 Adds comments and simplifies some of the code
Adds a PortalOption enum to simplify portal options
Adds a BungeeHelper class to collect the bungee-related code
2021-02-20 13:57:04 +01:00
2ae4fc9645 Adds class comment to the stargate tab completer 2021-02-19 12:07:34 +01:00
c912624df1 Adds comments to all custom events
Adds full comments to every class implementing StarGateEvent
Adds another abstract event StargatePlayerEvent which reduces code duplication
2021-02-19 12:06:23 +01:00
5b7f5649b1 Makes a whole lot of changes
Adds some new tests
Improves plugin command handling by using one class for each command
Makes some changes to vehicle teleportation to support horses and pigs, but vehicle teleportation is still buggy and messy
Adds some more missing comments
Adds a wildcard permission and uses built-in permissions some places to avoid checking for three different permissions
2021-02-16 21:58:31 +01:00
df074b9ff5 Adds an entity portal event listener which fixes the infuriating bug which caused empty minecarts to disappear into the nether 2021-02-12 01:35:55 +01:00
42fa6ed8d7 Huge refactoring
Splits Portal into Portal and PortalHandler
Adds EconomyHelper to make messaging of economy string easier
Adds a lot of missing comments
Adds vehicle teleportation again, but it needs a lot of changes to work properly
2021-02-12 00:26:47 +01:00
bd4586e386 Adds annotations to prevent warnings 2021-02-12 00:24:27 +01:00
1719e92494 Moves a bunch of inner classes to their own files 2021-02-11 15:53:54 +01:00
56410a58f8 Improves formatting of custom gate information 2021-02-10 15:12:48 +01:00
ff8f762ea8 Fixes information about water gates and adds a list of all valid buttons 2021-02-10 15:05:41 +01:00
c41429b6e0 Makes default gates load from files rather than being defined in code 2021-02-10 14:32:01 +01:00
d472eab21b Adds two gate types to resources 2021-02-10 14:30:30 +01:00
095e59c65e Makes it easier to use any compatible block as a button 2021-02-10 03:29:28 +01:00
7b9f5a6de5 Adds underwater portal support using any wall coral as a button replacement 2021-02-10 02:20:50 +01:00
e49b94cf9a Adds a class for helping to decide if a material is a wall coral (dead or alive) 2021-02-10 02:19:48 +01:00
a8c0574f3b Adds other authors to plugin.yml 2021-02-10 02:17:49 +01:00
f0a7ff8c47 Adds more BlockLocation tests 2021-02-09 23:25:52 +01:00
32410a82ba Adds some tests for the equals method of BlockLocationTest 2021-02-09 21:12:43 +01:00
b6d18a4217 Adds an extra constructor to Stargate required for testing 2021-02-09 21:12:04 +01:00
68f3bca04f Adds MockBukkit and JUnit dependencies for testing 2021-02-09 21:11:22 +01:00
2bd5bb36cf Updates README to account for recent changes 2021-02-09 20:40:48 +01:00
5a9d70f827 Adds a warning against treating BlockLocation as a Location 2021-02-09 20:38:50 +01:00
a5cf1a7cd3 Improves translation rate of info and error strings 2021-02-09 20:10:17 +01:00
af6a2537b8 Adds more information regarding why economy could not be enabled 2021-02-09 20:09:49 +01:00
cdae2d8f35 Adds language strings related to loading Vault 2021-02-09 20:08:40 +01:00
9ace568047 Fixes garbled text caused by writing, but not reading language files as UTF-8 2021-02-09 20:07:56 +01:00
5a8e8a219e Replaces several static strings with strings from the language files 2021-02-09 18:47:54 +01:00
3521257cb2 Adds some vault related strings to the english language file 2021-02-09 18:46:55 +01:00
b9cbe9ee4c Makes Vault a soft dependency 2021-02-09 18:46:30 +01:00
e702a0d734 Tidies up and comments the economy handler and removes the depreciated method of getting an offline player by username 2021-02-08 14:30:14 +01:00
f97cb32466 Improves messages for the language loader 2021-02-08 14:27:53 +01:00
4f5cb84d02 Improves error handling when unable to load a language and always uses english as backup language to make sure the plugin won't crash when an invalid language is chosen 2021-02-08 05:10:10 +01:00
341a445d16 Fixes language file formats, fixes name of Norwegian Bokmål and adds Norewgian Bokmål 2021-02-08 05:07:35 +01:00
df111c2750 Adds Norwegian translation 2021-02-08 01:54:18 +01:00
6825266a92 Fixes a bug caused by BlockLocation not being able to be instantiated from a location object 2021-02-08 00:35:34 +01:00
6d6a7e52b2 Renames usages of BungeeCoordListener to BungeeCordListener 2021-02-08 00:33:55 +01:00
6e1a69881c Cleans, comments and renames BungeeCordListener 2021-02-08 00:32:58 +01:00
9233776b2c Adds comments to BloxPopulator 2021-02-08 00:32:20 +01:00
27aa0ed29d Removes a function from BlockLocation present in Location which caused an infinite loop 2021-02-08 00:31:53 +01:00
4e3867eae9 Simplifies BlockLocation by making it extend Location 2021-02-07 16:58:33 +01:00
ad2be87404 Cleans and commments the BlockLocation class by storing most of its information as a Location 2021-02-07 16:37:42 +01:00
a268370f52 Updates plugin version to 0.9 2021-02-07 16:34:13 +01:00
c8d82a8575 Removes CommonFunctions as it wasn't used 2021-02-07 16:33:45 +01:00
6ff998ac3b Restructures the plugin and starts work on cleaning and commenting the code 2021-02-07 03:37:25 +01:00
114 changed files with 12103 additions and 5190 deletions

27
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,27 @@
pipeline {
agent any
tools {
jdk 'JDK16'
}
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'mvn clean & mvn validate & mvn compile'
}
}
stage('Test') {
steps {
echo 'Testing...'
sh 'mvn test'
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
sh 'mvn verify -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
}
}

1365
README.md

File diff suppressed because it is too large Load Diff

142
pom.xml
View File

@ -1,60 +1,84 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.TheDgtl</groupId>
<artifactId>Stargate</artifactId>
<version>0.8.0.3</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.2-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<targetPath>net/TheDgtl/Stargate/resources</targetPath>
<directory>src/net/TheDgtl/Stargate/resources</directory>
<includes>
<include>*.txt</include>
</includes>
</resource>
<resource>
<directory>src</directory>
<includes>
<include>*.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.knarcraft</groupId>
<artifactId>Stargate</artifactId>
<version>0.9.0.2</version>
<licenses>
<license>
<name>GNU Lesser General Public License</name>
<url>https://www.gnu.org/licenses/lgpl-3.0.en.html</url>
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.17.1-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.0-M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.17</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>19.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,54 +0,0 @@
# Stargate Configuration File
# Main Stargate config
#
# portal-folder - The folder for storing portals
# gate-folder - The folder for storing gate layouts
# default-gate-network - The default gate network
# destroyexplosion - Whether or not to destroy gates with explosions (Creeper, TNT, etc)
# maxgates - The maximum number of gates allowed on a network - 0 for unlimited
# lang - The language file to load for messages
# destMemory - Whether to remember the cursor location between uses
# ignoreEntrance - Ignore the entrance blocks of a gate when checking. Used to work around snowmen
# handleVehicles - Whether to allow vehicles through gates
# sortLists - Whether to sort network lists alphabetically
# protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material)
# signColor - The color used for drawing signs (Default: BLACK).
# verifyPortals - Whether or not all the non-sign blocks are checked to match the gate layout when a stargate is loaded.
############################
# Stargate economy options #
############################
# 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 gates 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
#################
# Debug options #
#################
# debug - Debug -- Only enable if you have issues, massive console output
# permdebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
portal-folder: plugins/Stargate/portals/
gate-folder: plugins/Stargate/gates/
default-gate-network: central
destroyexplosion: false
maxgates: 0
lang: en
destMemory: false
ignoreEntrance: false
handleVehicles: true
sortLists: false
protectEntrance: false
signColor: BLACK
useeconomy: false
createcost: 0
destroycost: 0
usecost: 0
toowner: false
chargefreedestination: true
freegatesgreen: false
debug: false
permdebug: false
enableBungee: false
verifyPortals: false

View File

@ -0,0 +1,392 @@
package net.knarcraft.stargate;
import net.knarcraft.stargate.command.CommandStarGate;
import net.knarcraft.stargate.command.StarGateTabCompleter;
import net.knarcraft.stargate.config.EconomyConfig;
import net.knarcraft.stargate.config.MessageSender;
import net.knarcraft.stargate.config.StargateConfig;
import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.container.BlockChangeRequest;
import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.listener.BlockEventListener;
import net.knarcraft.stargate.listener.EntityEventListener;
import net.knarcraft.stargate.listener.PlayerEventListener;
import net.knarcraft.stargate.listener.PluginEventListener;
import net.knarcraft.stargate.listener.PortalEventListener;
import net.knarcraft.stargate.listener.TeleportEventListener;
import net.knarcraft.stargate.listener.VehicleEventListener;
import net.knarcraft.stargate.listener.WorldEventListener;
import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.PortalRegistry;
import net.knarcraft.stargate.thread.BlockChangeThread;
import net.knarcraft.stargate.thread.ChunkUnloadThread;
import net.knarcraft.stargate.thread.StarGateThread;
import org.bukkit.Server;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The main class of the Stargate plugin
*/
@SuppressWarnings("unused")
public class Stargate extends JavaPlugin {
//Used for changing gate open/closed material.
private static final Queue<BlockChangeRequest> blockChangeRequestQueue = new LinkedList<>();
private static final Queue<ChunkUnloadRequest> chunkUnloadQueue = new PriorityQueue<>();
private static Logger logger;
private static Stargate stargate;
private static String pluginVersion;
private static PluginManager pluginManager;
private static StargateConfig stargateConfig;
/**
* Empty constructor necessary for Spigot
*/
public Stargate() {
super();
}
/**
* Special constructor used for MockBukkit
*
* @param loader <p>The plugin loader to be used.</p>
* @param descriptionFile <p>The description file to be used.</p>
* @param dataFolder <p>The data folder to be used.</p>
* @param file <p>The file to be used</p>
*/
protected Stargate(JavaPluginLoader loader, PluginDescriptionFile descriptionFile, File dataFolder, File file) {
super(loader, descriptionFile, dataFolder, file);
}
/**
* Gets an instance of this plugin
*
* @return <p>An instance of this plugin, or null if not instantiated</p>
*/
public static Stargate getInstance() {
return stargate;
}
/**
* Adds a block change request to the request queue
*
* @param request <p>The request to add</p>
*/
public static void addBlockChangeRequest(BlockChangeRequest request) {
if (request != null) {
blockChangeRequestQueue.add(request);
}
}
/**
* Gets the queue containing block change requests
*
* @return <p>A block change request queue</p>
*/
public static Queue<BlockChangeRequest> getBlockChangeRequestQueue() {
return blockChangeRequestQueue;
}
/**
* Gets the sender for sending messages to players
*
* @return <p>The sender for sending messages to players</p>
*/
public static MessageSender getMessageSender() {
return stargateConfig.getMessageSender();
}
/**
* Gets the object containing gate configuration values
*
* @return <p>The object containing gate configuration values</p>
*/
public static StargateGateConfig getGateConfig() {
return stargateConfig.getStargateGateConfig();
}
/**
* Gets the version of this plugin
*
* @return <p>This plugin's version</p>
*/
public static String getPluginVersion() {
return pluginVersion;
}
/**
* Gets the logger used for logging to the console
*
* @return <p>The logger</p>
*/
public static Logger getConsoleLogger() {
return logger;
}
/**
* Sends a debug message
*
* @param route <p>The class name/route where something happened</p>
* @param message <p>A message describing what happened</p>
*/
public static void debug(String route, String message) {
if (stargateConfig == null || stargateConfig.isDebuggingEnabled()) {
logger.info("[Stargate::" + route + "] " + message);
} else {
logger.log(Level.FINEST, "[Stargate::" + route + "] " + message);
}
}
/**
* Logs an info message to the console
*
* @param message <p>The message to log</p>
*/
public static void logInfo(String message) {
logger.info(getBackupString("prefix") + message);
}
/**
* Logs a severe error message to the console
*
* @param message <p>The message to log</p>
*/
public static void logSevere(String message) {
log(Level.SEVERE, message);
}
/**
* Logs a warning message to the console
*
* @param message <p>The message to log</p>
*/
public static void logWarning(String message) {
log(Level.WARNING, message);
}
/**
* Logs a message to the console
*
* @param severity <p>The severity of the event triggering the message</p>
* @param message <p>The message to log</p>
*/
private static void log(Level severity, String message) {
logger.log(severity, getBackupString("prefix") + message);
}
/**
* Gets the folder for saving created portals
*
* <p>The returned String path is the full path to the folder</p>
*
* @return <p>The folder for storing the portal database</p>
*/
public static String getPortalFolder() {
return stargateConfig.getPortalFolder();
}
/**
* Gets the folder storing gate files
*
* <p>The returned String path is the full path to the folder</p>
*
* @return <p>The folder storing gate files</p>
*/
public static String getGateFolder() {
return stargateConfig.getGateFolder();
}
/**
* Gets the default network for gates where a network is not specified
*
* @return <p>The default network</p>
*/
public static String getDefaultNetwork() {
return stargateConfig.getStargateGateConfig().getDefaultPortalNetwork();
}
/**
* Gets a translated string given its string key
*
* <p>The name/key is the string before the equals sign in the language files</p>
*
* @param name <p>The name/key of the string to get</p>
* @return <p>The full translated string</p>
*/
public static String getString(String name) {
return stargateConfig.getLanguageLoader().getString(name);
}
/**
* Gets a backup string given its string key
*
* <p>The name/key is the string before the equals sign in the language files</p>
*
* @param name <p>The name/key of the string to get</p>
* @return <p>The full string in the backup language (English)</p>
*/
public static String getBackupString(String name) {
return stargateConfig.getLanguageLoader().getBackupString(name);
}
/**
* Replaces a list of variables in a string in the order they are given
*
* @param input <p>The input containing the variables</p>
* @param search <p>The variables to replace</p>
* @param values <p>The replacement values</p>
* @return <p>The input string with the search values replaced with the given values</p>
*/
public static String replaceVars(String input, String[] search, String[] values) {
if (search.length != values.length) {
throw new IllegalArgumentException("The number of search values and replace values do not match.");
}
for (int i = 0; i < search.length; i++) {
input = replaceVars(input, search[i], values[i]);
}
return input;
}
/**
* Replaces a variable in a string
*
* @param input <p>The input containing the variables</p>
* @param search <p>The variable to replace</p>
* @param value <p>The replacement value</p>
* @return <p>The input string with the search replaced with value</p>
*/
public static String replaceVars(String input, String search, String value) {
return input.replace(search, value);
}
/**
* Gets this plugin's plugin manager
*
* @return <p>A plugin manager</p>
*/
public static PluginManager getPluginManager() {
return pluginManager;
}
/**
* Gets the object containing economy config values
*
* @return <p>The object containing economy config values</p>
*/
public static EconomyConfig getEconomyConfig() {
return stargateConfig.getEconomyConfig();
}
@Override
public void onDisable() {
PortalHandler.closeAllPortals();
PortalRegistry.clearPortals();
stargateConfig.clearManagedWorlds();
getServer().getScheduler().cancelTasks(this);
}
@Override
public void onEnable() {
PluginDescriptionFile pluginDescriptionFile = this.getDescription();
pluginManager = getServer().getPluginManager();
FileConfiguration newConfig = this.getConfig();
logger = Logger.getLogger("Minecraft");
Server server = getServer();
stargate = this;
stargateConfig = new StargateConfig(logger);
stargateConfig.finishSetup();
pluginVersion = pluginDescriptionFile.getVersion();
logger.info(pluginDescriptionFile.getName() + " v." + pluginDescriptionFile.getVersion() + " is enabled.");
//Register events before loading gates to stop weird things from happening.
registerEventListeners();
//Run necessary threads
runThreads();
this.registerCommands();
}
/**
* Starts threads using the bukkit scheduler
*/
private void runThreads() {
BukkitScheduler scheduler = getServer().getScheduler();
scheduler.runTaskTimer(this, new StarGateThread(), 0L, 100L);
scheduler.runTaskTimer(this, new BlockChangeThread(), 0L, 1L);
scheduler.runTaskTimer(this, new ChunkUnloadThread(), 0L, 100L);
}
/**
* Registers all event listeners
*/
private void registerEventListeners() {
pluginManager.registerEvents(new PlayerEventListener(), this);
pluginManager.registerEvents(new BlockEventListener(), this);
pluginManager.registerEvents(new VehicleEventListener(), this);
pluginManager.registerEvents(new EntityEventListener(), this);
pluginManager.registerEvents(new PortalEventListener(), this);
pluginManager.registerEvents(new WorldEventListener(), this);
pluginManager.registerEvents(new PluginEventListener(this), this);
pluginManager.registerEvents(new TeleportEventListener(), this);
}
/**
* Registers a command for this plugin
*/
private void registerCommands() {
PluginCommand stargateCommand = this.getCommand("stargate");
if (stargateCommand != null) {
stargateCommand.setExecutor(new CommandStarGate());
stargateCommand.setTabCompleter(new StarGateTabCompleter());
}
}
/**
* Gets the chunk unload queue containing chunks to unload
*
* @return <p>The chunk unload queue</p>
*/
public static Queue<ChunkUnloadRequest> getChunkUnloadQueue() {
return chunkUnloadQueue;
}
/**
* Adds a new chunk unload request to the chunk unload queue
*
* @param request <p>The new chunk unload request to add</p>
*/
public static void addChunkUnloadRequest(ChunkUnloadRequest request) {
chunkUnloadQueue.removeIf((item) -> item.getChunkToUnload().equals(request.getChunkToUnload()));
chunkUnloadQueue.add(request);
}
/**
* Gets the stargate configuration
*
* @return <p>The stargate configuration</p>
*/
public static StargateConfig getStargateConfig() {
return stargateConfig;
}
}

View File

@ -0,0 +1,31 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
/**
* This command represents the plugin's about command
*/
public class CommandAbout implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
ChatColor textColor = ChatColor.GOLD;
ChatColor highlightColor = ChatColor.GREEN;
commandSender.sendMessage(textColor + "Stargate Plugin originally created by " + highlightColor +
"Drakia" + textColor + ", and revived by " + highlightColor + "EpicKnarvik97");
commandSender.sendMessage(textColor + "Go to " + highlightColor +
"https://git.knarcraft.net/EpicKnarvik97/Stargate " + textColor + "for the official repository");
String author = Stargate.getStargateConfig().getLanguageLoader().getString("author");
if (!author.isEmpty())
commandSender.sendMessage(textColor + "Language created by " + highlightColor + author);
return true;
}
}

View File

@ -0,0 +1,28 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* This command represents the plugin's reload command
*/
public class CommandReload 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.reload")) {
Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied");
return true;
}
}
Stargate.getStargateConfig().reload(commandSender);
return true;
}
}

View File

@ -0,0 +1,34 @@
package net.knarcraft.stargate.command;
import net.knarcraft.stargate.Stargate;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
/**
* This command represents any command which starts with stargate
*
* <p>This prefix command should only be used for commands which are certain to collide with others and which relate to
* the plugin itself, not commands for functions of the plugin.</p>
*/
public class CommandStarGate implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] args) {
if (args.length > 0) {
if (args[0].equalsIgnoreCase("about")) {
return new CommandAbout().onCommand(commandSender, command, s, args);
} else if (args[0].equalsIgnoreCase("reload")) {
return new CommandReload().onCommand(commandSender, command, s, args);
}
return false;
} else {
commandSender.sendMessage(ChatColor.GOLD + "Stargate version " +
ChatColor.GREEN + Stargate.getPluginVersion());
return true;
}
}
}

View File

@ -0,0 +1,31 @@
package net.knarcraft.stargate.command;
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 tab completer for the /stargate (/sg) command
*/
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;
} else {
return new ArrayList<>();
}
}
}

View File

@ -0,0 +1,395 @@
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.utility.PermissionHelper;
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;
/**
* 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;
/**
* Instantiates a new economy config
*
* @param newConfig <p>The file configuration to read values from</p>
*/
public EconomyConfig(FileConfiguration newConfig) {
loadEconomyConfig(newConfig);
}
/**
* Gets the cost of using a gate without a specified cost
*
* @return <p>The gate use cost</p>
*/
public int getDefaultUseCost() {
return useCost;
}
/**
* Gets whether economy is enabled
*
* @return <p>Whether economy is enabled</p>
*/
public boolean isEconomyEnabled() {
return economyEnabled;
}
/**
* Gets the economy object to use for transactions
*
* @return <p>An economy object, or null if economy is disabled or not initialized</p>
*/
public Economy getEconomy() {
return economy;
}
/**
* Gets an instance of the Vault plugin
*
* @return <p>An instance of the Vault plugin, or null if Vault is not loaded</p>
*/
public Plugin getVault() {
return vault;
}
/**
* Disables economy support by clearing relevant values
*/
public void disableEconomy() {
this.economy = null;
this.vault = null;
}
/**
* Gets whether free portals should be marked with green coloring
*
* @return <p>Whether free portals should be green</p>
*/
public boolean drawFreePortalsGreen() {
return freeGatesGreen;
}
/**
* Whether a gate whose destination is a free gate is still charged
*
* <p>If teleporting from a free portal, it's free regardless of destination. If chargeFreeDestination is disabled,
* it's also free to teleport back to the free portal. If chargeFreeDestination is enabled, it's only free to
* teleport back if teleporting from another free portal.</p>
*
* @return <p>Whether to charge for free destinations</p>
*/
public boolean chargeFreeDestination() {
return chargeFreeDestination;
}
/**
* Gets whether payments should be sent to the owner of the used portal
*
* @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;
}
/**
* Gets the cost of creating a gate without a specified cost
*
* @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;
}
/**
* Gets the cost of destroying a gate without a specified cost
*
* @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);
}
/**
* Checks whether the given player can afford the given fee
*
* @param player <p>The player to check</p>
* @param cost <p>The fee to pay</p>
* @return <p>True if the player can afford to pay the fee</p>
*/
public boolean canAffordFee(Player player, int cost) {
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
*
* @param amount <p>The amount to display</p>
* @return <p>A formatted text string describing the amount</p>
*/
public String format(int amount) {
if (economyEnabled) {
return economy.format(amount);
} else {
return "";
}
}
/**
* Sets up economy by initializing vault and the vault economy provider
*
* @param pluginManager <p>The plugin manager to get plugins from</p>
* @return <p>True if economy was enabled</p>
*/
public boolean setupEconomy(PluginManager pluginManager) {
if (!economyEnabled) {
return false;
}
//Check if vault is loaded
Plugin vault = pluginManager.getPlugin("Vault");
if (vault != null && vault.isEnabled()) {
ServicesManager servicesManager = Stargate.getInstance().getServer().getServicesManager();
RegisteredServiceProvider<Economy> economyProvider = servicesManager.getRegistration(Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
this.vault = vault;
return true;
} else {
Stargate.logInfo(Stargate.getString("ecoLoadError"));
}
} else {
Stargate.logInfo(Stargate.getString("vaultLoadError"));
}
economyEnabled = false;
return false;
}
/**
* Gets whether to use economy
*
* @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();
}
/**
* Gets the cost of creating the given gate
*
* @param player <p>The player creating the gate</p>
* @param gate <p>The gate type used</p>
* @return <p>The cost of creating the gate</p>
*/
public int getCreateCost(Player player, Gate gate) {
if (isFree(player, "create")) {
return 0;
} else {
return gate.getCreateCost();
}
}
/**
* Gets the cost of destroying the given gate
*
* @param player <p>The player creating the gate</p>
* @param gate <p>The gate type used</p>
* @return <p>The cost of destroying the gate</p>
*/
public int getDestroyCost(Player player, Gate gate) {
if (isFree(player, "destroy")) {
return 0;
} else {
return gate.getDestroyCost();
}
}
/**
* 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
*
* @param player <p>The player to check</p>
* @param permissionNode <p>The free.permissionNode necessary to allow free gate {action}</p>
* @return <p></p>
*/
private boolean isFree(Player player, String permissionNode) {
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

@ -0,0 +1,255 @@
package net.knarcraft.stargate.config;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.FileHelper;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* This class is responsible for loading all strings which are translated into several languages
*/
public final class LanguageLoader {
private final String languageFolder;
private final Map<String, String> loadedBackupStrings;
private String chosenLanguage;
private Map<String, String> loadedStringTranslations;
/**
* Instantiates a new language loader
*
* <p>This will only load the backup language. Set the chosen language and reload afterwards.</p>
*
* @param languageFolder <p>The folder containing the language files</p>
*/
public LanguageLoader(String languageFolder) {
this.languageFolder = languageFolder;
File testFile = new File(languageFolder, "en.txt");
if (!testFile.exists()) {
if (testFile.getParentFile().mkdirs()) {
Stargate.debug("LanguageLoader", "Created language folder");
}
}
//Load english as backup language in case the chosen language is missing newly added text strings
InputStream inputStream = FileHelper.getInputStreamForInternalFile("/lang/en.txt");
if (inputStream != null) {
loadedBackupStrings = load("en", inputStream);
} else {
loadedBackupStrings = null;
Stargate.getConsoleLogger().severe("[stargate] Error loading backup language. " +
"There may be missing text in-game");
}
}
/**
* Reloads languages from the files on disk
*/
public void reload() {
//Extracts/Updates the language as needed
updateLanguage(chosenLanguage);
loadedStringTranslations = load(chosenLanguage);
}
/**
* Gets the string to display given its name/key
*
* @param name <p>The name/key of the string to display</p>
* @return <p>The string in the user's preferred language</p>
*/
public String getString(String name) {
String value = null;
if (loadedStringTranslations != null) {
value = loadedStringTranslations.get(name);
}
if (value == null) {
value = getBackupString(name);
}
return value;
}
/**
* Gets the string to display given its name/key
*
* @param name <p>The name/key of the string to display</p>
* @return <p>The string in the backup language (English)</p>
*/
public String getBackupString(String name) {
String value = null;
if (loadedBackupStrings != null) {
value = loadedBackupStrings.get(name);
}
if (value == null) {
return "";
}
return value;
}
/**
* Sets the chosen plugin language
*
* @param chosenLanguage <p>The new plugin language</p>
*/
public void setChosenLanguage(String chosenLanguage) {
this.chosenLanguage = chosenLanguage;
}
/**
* Updates files in the plugin directory with contents from the compiled .jar
*
* @param language <p>The language to update</p>
*/
private void updateLanguage(String language) {
Map<String, String> currentLanguageValues = load(language);
InputStream inputStream = getClass().getResourceAsStream("/lang/" + language + ".txt");
if (inputStream == null) {
Stargate.logInfo(String.format("The language %s is not available. Falling back to english, You can add a " +
"custom language by creating a new text file in the lang directory.", language));
Stargate.debug("LanguageLoader::updateLanguage", String.format("Unable to load /lang/%s.txt", language));
return;
}
try {
readChangedLanguageStrings(inputStream, language, currentLanguageValues);
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Reads language strings
*
* @param inputStream <p>The input stream to read from</p>
* @param language <p>The selected language</p>
* @param currentLanguageValues <p>The current values of the loaded/processed language</p>
* @throws IOException <p>if unable to read a language file</p>
*/
private void readChangedLanguageStrings(InputStream inputStream, String language, Map<String,
String> currentLanguageValues) throws IOException {
//Get language values
BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream);
Map<String, String> internalLanguageValues = FileHelper.readKeyValuePairs(bufferedReader);
//If currentLanguageValues is null; the chosen language has not been used before
if (currentLanguageValues == null) {
updateLanguageFile(language, internalLanguageValues, null);
Stargate.logInfo(String.format("Language (%s) has been loaded", language));
return;
}
//If a key is not found in the language file, add the one in the internal file. Must update the external file
if (!internalLanguageValues.keySet().equals(currentLanguageValues.keySet())) {
Map<String, String> newLanguageValues = new HashMap<>();
boolean updateNecessary = false;
for (String key : internalLanguageValues.keySet()) {
if (currentLanguageValues.get(key) == null) {
newLanguageValues.put(key, internalLanguageValues.get(key));
//Found at least one value in the internal file not in the external file. Need to update
updateNecessary = true;
} else {
newLanguageValues.put(key, currentLanguageValues.get(key));
currentLanguageValues.remove(key);
}
}
//Update the file itself
if (updateNecessary) {
updateLanguageFile(language, newLanguageValues, currentLanguageValues);
Stargate.logInfo(String.format("Your language file (%s.txt) has been updated", language));
}
}
}
/**
* Updates the language file for a given language
*
* @param language <p>The language to update</p>
* @param languageStrings <p>The updated language strings</p>
* @param customLanguageStrings <p>Any custom language strings not recognized</p>
* @throws IOException <p>If unable to write to the language file</p>
*/
private void updateLanguageFile(String language, Map<String, String> languageStrings,
Map<String, String> customLanguageStrings) throws IOException {
BufferedWriter bufferedWriter = FileHelper.getBufferedWriterFromString(languageFolder + language + ".txt");
//Output normal Language data
for (String key : languageStrings.keySet()) {
bufferedWriter.write(key + "=" + languageStrings.get(key));
bufferedWriter.newLine();
}
bufferedWriter.newLine();
//Output any custom language strings the user had
if (customLanguageStrings != null) {
for (String key : customLanguageStrings.keySet()) {
bufferedWriter.write(key + "=" + customLanguageStrings.get(key));
bufferedWriter.newLine();
}
}
bufferedWriter.close();
}
/**
* Loads the given language
*
* @param lang <p>The language to load</p>
* @return <p>A mapping between loaded string indexes and the strings to display</p>
*/
private Map<String, String> load(String lang) {
return load(lang, null);
}
/**
* Loads the given language
*
* @param lang <p>The language to load</p>
* @param inputStream <p>An optional input stream to use. Defaults to using a file input stream</p>
* @return <p>A mapping between loaded string indexes and the strings to display</p>
*/
private Map<String, String> load(String lang, InputStream inputStream) {
Map<String, String> strings;
BufferedReader bufferedReader;
try {
if (inputStream == null) {
bufferedReader = FileHelper.getBufferedReaderFromString(languageFolder + lang + ".txt");
} else {
bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream);
}
strings = FileHelper.readKeyValuePairs(bufferedReader);
} catch (Exception e) {
if (Stargate.getStargateConfig().isDebuggingEnabled()) {
Stargate.getConsoleLogger().info("[Stargate] Unable to load language " + lang);
}
return null;
}
return strings;
}
/**
* Prints debug output to the console for checking loaded language strings/translations
*/
public void debug() {
if (loadedStringTranslations != null) {
Set<String> keys = loadedStringTranslations.keySet();
for (String key : keys) {
Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", key + " => " +
loadedStringTranslations.get(key));
}
}
if (loadedBackupStrings == null) {
return;
}
Set<String> keys = loadedBackupStrings.keySet();
for (String key : keys) {
Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", key + " => " +
loadedBackupStrings.get(key));
}
}
}

View File

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

View File

@ -0,0 +1,470 @@
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.thread.BlockChangeThread;
import net.knarcraft.stargate.utility.FileHelper;
import net.knarcraft.stargate.utility.PortalFileHelper;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.messaging.Messenger;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger;
/**
* The stargate config is responsible for keeping track of all configuration values
*/
public final class StargateConfig {
private final Queue<Portal> activePortalsQueue = new ConcurrentLinkedQueue<>();
private final Queue<Portal> openPortalsQueue = new ConcurrentLinkedQueue<>();
private final HashSet<String> managedWorlds = new HashSet<>();
private StargateGateConfig stargateGateConfig;
private MessageSender messageSender;
private final LanguageLoader languageLoader;
private EconomyConfig economyConfig;
private final Logger logger;
private final String dataFolderPath;
private String gateFolder;
private String portalFolder;
private String languageName = "en";
private boolean debuggingEnabled = false;
private boolean permissionDebuggingEnabled = false;
/**
* Instantiates a new stargate config
*
* @param logger <p>The logger to use for logging errors</p>
*/
public StargateConfig(Logger logger) {
this.logger = logger;
dataFolderPath = Stargate.getInstance().getDataFolder().getPath().replaceAll("\\\\", "/");
portalFolder = dataFolderPath + "/portals/";
gateFolder = dataFolderPath + "/gates/";
languageLoader = new LanguageLoader(dataFolderPath + "/lang/");
}
/**
* Finish the config setup by loading languages, gates and portals, and loading economy if vault is loaded
*/
public void finishSetup() {
this.loadConfig();
//Enable the required channels for Bungee support
if (stargateGateConfig.enableBungee()) {
startStopBungeeListener(true);
}
//Set the chosen language and reload the language loader
languageLoader.setChosenLanguage(languageName);
languageLoader.reload();
messageSender = new MessageSender(languageLoader);
if (debuggingEnabled) {
languageLoader.debug();
}
this.createMissingFolders();
this.loadGates();
this.loadAllPortals();
//Set up vault economy if vault has been loaded
setupVaultEconomy();
}
/**
* Gets the queue of open portals
*
* <p>The open portals queue is used to close open portals after some time has passed</p>
*
* @return <p>The open portals queue</p>
*/
public Queue<Portal> getOpenPortalsQueue() {
return openPortalsQueue;
}
/**
* Gets the queue of active portals
*
* <p>The active portals queue is used to de-activate portals after some time has passed</p>
*
* @return <p>The active portals queue</p>
*/
public Queue<Portal> getActivePortalsQueue() {
return activePortalsQueue;
}
/**
* Gets whether debugging is enabled
*
* @return <p>Whether debugging is enabled</p>
*/
public boolean isDebuggingEnabled() {
return debuggingEnabled;
}
/**
* Gets whether permission debugging is enabled
*
* @return <p>Whether permission debugging is enabled</p>
*/
public boolean isPermissionDebuggingEnabled() {
return permissionDebuggingEnabled;
}
/**
* Gets the object containing economy config values
*
* @return <p>The object containing economy config values</p>
*/
public EconomyConfig getEconomyConfig() {
return this.economyConfig;
}
/**
* Reloads all portals and files
*
* @param sender <p>The sender of the reload request</p>
*/
public void reload(CommandSender sender) {
//Unload all saved data
unload();
//Perform all block change requests to prevent mismatch if a gate's open-material changes. Changing the
// closed-material still requires a restart.
BlockChangeRequest firstElement = Stargate.getBlockChangeRequestQueue().peek();
while (firstElement != null) {
BlockChangeThread.pollQueue();
firstElement = Stargate.getBlockChangeRequestQueue().peek();
}
//Store the old enable bungee state in case it changes
boolean oldEnableBungee = stargateGateConfig.enableBungee();
//Load all data
load();
//Enable or disable the required channels for Bungee support
if (oldEnableBungee != stargateGateConfig.enableBungee()) {
startStopBungeeListener(stargateGateConfig.enableBungee());
}
messageSender.sendErrorMessage(sender, languageLoader.getString("reloaded"));
}
/**
* Un-loads all loaded data
*/
private void unload() {
//De-activate all currently active portals
for (Portal activePortal : activePortalsQueue) {
activePortal.getPortalActivator().deactivate();
}
//Force all portals to close
closeAllOpenPortals();
PortalHandler.closeAllPortals();
//Clear queues and lists
activePortalsQueue.clear();
openPortalsQueue.clear();
managedWorlds.clear();
//Clear all loaded portals
PortalRegistry.clearPortals();
//Clear all loaded gates
GateHandler.clearGates();
}
/**
* Clears the set of managed worlds
*/
public void clearManagedWorlds() {
managedWorlds.clear();
}
/**
* Gets a copy of the set of managed worlds
*
* @return <p>The managed worlds</p>
*/
public Set<String> getManagedWorlds() {
return new HashSet<>(managedWorlds);
}
/**
* Adds a world to the managed worlds
*
* @param worldName <p>The name of the world to manage</p>
*/
public void addManagedWorld(String worldName) {
managedWorlds.add(worldName);
}
/**
* Removes a world from the managed worlds
*
* @param worldName <p>The name of the world to stop managing</p>
*/
public void removeManagedWorld(String worldName) {
managedWorlds.remove(worldName);
}
/**
* Loads all necessary data
*/
private void load() {
//Load the config from disk
loadConfig();
//Load all gates
loadGates();
//Load all portals
loadAllPortals();
//Update the language loader in case the loaded language changed
languageLoader.setChosenLanguage(languageName);
languageLoader.reload();
if (debuggingEnabled) {
languageLoader.debug();
}
//Load Economy support if enabled/clear if disabled
reloadEconomy();
}
/**
* Starts the listener for listening to BungeeCord messages
*/
private void startStopBungeeListener(boolean start) {
Messenger messenger = Bukkit.getMessenger();
String bungeeChannel = "BungeeCord";
if (start) {
messenger.registerOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel);
messenger.registerIncomingPluginChannel(Stargate.getInstance(), bungeeChannel, new BungeeCordListener());
} else {
messenger.unregisterIncomingPluginChannel(Stargate.getInstance(), bungeeChannel);
messenger.unregisterOutgoingPluginChannel(Stargate.getInstance(), bungeeChannel);
}
}
/**
* Reloads economy by enabling or disabling it as necessary
*/
private void reloadEconomy() {
EconomyConfig economyConfig = getEconomyConfig();
if (economyConfig.isEconomyEnabled() && economyConfig.getEconomy() == null) {
setupVaultEconomy();
} else if (!economyConfig.isEconomyEnabled()) {
economyConfig.disableEconomy();
}
}
/**
* Forces all open portals to close
*/
public void closeAllOpenPortals() {
for (Portal openPortal : openPortalsQueue) {
openPortal.getPortalOpener().closePortal(false);
}
}
/**
* Loads all config values
*/
public void loadConfig() {
Stargate.getInstance().reloadConfig();
FileConfiguration newConfig = Stargate.getInstance().getConfig();
boolean isMigrating = false;
if (newConfig.getString("lang") != null ||
newConfig.getString("gates.integrity.ignoreEntrance") != null ||
newConfig.getString("ignoreEntrance") != null) {
migrateConfig(newConfig);
isMigrating = true;
}
//Copy missing default values if any values are missing
newConfig.options().copyDefaults(true);
//Get the language name from the config
languageName = newConfig.getString("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");
//If users have an outdated config, assume they also need to update their default gates
if (isMigrating) {
GateHandler.writeDefaultGatesToFolder(gateFolder);
}
//Load all gate config values
stargateGateConfig = new StargateGateConfig(newConfig);
//Load all economy config values
economyConfig = new EconomyConfig(newConfig);
Stargate.getInstance().saveConfig();
}
/**
* Gets the object containing configuration values regarding gates
*
* @return <p>Gets the gate config</p>
*/
public StargateGateConfig getStargateGateConfig() {
return stargateGateConfig;
}
/**
* Loads all available gates
*/
public void loadGates() {
GateHandler.loadGates(gateFolder);
Stargate.logInfo(String.format("Loaded %s gate layouts", GateHandler.getGateCount()));
}
/**
* Changes all configuration values from the old name to the new name
*
* @param newConfig <p>The config to read from and write to</p>
*/
private void migrateConfig(FileConfiguration newConfig) {
//Save the old config just in case something goes wrong
try {
newConfig.save(dataFolderPath + "/config.yml.old");
} catch (IOException e) {
Stargate.debug("Stargate::migrateConfig", "Unable to save old backup and do migration");
e.printStackTrace();
return;
}
//Read all available config migrations
Map<String, String> migrationFields;
try {
migrationFields = FileHelper.readKeyValuePairs(FileHelper.getBufferedReaderFromInputStream(
FileHelper.getInputStreamForInternalFile("/config-migrations.txt")));
} catch (IOException e) {
Stargate.debug("Stargate::migrateConfig", "Unable to load config migration file");
e.printStackTrace();
return;
}
//Replace old config names with the new ones
for (String key : migrationFields.keySet()) {
if (newConfig.contains(key)) {
String newPath = migrationFields.get(key);
Object oldValue = newConfig.get(key);
if (!newPath.trim().isEmpty()) {
newConfig.set(newPath, oldValue);
}
newConfig.set(key, null);
}
}
}
/**
* Loads economy from Vault
*/
private void setupVaultEconomy() {
EconomyConfig economyConfig = getEconomyConfig();
if (economyConfig.setupEconomy(Stargate.getPluginManager()) && economyConfig.getEconomy() != null) {
String vaultVersion = economyConfig.getVault().getDescription().getVersion();
Stargate.logInfo(Stargate.replaceVars(Stargate.getString("vaultLoaded"), "%version%", vaultVersion));
}
}
/**
* Loads all portals in all un-managed worlds
*/
public void loadAllPortals() {
for (World world : Stargate.getInstance().getServer().getWorlds()) {
if (!managedWorlds.contains(world.getName())) {
PortalFileHelper.loadAllPortals(world);
managedWorlds.add(world.getName());
}
}
}
/**
* Creates missing folders
*/
private void createMissingFolders() {
File newPortalDir = new File(portalFolder);
if (!newPortalDir.exists()) {
if (!newPortalDir.mkdirs()) {
logger.severe("Unable to create portal directory");
}
}
File newFile = new File(portalFolder, Stargate.getInstance().getServer().getWorlds().get(0).getName() +
".db");
if (!newFile.exists() && !newFile.getParentFile().exists()) {
if (!newFile.getParentFile().mkdirs()) {
logger.severe("Unable to create portal database folder: " + newFile.getParentFile().getPath());
}
}
}
/**
* Gets the folder all portals are stored in
*
* @return <p>The portal folder</p>
*/
public String getPortalFolder() {
return portalFolder;
}
/**
* Gets the folder storing gate files
*
* <p>The returned String path is the full path to the folder</p>
*
* @return <p>The folder storing gate files</p>
*/
public String getGateFolder() {
return gateFolder;
}
/**
* Gets the sender for sending messages to players
*
* @return <p>The sender for sending messages to players</p>
*/
public MessageSender getMessageSender() {
return messageSender;
}
/**
* Gets the language loader containing translated strings
*
* @return <p>The language loader</p>
*/
public LanguageLoader getLanguageLoader() {
return languageLoader;
}
}

View File

@ -0,0 +1,177 @@
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;
/**
* The Stargate gate config keeps track of all global config values related to gates
*/
public final class StargateGateConfig {
private int maxGatesEachNetwork = 0;
private boolean rememberDestination = false;
private boolean handleVehicles = true;
private boolean sortNetworkDestinations = false;
private boolean protectEntrance = false;
private boolean enableBungee = true;
private boolean verifyPortals = true;
private boolean destroyExplosion = false;
private String defaultGateNetwork = "central";
private static final int activeTime = 10;
private static final int openTime = 10;
/**
* Instantiates a new stargate config
*
* @param newConfig <p>The file configuration to read values from</p>
*/
public StargateGateConfig(FileConfiguration newConfig) {
loadGateConfig(newConfig);
}
/**
* Gets the amount of seconds a portal should be open before automatically closing
*
* @return <p>The open time of a gate</p>
*/
public int getOpenTime() {
return openTime;
}
/**
* Gets the amount of seconds a portal should be active before automatically deactivating
*
* @return <p>The active time of a gate</p>
*/
public int getActiveTime() {
return activeTime;
}
/**
* Gets the maximum number of gates allowed on each network
*
* @return <p>Maximum number of gates for each network</p>
*/
public int maxGatesEachNetwork() {
return maxGatesEachNetwork;
}
/**
* Gets whether a portal's lastly used destination should be remembered
*
* @return <p>Whether a portal's lastly used destination should be remembered</p>
*/
public boolean rememberDestination() {
return rememberDestination;
}
/**
* Gets whether vehicle teleportation should be handled in addition to player teleportation
*
* @return <p>Whether vehicle teleportation should be handled</p>
*/
public boolean handleVehicles() {
return handleVehicles;
}
/**
* Gets whether the list of destinations within a network should be sorted
*
* @return <p>Whether network destinations should be sorted</p>
*/
public boolean sortNetworkDestinations() {
return sortNetworkDestinations;
}
/**
* Gets whether portal entrances should be protected from block breaking
*
* @return <p>Whether portal entrances should be protected</p>
*/
public boolean protectEntrance() {
return protectEntrance;
}
/**
* Gets whether BungeeCord support is enabled
*
* @return <p>Whether bungee support is enabled</p>
*/
public boolean enableBungee() {
return enableBungee;
}
/**
* Gets whether all portals' integrity has to be verified on startup and reload
*
* @return <p>Whether portals need to be verified</p>
*/
public boolean verifyPortals() {
return verifyPortals;
}
/**
* Gets whether portals should be destroyed by nearby explosions
*
* @return <p>Whether portals should be destroyed by explosions</p>
*/
public boolean destroyedByExplosion() {
return destroyExplosion;
}
/**
* Gets the default portal network to use if no other network is given
*
* @return <p>The default portal network</p>
*/
public String getDefaultPortalNetwork() {
return defaultGateNetwork;
}
/**
* 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");
//Functionality
handleVehicles = newConfig.getBoolean("gates.functionality.handleVehicles");
enableBungee = newConfig.getBoolean("gates.functionality.enableBungee");
//Integrity
protectEntrance = newConfig.getBoolean("gates.integrity.protectEntrance");
verifyPortals = newConfig.getBoolean("gates.integrity.verifyPortals");
destroyExplosion = newConfig.getBoolean("gates.integrity.destroyedByExplosion");
//Cosmetic
sortNetworkDestinations = newConfig.getBoolean("gates.cosmetic.sortNetworkDestinations");
rememberDestination = newConfig.getBoolean("gates.cosmetic.rememberDestination");
loadSignColor(newConfig.getString("gates.cosmetic.mainSignColor"),
newConfig.getString("gates.cosmetic.highlightSignColor"));
}
/**
* Loads the correct sign color given a sign color string
*
* @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) {
}
}
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,55 @@
package net.knarcraft.stargate.container;
import org.bukkit.Axis;
import org.bukkit.Material;
/**
* Represents a request for changing a block into another material
*/
public class BlockChangeRequest {
private final BlockLocation blockLocation;
private final Material newMaterial;
private final Axis newAxis;
/**
* Instantiates a new block change request
*
* @param blockLocation <p>The location of the block to change</p>
* @param material <p>The new material to change the block to</p>
* @param axis <p>The new axis to orient the block along</p>
*/
public BlockChangeRequest(BlockLocation blockLocation, Material material, Axis axis) {
this.blockLocation = blockLocation;
newMaterial = material;
newAxis = axis;
}
/**
* Gets the location of the block to change
*
* @return <p>The location of the block</p>
*/
public BlockLocation getBlockLocation() {
return blockLocation;
}
/**
* Gets the material to change the block into
*
* @return <p>The material to change the block into</p>
*/
public Material getMaterial() {
return newMaterial;
}
/**
* Gets the axis to orient the block along
*
* @return <p>The axis to orient the block along</p>
*/
public Axis getAxis() {
return newAxis;
}
}

View File

@ -0,0 +1,227 @@
package net.knarcraft.stargate.container;
import net.knarcraft.stargate.utility.DirectionHelper;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Sign;
import org.bukkit.util.Vector;
/**
* This class represents a block location
*
* <p>The BlockLocation class is basically a Location with some extra functionality.
* Warning: Because of differences in the equals methods between Location and BlockLocation, a BlockLocation which
* equals another BlockLocation does not necessarily equal the same BlockLocation if treated as a Location.</p>
*/
public class BlockLocation extends Location {
private BlockLocation parent = null;
/**
* Creates a new block location
*
* @param world <p>The world the block exists in</p>
* @param x <p>The x coordinate of the block</p>
* @param y <p>The y coordinate of the block</p>
* @param z <p>The z coordinate of the block</p>
*/
public BlockLocation(World world, int x, int y, int z) {
super(world, x, y, z);
}
/**
* Creates a block location from a block
*
* @param block <p>The block to get the location of</p>
*/
public BlockLocation(Block block) {
super(block.getWorld(), block.getX(), block.getY(), block.getZ());
}
/**
* Gets a block location from a string
*
* @param world <p>The world the block exists in</p>
* @param string <p>A comma separated list of x, y and z coordinates as integers</p>
*/
public BlockLocation(World world, String string) {
super(world, Integer.parseInt(string.split(",")[0]), Integer.parseInt(string.split(",")[1]),
Integer.parseInt(string.split(",")[2]));
}
/**
* Creates a new block location in a relative position to this block location
*
* @param x <p>The number of blocks to move in the x-direction</p>
* @param y <p>The number of blocks to move in the y-direction</p>
* @param z <p>The number of blocks to move in the z-direction</p>
* @return <p>A new block location</p>
*/
public BlockLocation makeRelativeBlockLocation(int x, int y, int z) {
return (BlockLocation) this.clone().add(x, y, z);
}
/**
* Creates a location in a relative position to this block location
*
* @param x <p>The number of blocks to move in the x-direction</p>
* @param y <p>The number of blocks to move in the y-direction</p>
* @param z <p>The z position relative to this block's position</p>
* @param yaw <p>The number of blocks to move in the z-direction</p>
* @return <p>A new location</p>
*/
public Location makeRelativeLocation(double x, double y, double z, float yaw) {
Location newLocation = this.clone();
newLocation.setYaw(yaw);
newLocation.setPitch(0);
return newLocation.add(x, y, z);
}
/**
* Gets a location relative to this block location
*
* @param relativeVector <p>The relative block vector describing the relative location</p>
* @param yaw <p>The yaw pointing outwards from a portal (in the relative vector's out direction)</p>
* @return <p>A location relative to this location</p>
*/
public BlockLocation getRelativeLocation(RelativeBlockVector relativeVector, double yaw) {
Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(relativeVector.getRight(),
relativeVector.getDown(), relativeVector.getOut(), yaw);
return makeRelativeBlockLocation(realVector.getBlockX(), realVector.getBlockY(), realVector.getBlockZ());
}
/**
* Makes a location relative to the current location according to given parameters
*
* <p>Out goes in the direction of the yaw. Right goes in the direction of (yaw - 90) degrees.
* Depth goes downwards following the -y direction.</p>
*
* @param right <p>The amount of blocks to go right when looking towards a portal</p>
* @param down <p>The amount of blocks to go downwards when looking towards a portal</p>
* @param out <p>The amount of blocks to go outwards when looking towards a portal</p>
* @param portalYaw <p>The yaw when looking out from the portal</p>
* @return A new location relative to this block location
*/
public Location getRelativeLocation(double right, double down, double out, float portalYaw) {
Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(right, down, out, portalYaw);
return makeRelativeLocation(0.5 + realVector.getBlockX(), realVector.getBlockY(),
0.5 + realVector.getBlockZ(), portalYaw);
}
/**
* Gets the type of block at this block location
*
* @return <p>The block's material type</p>
*/
public Material getType() {
return this.getBlock().getType();
}
/**
* Sets the type of block at this location
*
* @param type <p>The block's new material type</p>
*/
public void setType(Material type) {
this.getBlock().setType(type);
}
/**
* Gets the location representing this block location
*
* @return <p>The location representing this block location</p>
*/
public Location getLocation() {
return this.clone();
}
/**
* Gets this block location's parent block
*
* <p>The parent block is the block the item at this block location is attached to. Usually this is the block a
* sign or wall sign is attached to.</p>
*
* @return <p>This block location's parent block</p>
*/
public Block getParent() {
if (parent == null) {
findParent();
}
if (parent == null) {
return null;
}
return parent.getBlock();
}
/**
* Tries to find the parent block location
*
* <p>If this block location is a sign, the parent is the block location of the block the sign is connected to.</p>
*/
private void findParent() {
int offsetX = 0;
int offsetY = 0;
int offsetZ = 0;
BlockData blockData = getBlock().getBlockData();
if (blockData instanceof Directional) {
//Get the offset of the block "behind" this block
BlockFace facing = ((Directional) blockData).getFacing().getOppositeFace();
offsetX = facing.getModX();
offsetZ = facing.getModZ();
} else if (blockData instanceof Sign) {
//Get offset the block beneath the sign
offsetY = -1;
} else {
return;
}
parent = this.makeRelativeBlockLocation(offsetX, offsetY, offsetZ);
}
@Override
public String toString() {
return String.valueOf(this.getBlockX()) + ',' + this.getBlockY() + ',' + this.getBlockZ();
}
@Override
public int hashCode() {
int result = 18;
result = result * 27 + this.getBlockX();
result = result * 27 + this.getBlockY();
result = result * 27 + this.getBlockZ();
if (this.getWorld() != null) {
result = result * 27 + this.getWorld().getName().hashCode();
}
return result;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
BlockLocation blockLocation = (BlockLocation) object;
World thisWorld = this.getWorld();
World otherWorld = blockLocation.getWorld();
//Check if the worlds of the two locations match
boolean worldsEqual = (thisWorld == null && otherWorld == null) || ((thisWorld != null && otherWorld != null)
&& thisWorld == otherWorld);
//As this is a block location, only the block coordinates are compared
return blockLocation.getBlockX() == this.getBlockX() && blockLocation.getBlockY() == this.getBlockY() &&
blockLocation.getBlockZ() == this.getBlockZ() && worldsEqual;
}
}

View File

@ -0,0 +1,55 @@
package net.knarcraft.stargate.container;
import org.bukkit.Chunk;
import org.jetbrains.annotations.NotNull;
/**
* Represents a requests for the unloading of a chunk which has been previously loaded by the Stargate plugin
*/
public class ChunkUnloadRequest implements Comparable<ChunkUnloadRequest> {
private final Long unloadNanoTime;
private final Chunk chunkToUnload;
/**
* Instantiates a new chunk unloading request
*
* @param chunkToUnload <p>The chunk to request the unloading of</p>
* @param timeUntilUnload <p>The time in milliseconds to wait before unloading the chunk</p>
*/
public ChunkUnloadRequest(Chunk chunkToUnload, Long timeUntilUnload) {
this.chunkToUnload = chunkToUnload;
long systemNanoTime = System.nanoTime();
this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000);
}
/**
* Gets the chunk to unload
*
* @return <p>The chunk to unload</p>
*/
public Chunk getChunkToUnload() {
return this.chunkToUnload;
}
/**
* Gets the system nano time denoting at which time the unload request should be executed
*
* @return <p>The system nano time denoting when the chunk is to be unloaded</p>
*/
public Long getUnloadNanoTime() {
return this.unloadNanoTime;
}
@Override
public String toString() {
return "{" + chunkToUnload + ", " + unloadNanoTime + "}";
}
@Override
public int compareTo(@NotNull ChunkUnloadRequest otherRequest) {
//Prioritize requests based on time until unload
return unloadNanoTime.compareTo(otherRequest.unloadNanoTime);
}
}

View File

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

View File

@ -0,0 +1,127 @@
package net.knarcraft.stargate.container;
/**
* This stores a block location as a vector relative to a position
*
* <p>A relative block vector stores a vector relative to some origin. The origin in this plugin is usually the
* top-left block of a gate (top-left when looking at the side with the sign). The right is therefore the distance
* from the top-left corner towards the top-right corner. Down is the distance from the top-left corner towards the
* bottom-left corner. Out is the distance outward from the gate.</p>
*/
public class RelativeBlockVector {
private final int right;
private final int down;
private final int out;
/**
* A specifier for one of the relative block vector's three properties
*/
public enum Property {
/**
* Specifies the relative block vector's right property
*/
RIGHT,
/**
* Specifies the relative block vector's down property
*/
DOWN,
/**
* Specifies the relative block vector's out property
*/
OUT
}
/**
* Instantiates a new relative block vector
*
* <p>Relative block vectors start from a top-left corner. A yaw is used to orient a relative block vector in the
* "real world".
* In terms of a gate layout, the origin is 0,0. Right is towards the end of the line. Down is to the
* next line. Out is towards the observer.</p>
*
* @param right <p>The distance rightward relative to the origin</p>
* @param down <p>The distance downward relative to the origin</p>
* @param out <p>The distance outward relative to the origin</p>
*/
public RelativeBlockVector(int right, int down, int out) {
this.right = right;
this.down = down;
this.out = out;
}
/**
* Adds a value to one of the properties of this relative block vector
*
* @param propertyToAddTo <p>The property to add to</p>
* @param valueToAdd <p>The value to add to the property (negative to move in the opposite direction)</p>
* @return <p>A new relative block vector with the property altered</p>
*/
public RelativeBlockVector addToVector(Property propertyToAddTo, int valueToAdd) {
switch (propertyToAddTo) {
case RIGHT:
return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out);
case DOWN:
return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out);
case OUT:
return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd);
default:
throw new IllegalArgumentException("Invalid relative block vector property given");
}
}
/**
* Gets a relative block vector which is this inverted (pointing in the opposite direction)
*
* @return <p>This vector, but inverted</p>
*/
public RelativeBlockVector invert() {
return new RelativeBlockVector(-this.right, -this.down, -this.out);
}
/**
* Gets the distance to the right relative to the origin
*
* @return <p>The distance to the right relative to the origin</p>
*/
public int getRight() {
return right;
}
/**
* Gets the distance downward relative to the origin
*
* @return <p>The distance downward relative to the origin</p>
*/
public int getDown() {
return down;
}
/**
* Gets the distance outward relative to the origin
*
* @return <p>The distance outward relative to the origin</p>
*/
public int getOut() {
return out;
}
@Override
public String toString() {
return String.format("(right = %d, down = %d, out = %d)", right, down, out);
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other == null || this.getClass() != other.getClass()) {
return false;
}
RelativeBlockVector otherVector = (RelativeBlockVector) other;
return this.right == otherVector.right && this.down == otherVector.down &&
this.out == otherVector.out;
}
}

View File

@ -0,0 +1,66 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a player attempts to access a stargate
*
* <p>This event is triggered whenever a player enters or activates a stargate. This event can be used to override
* whether the player should be allowed to access the stargate.</p>
*/
@SuppressWarnings("unused")
public class StargateAccessEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private boolean deny;
/**
* Instantiates a new stargate access event
*
* @param player <p>The player involved in the event</p>
* @param portal <p>The portal involved in the event</p>
* @param deny <p>Whether the stargate access should be denied</p>
*/
public StargateAccessEvent(Player player, Portal portal, boolean deny) {
super(portal, player);
this.deny = deny;
}
/**
* Gets whether the player should be denied access
*
* @return <p>Whether the player should be denied access</p>
*/
public boolean getDeny() {
return this.deny;
}
/**
* Sets whether to deny access to the player
*
* @param deny <p>Whether to deny access to the player</p>
*/
public void setDeny(boolean deny) {
this.deny = deny;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,89 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* This event should be called whenever a player activates a stargate
*
* <p>Activation of a stargate happens when a player right-clicks the sign of a stargate.
* This event can be used to overwrite the selected destination, and all destinations the player can see.</p>
*/
@SuppressWarnings("unused")
public class StargateActivateEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private List<String> destinations;
private String destination;
/**
* Instantiates a new stargate activate event
*
* @param portal <p>The activated portal</p>
* @param player <p>The player activating the portal</p>
* @param destinations <p>The destinations available to the player using the portal</p>
* @param destination <p>The currently selected destination</p>
*/
public StargateActivateEvent(Portal portal, Player player, List<String> destinations, String destination) {
super(portal, player);
this.destinations = destinations;
this.destination = destination;
}
/**
* Gets the destinations available for the portal
*
* @return <p>The destinations available for the portal</p>
*/
public List<String> getDestinations() {
return destinations;
}
/**
* Sets the destinations available to the player using the portal
*
* @param destinations <p>The new list of available destinations</p>
*/
public void setDestinations(List<String> destinations) {
this.destinations = destinations;
}
/**
* Gets the selected destination
*
* @return <p>The selected destination</p>
*/
public String getDestination() {
return destination;
}
/**
* Sets (changes) the selected destination
*
* @param destination <p>The new selected destination</p>
*/
public void setDestination(String destination) {
this.destination = destination;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,64 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a stargate is closed
*
* <p>This event can be used to overwrite whether the stargate should be forced to close, even if it's set as
* always-on.</p>
*/
@SuppressWarnings("unused")
public class StargateCloseEvent extends StargateEvent {
private static final HandlerList handlers = new HandlerList();
private boolean force;
/**
* Instantiates a new stargate closing event
*
* @param portal <p>The portal to close</p>
* @param force <p>Whether to force the gate to close, even if set as always-on</p>
*/
public StargateCloseEvent(Portal portal, boolean force) {
super(portal);
this.force = force;
}
/**
* Gets whether to force the stargate to close
*
* @return <p>Whether to force the stargate to close</p>
*/
public boolean getForce() {
return force;
}
/**
* Sets whether the stargate should be forced to close
*
* @param force <p>Whether the stargate should be forced to close</p>
*/
public void setForce(boolean force) {
this.force = force;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,120 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a stargate is created
*
* <p>This event can be used to deny or change the cost of a stargate creation.</p>
*/
@SuppressWarnings("unused")
public class StargateCreateEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private final String[] lines;
private boolean deny;
private String denyReason;
private int cost;
/**
* Instantiates a new stargate creation event
*
* @param player <p>Thg player creating the stargate</p>
* @param portal <p>The created portal</p>
* @param lines <p>The lines of the sign creating the star gate</p>
* @param deny <p>Whether to deny the creation of the new gate</p>
* @param denyReason <p>The reason stargate creation was denied</p>
* @param cost <p>The cost of creating the new star gate</p>
*/
public StargateCreateEvent(Player player, Portal portal, String[] lines, boolean deny, String denyReason, int cost) {
super(portal, player);
this.lines = lines;
this.deny = deny;
this.denyReason = denyReason;
this.cost = cost;
}
/**
* Gets a given line from the sign creating the star gate
*
* @param index <p>The line number to get</p>
* @return <p>The text on the given line</p>
* @throws IndexOutOfBoundsException <p>If given a line index less than zero or above three</p>
*/
public String getLine(int index) throws IndexOutOfBoundsException {
return lines[index];
}
/**
* Gets whether the stargate creation should be denied
*
* @return <p>Whether the stargate creation should be denied</p>
*/
public boolean getDeny() {
return deny;
}
/**
* Sets whether the stargate creation should be denied
*
* @param deny <p>Whether the stargate creation should be denied</p>
*/
public void setDeny(boolean deny) {
this.deny = deny;
}
/**
* Gets the reason the stargate creation was denied
*
* @return <p>The reason the stargate creation was denied</p>
*/
public String getDenyReason() {
return denyReason;
}
/**
* Sets the reason the stargate creation was denied
*
* @param denyReason <p>The new reason why the stargate creation was denied</p>
*/
public void setDenyReason(String denyReason) {
this.denyReason = denyReason;
}
/**
* Gets the cost of creating the stargate
*
* @return <p>The cost of creating the stargate</p>
*/
public int getCost() {
return cost;
}
/**
* Sets the cost of creating the stargate
*
* @param cost <p>The new cost of creating the stargate</p>
*/
public void setCost(int cost) {
this.cost = cost;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,42 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a stargate is deactivated
*
* <p>A deactivation is usually caused by no activity for a set amount of time.
* This event can only be used to listen for de-activation events.</p>
*/
@SuppressWarnings("unused")
public class StargateDeactivateEvent extends StargateEvent {
private static final HandlerList handlers = new HandlerList();
/**
* Instantiates a new stargate deactivation event
*
* @param portal <p>The portal which was deactivated</p>
*/
public StargateDeactivateEvent(Portal portal) {
super(portal);
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,106 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a stargate is destroyed
*
* <p>This event can be used to deny or change the cost of a stargate destruction.</p>
*/
@SuppressWarnings("unused")
public class StargateDestroyEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private boolean deny;
private String denyReason;
private int cost;
/**
* Instantiates a new Stargate Destroy Event
*
* @param portal <p>The destroyed portal</p>
* @param player <p>The player destroying the portal</p>
* @param deny <p>Whether the event should be denied (cancelled)</p>
* @param denyMsg <p>The message to display if the event is denied</p>
* @param cost <p>The cost of destroying the portal</p>
*/
public StargateDestroyEvent(Portal portal, Player player, boolean deny, String denyMsg, int cost) {
super(portal, player);
this.deny = deny;
this.denyReason = denyMsg;
this.cost = cost;
}
/**
* Gets whether this event should be denied
*
* @return <p>Whether this event should be denied</p>
*/
public boolean getDeny() {
return deny;
}
/**
* Sets whether this event should be denied
*
* @param deny <p>Whether this event should be denied</p>
*/
public void setDeny(boolean deny) {
this.deny = deny;
}
/**
* Gets the reason the event was denied
*
* @return <p>The reason the event was denied</p>
*/
public String getDenyReason() {
return denyReason;
}
/**
* Sets the reason the event was denied
*
* @param denyReason <p>The reason the event was denied</p>
*/
public void setDenyReason(String denyReason) {
this.denyReason = denyReason;
}
/**
* Gets the cost of destroying the portal
*
* @return <p>The cost of destroying the portal</p>
*/
public int getCost() {
return cost;
}
/**
* Sets the cost of destroying the portal
*
* @param cost <p>The cost of destroying the portal</p>
*/
public void setCost(int cost) {
this.cost = cost;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,89 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a non-player teleports through a stargate
*
* <p>This event can be used to overwrite the location the entity is teleported to.</p>
*/
@SuppressWarnings("unused")
public class StargateEntityPortalEvent extends StargateEvent {
private static final HandlerList handlers = new HandlerList();
final Entity travellingEntity;
private final Portal destination;
private Location exit;
/**
* Instantiates a new stargate portal event
*
* @param travellingEntity <p>The entity travelling through this portal</p>
* @param portal <p>The portal the entity entered from</p>
* @param destination <p>The destination the entity should exit from</p>
* @param exit <p>The exit location of the destination portal the entity will be teleported to</p>
*/
public StargateEntityPortalEvent(Entity travellingEntity, Portal portal, Portal destination, Location exit) {
super(portal);
this.travellingEntity = travellingEntity;
this.destination = destination;
this.exit = exit;
}
/**
* Return the non-player entity teleporting
*
* @return <p>The non-player teleporting</p>
*/
public Entity getEntity() {
return travellingEntity;
}
/**
* Return the destination portal
*
* @return <p>The destination portal</p>
*/
public Portal getDestination() {
return destination;
}
/**
* Return the location of the players exit point
*
* @return <p>Location of the exit point</p>
*/
public Location getExit() {
return exit;
}
/**
* Set the location of the entity's exit point
*
* @param location <p>The new location of the entity's exit point</p>
*/
public void setExit(Location location) {
this.exit = location;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,45 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
/**
* An abstract event describing any stargate event
*/
@SuppressWarnings("unused")
public abstract class StargateEvent extends Event implements Cancellable {
private final Portal portal;
private boolean cancelled;
/**
* Instantiates a new stargate event
*
* @param portal <p>The portal involved in this stargate event</p>
*/
StargateEvent(Portal portal) {
this.portal = portal;
this.cancelled = false;
}
/**
* Gets the portal involved in this stargate event
*
* @return <p>The portal involved in this stargate event</p>
*/
public Portal getPortal() {
return portal;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -0,0 +1,65 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a player opens a stargate
*
* <p>This event can be used to overwrite whether the stargate should be forced to open, even if it's already open.</p>
*/
@SuppressWarnings({"unused"})
public class StargateOpenEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private boolean force;
/**
* Instantiates a new stargate open event
*
* @param player <p>The player opening the stargate</p>
* @param portal <p>The opened portal</p>
* @param force <p>Whether to force the portal open</p>
*/
public StargateOpenEvent(Player player, Portal portal, boolean force) {
super(portal, player);
this.force = force;
}
/**
* Gets whether the portal should be forced open
*
* @return <p>Whether the portal should be forced open</p>
*/
public boolean getForce() {
return force;
}
/**
* Sets whether the portal should be forced open
*
* @param force <p>Whether the portal should be forced open</p>
*/
public void setForce(boolean force) {
this.force = force;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,33 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.entity.Player;
/**
* An abstract event describing any stargate event where a player is involved
*/
@SuppressWarnings("unused")
public abstract class StargatePlayerEvent extends StargateEvent {
private final Player player;
/**
* Instantiates a new stargate player event
*
* @param portal <p>The portal involved in this stargate event</p>
*/
StargatePlayerEvent(Portal portal, Player player) {
super(portal);
this.player = player;
}
/**
* Gets the player creating the star gate
*
* @return <p>The player creating the star gate</p>
*/
public Player getPlayer() {
return player;
}
}

View File

@ -0,0 +1,78 @@
package net.knarcraft.stargate.event;
import net.knarcraft.stargate.portal.Portal;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* This event should be called whenever a player teleports through a stargate
*
* <p>This event can be used to overwrite the location the player is teleported to.</p>
*/
@SuppressWarnings("unused")
public class StargatePlayerPortalEvent extends StargatePlayerEvent {
private static final HandlerList handlers = new HandlerList();
private final Portal destination;
private Location exit;
/**
* Instantiates a new stargate player portal event
*
* @param player <p>The player teleporting</p>
* @param portal <p>The portal the player entered from</p>
* @param destination <p>The destination the player should exit from</p>
* @param exit <p>The exit location of the destination portal the user will be teleported to</p>
*/
public StargatePlayerPortalEvent(Player player, Portal portal, Portal destination, Location exit) {
super(portal, player);
this.destination = destination;
this.exit = exit;
}
/**
* Return the destination portal
*
* @return <p>The destination portal</p>
*/
public Portal getDestination() {
return destination;
}
/**
* Return the location of the players exit point
*
* @return <p>Location of the exit point</p>
*/
public Location getExit() {
return exit;
}
/**
* Set the location of the player's exit point
*
* @param location <p>The new location of the player's exit point</p>
*/
public void setExit(Location location) {
this.exit = location;
}
/**
* Gets a handler-list containing all event handlers
*
* @return <p>A handler-list with all event handlers</p>
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}

View File

@ -0,0 +1,25 @@
/**
* Events for any plugins wanting to interact with the Stargate plugin
*
* <p>This package contains several events used for interactions with Stargate. All events can be cancelled. For
* several of the events, it is possible to overrule permissions. A general overview of the events' usage:</p>
*
* <ul>
* <li>The StargateAccessEvent is called whenever a player clicks a stargate's sign, and when a player enters a
* Stargate. It can be used to override whether the access should be allowed or denied.</li>
* <li>The StargateActivateEvent is called whenever a player activates a stargate (uses the stargate's sign). It
* can be used to override which destinations are available to the player.</li>
* <li>The StargateCloseEvent is called whenever a stargate is closed. Forcing the stargate closed can be toggled.</li>
* <li>The StargateCreateEvent is called whenever a new stargate is created. Its deny value can be overridden, the
* cost can be changed</li>
* <li>The StargateDeactivateEvent is called whenever a stargate is deactivated.</li>
* <li>The StargateDestroyEvent is called whenever a stargate is destroyed. Its deny value can be overridden or the
* cost can be changed.</li>
* <li>The StargateEntityPortalEvent is called whenever an entity teleports through a stargate. The exit location
* can be changed.</li>
* <li>The StargateOpenEvent is called whenever a stargate is opened. Forcing the stargate open can be toggled.</li>
* <li>The StargatePlayerPortalEvent is called whenever a player teleports through a stargate. The exit location can
* be changed.</li>
* </ul>
*/
package net.knarcraft.stargate.event;

View File

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

View File

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

View File

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

View File

@ -0,0 +1,344 @@
package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate;
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.utility.BungeeHelper;
import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.GameMode;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
/**
* This listener listens to any player-related events related to stargates
*/
@SuppressWarnings("unused")
public class PlayerEventListener implements Listener {
private static long eventTime;
private static PlayerInteractEvent previousEvent;
/**
* This event handler handles detection of any player teleporting through a bungee gate
*
* @param event <p>The event to check for a teleporting player</p>
*/
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
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) {
return;
}
Portal portal = PortalHandler.getBungeePortal(destination);
if (portal == null) {
Stargate.debug("PlayerJoin", "Error fetching destination portal: " + destination);
return;
}
//Teleport the player to the stargate
new PlayerTeleporter(portal, player).teleport(portal, null);
}
/**
* This event handler detects if a player moves into a portal
*
* @param event <p>The player move event which was triggered</p>
*/
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
BlockLocation fromLocation = new BlockLocation(event.getFrom().getBlock());
BlockLocation toLocation = new BlockLocation(event.getTo().getBlock());
Player player = event.getPlayer();
//Check whether the event needs to be considered
if (!isRelevantMoveEvent(event, player, fromLocation, toLocation)) {
return;
}
Portal entrancePortal = PortalHandler.getByEntrance(toLocation);
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
Entity playerVehicle = player.getVehicle();
//If the player is in a vehicle, but vehicle handling is disabled, just ignore the player
if (playerVehicle == null || Stargate.getGateConfig().handleVehicles()) {
teleportPlayer(playerVehicle, player, entrancePortal, destination, event);
}
}
/**
* Teleports a player, also teleports the player's vehicle if it's a living entity
*
* @param playerVehicle <p>The vehicle the player is currently sitting in</p>
* @param player <p>The player which moved</p>
* @param entrancePortal <p>The entrance the player entered</p>
* @param destination <p>The destination of the entrance portal</p>
* @param event <p>The move event causing the teleportation to trigger</p>
*/
private void teleportPlayer(Entity playerVehicle, Player player, Portal entrancePortal, Portal destination,
PlayerMoveEvent event) {
if (playerVehicle instanceof LivingEntity) {
//Make sure any horses are properly tamed
if (playerVehicle instanceof AbstractHorse horse && !horse.isTamed()) {
horse.setTamed(true);
horse.setOwner(player);
}
//Teleport the player's vehicle
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleport(entrancePortal);
} else {
//Just teleport the player like normal
new PlayerTeleporter(destination, player).teleport(entrancePortal, event);
}
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
entrancePortal.getPortalOpener().closePortal(false);
}
/**
* Checks whether a player move event is relevant for this plugin
*
* @param event <p>The player move event to check</p>
* @param player <p>The player which moved</p>
* @param fromLocation <p>The location the player is moving from</p>
* @param toLocation <p>The location the player is moving to</p>
* @return <p>True if the event is relevant</p>
*/
private boolean isRelevantMoveEvent(PlayerMoveEvent event, Player player, BlockLocation fromLocation,
BlockLocation toLocation) {
//Check to see if the player moved to another block
if (fromLocation.equals(toLocation)) {
return false;
}
//Check if the player moved from a portal
Portal entrancePortal = PortalHandler.getByEntrance(toLocation);
if (entrancePortal == null) {
return false;
}
Portal destination = entrancePortal.getPortalActivator().getDestination(player);
//Catch always open portals without a valid destination to prevent the user for being teleported and denied
if (!entrancePortal.getOptions().isBungee() && destination == null) {
return false;
}
//Decide if the anything stops the player from teleport
if (PermissionHelper.playerCannotTeleport(entrancePortal, destination, player, event)) {
return false;
}
//Decide if the user should be teleported to another bungee server
if (entrancePortal.getOptions().isBungee()) {
if (bungeeTeleport(player, entrancePortal, event)) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
return false;
}
return true;
}
/**
* This event handler detects if a player clicks a button or a sign
*
* @param event <p>The player interact event which was triggered</p>
*/
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
Block block = event.getClickedBlock();
if (block == null) {
return;
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
handleRightClickBlock(event, player, block);
} else if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getBlockData() instanceof WallSign) {
//Handle left click of a wall sign
handleSignClick(event, player, block, true);
}
}
/**
* This method handles left- or right-clicking of a sign
*
* @param event <p>The event causing the click</p>
* @param player <p>The player clicking the sign</p>
* @param block <p>The block that was clicked</p>
* @param leftClick <p>Whether the player performed a left click as opposed to a right click</p>
*/
private void handleSignClick(PlayerInteractEvent event, Player player, Block block, boolean leftClick) {
Portal portal = PortalHandler.getByBlock(block);
if (portal == null) {
return;
}
event.setUseInteractedBlock(Event.Result.DENY);
if (leftClick) {
//Cancel event in creative mode to prevent breaking the sign
if (player.getGameMode().equals(GameMode.CREATIVE)) {
event.setCancelled(true);
}
} else {
//Prevent usage of item in the player's hand (placing block and such)
event.setUseItemInHand(Event.Result.DENY);
}
//Check if the user can use the portal
if (cannotAccessPortal(player, portal)) {
return;
}
//Cycle portal destination
if ((!portal.isOpen()) && (!portal.getOptions().isFixed())) {
PortalActivator destinations = portal.getPortalActivator();
if (leftClick) {
destinations.cycleDestination(player, -1);
} else {
destinations.cycleDestination(player);
}
}
}
/**
* Check if a player should be denied from accessing (using) a portal
*
* @param player <p>The player trying to access the portal</p>
* @param portal <p>The portal the player is trying to use</p>
* @return <p>True if the player should be denied</p>
*/
private boolean cannotAccessPortal(Player player, Portal portal) {
boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getNetwork());
if (PermissionHelper.portalAccessDenied(player, portal, deny)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
return true;
}
return false;
}
/**
* This method handles right-clicking of a sign or button belonging to a stargate
*
* @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>
*/
private void handleRightClickBlock(PlayerInteractEvent event, Player player, Block block) {
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;
}
Portal portal = PortalHandler.getByBlock(block);
if (portal == null) {
return;
}
//Prevent the held item from being placed
event.setUseItemInHand(Event.Result.DENY);
event.setUseInteractedBlock(Event.Result.DENY);
//Check if the user can use the portal
if (cannotAccessPortal(player, portal)) {
return;
}
PermissionHelper.openPortal(player, portal);
if (portal.getPortalOpener().isOpenFor(player)) {
event.setUseInteractedBlock(Event.Result.ALLOW);
}
}
}
/**
* This function decides if a right click of a coral is caused by a Spigot bug
*
* <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>
*
* @param event <p>The event causing 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();
}
return false;
}
/**
* Teleports a player to a bungee gate
*
* @param player <p>The player to teleport</p>
* @param entrancePortal <p>The gate the player is entering from</p>
* @param event <p>The event causing the teleportation</p>
* @return <p>True if the teleportation was successful</p>
*/
private boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) {
//Check if bungee is actually enabled
if (!Stargate.getGateConfig().enableBungee()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Teleport the player back to this gate, for sanity's sake
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
//Send the SGBungee packet first, it will be queued by BC if required
if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to send teleportation message");
return false;
}
//Send the connect-message to make the player change server
if (!BungeeHelper.changeServer(player, entrancePortal)) {
Stargate.debug("bungeeTeleport", "Unable to change server");
return false;
}
Stargate.debug("bungeeTeleport", "Teleported player to another server");
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,4 @@
/**
* The root package of the Stargate plugin. Contains the main Stargate.java file
*/
package net.knarcraft.stargate;

View File

@ -0,0 +1,347 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import org.bukkit.Material;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* A gate describes the physical structure of a stargate
*
* <p>While the portal class represents a portal in space, the Gate class represents the physical gate/portal entrance.</p>
*/
public class Gate {
private final String filename;
private final GateLayout layout;
private final Map<Character, Material> characterMaterialMap;
//Gate materials
private final Material portalOpenBlock;
private final Material portalClosedBlock;
private final Material portalButton;
//Economy information
private final int useCost;
private final int createCost;
private final int destroyCost;
private final boolean toOwner;
/**
* Instantiates a new gate
*
* @param filename <p>The name of the gate file, including extension</p>
* @param layout <p>The gate layout defined in the gate file</p>
* @param characterMaterialMap <p>The material types the different layout characters represent</p>
* @param portalOpenBlock <p>The material to set the opening to when the portal is open</p>
* @param portalClosedBlock <p>The material to set the opening to when the portal is closed</p>
* @param portalButton <p>The material to use for the portal button</p>
* @param useCost <p>The cost of using a portal with this gate layout (-1 to disable)</p>
* @param createCost <p>The cost of creating a portal with this gate layout (-1 to disable)</p>
* @param destroyCost <p>The cost of destroying a portal with this gate layout (-1 to disable)</p>
* @param toOwner <p>Whether any payment should go to the owner of the gate, as opposed to just disappearing</p>
*/
public Gate(String filename, GateLayout layout, Map<Character, Material> characterMaterialMap, Material portalOpenBlock,
Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost,
boolean toOwner) {
this.filename = filename;
this.layout = layout;
this.characterMaterialMap = characterMaterialMap;
this.portalOpenBlock = portalOpenBlock;
this.portalClosedBlock = portalClosedBlock;
this.portalButton = portalButton;
this.useCost = useCost;
this.createCost = createCost;
this.destroyCost = destroyCost;
this.toOwner = toOwner;
}
/**
* Gets this gate's layout
*
* @return <p>This gate's layout</p>
*/
public GateLayout getLayout() {
return layout;
}
/**
* Gets the material type used for this gate's control blocks
*
* @return <p>The material type used for control blocks</p>
*/
public Material getControlBlock() {
return characterMaterialMap.get(GateHandler.getControlBlockCharacter());
}
/**
* Gets the filename of this gate's file
*
* @return <p>The filename of this gate's file</p>
*/
public String getFilename() {
return filename;
}
/**
* Gets the block type to use for the opening when a portal using this gate is open
*
* @return <p>The block type to use for the opening when open</p>
*/
public Material getPortalOpenBlock() {
return portalOpenBlock;
}
/**
* Gets the block type to use for the opening when a portal using this gate is closed
*
* @return <p>The block type to use for the opening when closed</p>
*/
public Material getPortalClosedBlock() {
return portalClosedBlock;
}
/**
* Gets the material to use for a portal's button if using this gate type
*
* @return <p>The material to use for a portal's button if using this gate type</p>
*/
public Material getPortalButton() {
return portalButton;
}
/**
* Gets the cost of using a portal with this gate
*
* @return <p>The cost of using a portal with this gate</p>
*/
public int getUseCost() {
return useCost < 0 ? Stargate.getEconomyConfig().getDefaultUseCost() : useCost;
}
/**
* Gets the cost of creating a portal with this gate
*
* @return <p>The cost of creating a portal with this gate</p>
*/
public Integer getCreateCost() {
return createCost < 0 ? Stargate.getEconomyConfig().getDefaultCreateCost() : createCost;
}
/**
* Gets the cost of destroying a portal with this gate
*
* @return <p>The cost of destroying a portal with this gate</p>
*/
public Integer getDestroyCost() {
return destroyCost < 0 ? Stargate.getEconomyConfig().getDefaultDestroyCost() : destroyCost;
}
/**
* Gets whether portal payments go to this portal's owner
*
* @return <p>Whether portal payments go to the owner</p>
*/
public Boolean getToOwner() {
return toOwner;
}
/**
* Checks whether a portal's gate matches this gate type
*
* @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards</p>
* @return <p>True if this gate matches the portal</p>
*/
public boolean matches(BlockLocation topLeft, double yaw) {
return matches(topLeft, yaw, false);
}
/**
* Checks whether a portal's gate matches this gate type
*
* <p>If enabling onCreate, opening blocks with materials AIR and WATER will be allowed even if the gate closed
* material is a different one. If checking and onCreate is not enabled, any inconsistency with opening blocks
* containing AIR or WATER will cause the gate to not match.</p>
*
* @param topLeft <p>The top-left block of the portal's gate</p>
* @param yaw <p>The yaw when looking directly outwards</p>
* @param onCreate <p>Whether this is used in the context of creating a new gate</p>
* @return <p>True if this gate matches the portal</p>
*/
public boolean matches(BlockLocation topLeft, double yaw, boolean onCreate) {
return verifyGateEntrancesMatch(topLeft, yaw, onCreate) && verifyGateBorderMatches(topLeft, yaw);
}
/**
* Verifies that all border blocks of a portal matches this gate type
*
* @param topLeft <p>The top-left block of the portal</p>
* @param yaw <p>The yaw when looking directly outwards from the portal</p>
* @return <p>True if all border blocks of the gate match the layout</p>
*/
private boolean verifyGateBorderMatches(BlockLocation topLeft, double yaw) {
Map<Character, Material> characterMaterialMap = new HashMap<>(this.characterMaterialMap);
for (RelativeBlockVector borderVector : layout.getBorder()) {
int rowIndex = borderVector.getRight();
int lineIndex = borderVector.getDown();
Character key = layout.getLayout()[lineIndex][rowIndex];
Material materialInLayout = characterMaterialMap.get(key);
Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType();
if (materialInLayout == null) {
/* This generally should not happen with proper checking, but just in case a material character is not
* recognized, but still allowed in previous checks, verify the gate as long as all such instances of
* the character correspond to the same material in the physical gate. All subsequent gates will also
* need to match the first verified gate. */
characterMaterialMap.put(key, materialAtLocation);
Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" +
" physical portal.", getFilename(), materialAtLocation));
} else if (materialAtLocation != materialInLayout) {
Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
materialAtLocation, materialInLayout));
return false;
}
}
return true;
}
/**
* Verifies that all entrances of a portal gate matches this gate type
*
* @param topLeft <p>The top-left block of this portal</p>
* @param yaw <p>The yaw when looking directly outwards</p>
* @param onCreate <p>Whether this is used in the context of creating a new gate</p>
* @return <p>Whether this is used in the context of creating a new gate</p>
*/
private boolean verifyGateEntrancesMatch(BlockLocation topLeft, double yaw, boolean onCreate) {
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(topLeft));
for (RelativeBlockVector entranceVector : layout.getEntrances()) {
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(entranceVector));
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)) {
continue;
}
if (type != portalClosedBlock && type != portalOpenBlock) {
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
return false;
}
}
return true;
}
/**
* Saves this gate to a file
*
* <p>This method will save the gate to its filename in the given folder.</p>
*
* @param gateFolder <p>The folder to save the gate file in</p>
*/
public void save(String gateFolder) {
try {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(gateFolder + filename));
//Save main material names
writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name());
writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name());
writeConfig(bufferedWriter, "button", portalButton.name());
//Save the values necessary for economy
saveEconomyValues(bufferedWriter);
//Store material types to use for frame blocks
saveFrameBlockTypes(bufferedWriter);
bufferedWriter.newLine();
//Save the gate layout
layout.saveLayout(bufferedWriter);
bufferedWriter.close();
} catch (IOException ex) {
Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, ex.getMessage()));
}
}
/**
* Saves current economy related values using a buffered writer
*
* @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
private void saveEconomyValues(BufferedWriter bufferedWriter) throws IOException {
//Write use cost if not disabled
if (useCost != -1) {
writeConfig(bufferedWriter, "usecost", useCost);
}
//Write create cost if not disabled
if (createCost != -1) {
writeConfig(bufferedWriter, "createcost", createCost);
}
//Write destroy cost if not disabled
if (destroyCost != -1) {
writeConfig(bufferedWriter, "destroycost", destroyCost);
}
writeConfig(bufferedWriter, "toowner", toOwner);
}
/**
* Saves the types of blocks used for the gate frame/border using a buffered writer
*
* @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
private void saveFrameBlockTypes(BufferedWriter bufferedWriter) throws IOException {
for (Map.Entry<Character, Material> entry : characterMaterialMap.entrySet()) {
Character type = entry.getKey();
Material value = entry.getValue();
//Skip characters not part of the frame
if (type.equals(GateHandler.getAnythingCharacter()) ||
type.equals(GateHandler.getEntranceCharacter()) ||
type.equals(GateHandler.getExitCharacter())) {
continue;
}
bufferedWriter.append(type);
bufferedWriter.append('=');
if (value != null) {
bufferedWriter.append(value.toString());
}
bufferedWriter.newLine();
}
}
/**
* Writes a formatted string to a buffered writer
*
* @param bufferedWriter <p>The buffered writer to write the formatted string to</p>
* @param key <p>The config key to save</p>
* @param value <p>The config value to save</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
private void writeConfig(BufferedWriter bufferedWriter, String key, Object value) throws IOException {
//Figure out the correct formatting to use
String format = "%s=";
if (value instanceof Boolean) {
format += "%b";
} else if (value instanceof Integer) {
format += "%d";
} else if (value instanceof String) {
format += "%s";
} else {
throw new IllegalArgumentException("Unrecognized config value type");
}
bufferedWriter.append(String.format(format, key, value));
bufferedWriter.newLine();
}
}

View File

@ -0,0 +1,327 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.GateReader;
import net.knarcraft.stargate.utility.MaterialHelper;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix;
import static net.knarcraft.stargate.utility.GateReader.readGateConfig;
import static net.knarcraft.stargate.utility.GateReader.readGateFile;
/**
* The gate handler keeps track of all gates
*/
public class GateHandler {
private static final Character ANYTHING = ' ';
private static final Character ENTRANCE = '.';
private static final Character EXIT = '*';
private static final Character CONTROL_BLOCK = '-';
private static final Material defaultPortalBlockOpen = Material.NETHER_PORTAL;
private static final Material defaultPortalBlockClosed = Material.AIR;
private static final Material defaultButton = Material.STONE_BUTTON;
private static final HashMap<String, Gate> gates = new HashMap<>();
private static final HashMap<Material, List<Gate>> controlBlocks = new HashMap<>();
private GateHandler() {
}
/**
* Gets the character used for blocks that are not part of the gate
*
* @return <p>The character used for blocks that are not part of the gate</p>
*/
public static Character getAnythingCharacter() {
return ANYTHING;
}
/**
* Gets the character used for defining the entrance
*
* @return <p>The character used for defining the entrance</p>
*/
public static Character getEntranceCharacter() {
return ENTRANCE;
}
/**
* Gets the character used for defining the exit
*
* @return <p>The character used for defining the exit</p>
*/
public static Character getExitCharacter() {
return EXIT;
}
/**
* Gets the character used for defining control blocks
*
* @return <p>The character used for defining control blocks</p>
*/
public static Character getControlBlockCharacter() {
return CONTROL_BLOCK;
}
/**
* Register a gate into the list of available gates
*
* @param gate <p>The gate to register</p>
*/
private static void registerGate(Gate gate) {
gates.put(gate.getFilename(), gate);
Material blockID = gate.getControlBlock();
if (!controlBlocks.containsKey(blockID)) {
controlBlocks.put(blockID, new ArrayList<>());
}
controlBlocks.get(blockID).add(gate);
}
/**
* Loads a gate from a file
*
* @param file <p>The file containing the gate data</p>
* @return <p>The loaded gate, or null if unable to load the gate</p>
*/
private static Gate loadGate(File file) {
try (Scanner scanner = new Scanner(file)) {
return loadGate(file.getName(), file.getParent(), scanner);
} catch (Exception exception) {
Stargate.logSevere(String.format("Could not load Gate %s - %s", file.getName(), exception.getMessage()));
return null;
}
}
/**
* Loads a gate from a file
*
* @param fileName <p>The name of the file containing the gate data</p>
* @param parentFolder <p>The parent folder of the gate data file</p>
* @param scanner <p>The scanner to use for reading the gate data</p>
* @return <p>The loaded gate or null if unable to load the gate</p>
*/
private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) {
List<List<Character>> design = new ArrayList<>();
Map<Character, Material> characterMaterialMap = new HashMap<>();
Map<String, String> config = new HashMap<>();
Set<Material> frameTypes = new HashSet<>();
//Initialize character to material map
characterMaterialMap.put(ENTRANCE, Material.AIR);
characterMaterialMap.put(EXIT, Material.AIR);
characterMaterialMap.put(ANYTHING, Material.AIR);
//Read the file into appropriate lists and maps
int columns = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config);
if (columns < 0) {
return null;
}
Character[][] layout = generateLayoutMatrix(design, columns);
//Create and validate the new gate
Gate gate = createGate(config, fileName, layout, characterMaterialMap);
if (gate == null) {
return null;
}
//Update gate file in case the format has changed between versions
gate.save(parentFolder + "/");
return gate;
}
/**
* Creates a new gate
*
* @param config <p>The config map to get configuration values from</p>
* @param fileName <p>The name of the saved gate config file</p>
* @param layout <p>The layout matrix of the new gate</p>
* @param characterMaterialMap <p>A map between layout characters and the material to use</p>
* @return <p>A new gate, or null if the config is invalid</p>
*/
private static Gate createGate(Map<String, String> config, String fileName, Character[][] layout,
Map<Character, Material> characterMaterialMap) {
//Read relevant material types
Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen);
Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed);
Material portalButton = readGateConfig(config, fileName, "button", defaultButton);
//Read economy values
int useCost = GateReader.readGateConfig(config, fileName, "usecost");
int createCost = GateReader.readGateConfig(config, fileName, "createcost");
int destroyCost = GateReader.readGateConfig(config, fileName, "destroycost");
boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) :
Stargate.getEconomyConfig().sendPaymentToOwner());
//Create the new gate
Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock,
portalButton, useCost, createCost, destroyCost, toOwner);
if (!validateGate(gate, fileName)) {
return null;
}
return gate;
}
/**
* Validates that a gate is valid
*
* @param gate <p>The gate to validate</p>
* @param fileName <p>The filename of the loaded gate file</p>
* @return <p>True if the gate is valid. False otherwise</p>
*/
private static boolean validateGate(Gate gate, String fileName) {
if (gate.getLayout().getControls().length != 2) {
Stargate.logSevere(String.format("Could not load Gate %s - Gates must have exactly 2 control points.",
fileName));
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));
return false;
}
return true;
}
/**
* Loads all gates inside the given folder
*
* @param gateFolder <p>The folder containing the gates</p>
*/
public static void loadGates(String gateFolder) {
File directory = new File(gateFolder);
File[] files;
if (directory.exists()) {
//Get all files with a .gate extension
files = directory.listFiles((file) -> file.isFile() && file.getName().endsWith(".gate"));
} else {
//Set files to empty list to signal that default gates need to be copied
files = new File[0];
}
if (files == null || files.length == 0) {
//The gates-folder was not found. Assume this is the first run
if (directory.mkdir()) {
writeDefaultGatesToFolder(gateFolder);
}
} else {
//Load and register the corresponding gate for each file
for (File file : files) {
Gate gate = loadGate(file);
if (gate != null) {
registerGate(gate);
}
}
}
}
/**
* Writes the default gates to the given folder
*
* @param gateFolder <p>The folder containing gate config files</p>
*/
public static void writeDefaultGatesToFolder(String gateFolder) {
loadGateFromJar("nethergate.gate", gateFolder);
loadGateFromJar("watergate.gate", gateFolder);
loadGateFromJar("endgate.gate", gateFolder);
}
/**
* Loads the given gate file from within the Jar's resources directory
*
* @param gateFile <p>The name of the gate file</p>
* @param gateFolder <p>The folder containing gates</p>
*/
private static void loadGateFromJar(String gateFile, String gateFolder) {
//Get an input stream for the internal file
InputStream gateFileStream = Gate.class.getResourceAsStream("/gates/" + gateFile);
if (gateFileStream != null) {
Scanner scanner = new Scanner(gateFileStream);
//Load and register the gate
Gate gate = loadGate(gateFile, gateFolder, scanner);
if (gate != null) {
registerGate(gate);
}
}
}
/**
* Gets the gates with the given control block
*
* <p>The control block is the block type where the sign should be placed. It is used to decide whether a user
* is creating a new portal.</p>
*
* @param block <p>The control block to check</p>
* @return <p>A list of gates using the given control block</p>
*/
public static Gate[] getGatesByControlBlock(Block block) {
return getGatesByControlBlock(block.getType());
}
/**
* Gets the gates with the given control block
*
* <p>The control block is the block type where the sign should be placed. It is used to decide whether a user
* is creating a new portal.</p>
*
* @param type <p>The type of the control block to check</p>
* @return <p>A list of gates using the given material for control block</p>
*/
public static Gate[] getGatesByControlBlock(Material type) {
Gate[] result = new Gate[0];
List<Gate> lookup = controlBlocks.get(type);
if (lookup != null) {
result = lookup.toArray(result);
}
return result;
}
/**
* Gets a portal given its filename
*
* @param fileName <p>The filename of the gate to get</p>
* @return <p>The gate with the given filename</p>
*/
public static Gate getGateByName(String fileName) {
return gates.get(fileName);
}
/**
* Gets the number of loaded gate configurations
*
* @return <p>The number of loaded gate configurations</p>
*/
public static int getGateCount() {
return gates.size();
}
/**
* Clears all loaded gates and control blocks
*/
public static void clearGates() {
gates.clear();
controlBlocks.clear();
}
}

View File

@ -0,0 +1,209 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.container.RelativeBlockVector;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* The gate layout describes where every part of the gate should be
*
* <p>The gate layout parses a layout described by a Character matrix and stores the different parts of the gate as
* relative block vectors. All relative vectors has an origin in the top-left block when looking at the gate's front
* (the side with the sign). The origin of the relative vectors can also be seen as 0,0 in the character matrix.</p>
*/
public class GateLayout {
private final Character[][] layout;
private final List<RelativeBlockVector> exits = new ArrayList<>();
private RelativeBlockVector[] entrances = new RelativeBlockVector[0];
private RelativeBlockVector[] border = new RelativeBlockVector[0];
private RelativeBlockVector[] controls = new RelativeBlockVector[0];
private RelativeBlockVector exitBlock = null;
/**
* Instantiates a new gate layout
*
* @param layout <p>A character matrix describing the layout</p>
*/
public GateLayout(Character[][] layout) {
this.layout = layout;
readLayout();
}
/**
* Gets the character array describing this layout
*
* @return <p>The character array describing this layout</p>
*/
public Character[][] getLayout() {
return this.layout;
}
/**
* Gets the locations of all entrances for this gate
*
* <p>Entrances contain both the portal entrance blocks and the portal exit blocks.</p>
*
* @return <p>The locations of entrances for this gate</p>
*/
public RelativeBlockVector[] getEntrances() {
return entrances;
}
/**
* Gets the locations of border blocks for the gate described by this layout
*
* <p>A border block is basically any block of the frame. In terms of the nether gate, the border blocks are every
* block of the gate that's not air when the gate is closed. The sign and button are not border blocks.</p>
*
* @return <p>The locations of border blocks for this gate</p>
*/
public RelativeBlockVector[] getBorder() {
return border;
}
/**
* Gets the exit block defined in the layout
*
* @return <p>The exit block defined in the layout</p>
*/
public RelativeBlockVector getExit() {
return exitBlock;
}
/**
* Gets all possible exit locations defined in the layout
*
* <p>This returns all blocks usable as exits. This basically means it returns the lowest block in each opening of
* the gate layout.</p>
*
* @return <p>All possible exits</p>
*/
public List<RelativeBlockVector> getExits() {
return exits;
}
/**
* Gets the locations of the control blocks for this gate
*
* <p>The control blocks are the blocks where a sign can be placed to create a portal. The control block without a
* sign will be used for the button if necessary. There will always be exactly two control blocks.</p>
*
* @return <p>The locations of the control blocks for this gate</p>
*/
public RelativeBlockVector[] getControls() {
return controls;
}
/**
* Saves the gate layout using a buffered writer
*
* @param bufferedWriter <p>The buffered writer to write to</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
public void saveLayout(BufferedWriter bufferedWriter) throws IOException {
for (Character[] line : this.layout) {
for (Character character : line) {
bufferedWriter.append(character);
}
bufferedWriter.newLine();
}
}
/**
* Reads the layout and stores key information
*
* <p>This methods reads the layout and stores exits, entrances, border blocks and control blocks.</p>
*/
private void readLayout() {
List<RelativeBlockVector> entranceList = new ArrayList<>();
List<RelativeBlockVector> borderList = new ArrayList<>();
List<RelativeBlockVector> controlList = new ArrayList<>();
readLayout(controlList, entranceList, borderList);
this.entrances = entranceList.toArray(this.entrances);
this.border = borderList.toArray(this.border);
this.controls = controlList.toArray(this.controls);
}
/**
* Reads the given layout matrix, filling in the given lists of relative block vectors
*
* @param controlList <p>The list of control blocks to save to</p>
* @param entranceList <p>The list of entrances to save to</p>
* @param borderList <p>The list of border blocks to save to</p>
*/
private void readLayout(List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList,
List<RelativeBlockVector> borderList) {
//Store the lowest opening for each column
int[] exitDepths = new int[layout[0].length];
//A row is the same as one line in the gate file
int lineCount = layout.length;
for (int rowIndex = 0; rowIndex < lineCount; rowIndex++) {
Character[] row = layout[rowIndex];
int rowSize = row.length;
for (int columnIndex = 0; columnIndex < rowSize; columnIndex++) {
Character key = row[columnIndex];
parseLayoutCharacter(key, columnIndex, rowIndex, exitDepths, controlList, entranceList, borderList);
}
}
//Generate all possible exits
for (int x = 0; x < exitDepths.length; x++) {
//Ignore invalid exits
if (exitDepths[x] > 0) {
this.exits.add(new RelativeBlockVector(x, exitDepths[x], 0));
}
}
}
/**
* Parses one character of the layout
*
* @param key <p>The read character</p>
* @param columnIndex <p>The column containing the read character</p>
* @param rowIndex <p>The row containing the read character</p>
* @param exitDepths <p>The list of exit depths to save to</p>
* @param controlList <p>The list of control blocks to save to</p>
* @param entranceList <p>The list of entrances to save to</p>
* @param borderList <p>The list of border blocks to save to</p>
*/
private void parseLayoutCharacter(Character key, int columnIndex, int rowIndex, int[] exitDepths,
List<RelativeBlockVector> controlList, List<RelativeBlockVector> entranceList,
List<RelativeBlockVector> borderList) {
//Add control blocks to the control block list
if (key.equals(GateHandler.getControlBlockCharacter())) {
controlList.add(new RelativeBlockVector(columnIndex, rowIndex, 0));
}
if (isOpening(key)) {
//Register entrance
entranceList.add(new RelativeBlockVector(columnIndex, rowIndex, 0));
//Overwrite the lowest exit location for this column/x-coordinate
exitDepths[columnIndex] = rowIndex;
//Register exit if found
if (key.equals(GateHandler.getExitCharacter())) {
this.exitBlock = new RelativeBlockVector(columnIndex, rowIndex, 0);
}
} else if (!key.equals(GateHandler.getAnythingCharacter())) {
//Register border block
borderList.add(new RelativeBlockVector(columnIndex, rowIndex, 0));
}
}
/**
* Checks whether the given character represents a gate opening
*
* @param character <p>The character to check</p>
* @return <p>True if the character represents an opening</p>
*/
private boolean isOpening(Character character) {
return character.equals(GateHandler.getEntranceCharacter()) || character.equals(GateHandler.getExitCharacter());
}
}

View File

@ -0,0 +1,79 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargatePlayerPortalEvent;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
/**
* The portal teleporter takes care of the actual portal teleportation for any players
*/
public class PlayerTeleporter extends Teleporter {
private final Player player;
/**
* Instantiates a new player teleporter
*
* @param portal <p>The portal which is the target of the teleportation</p>
* @param player <p>The teleporting player</p>
*/
public PlayerTeleporter(Portal portal, Player player) {
super(portal);
this.player = player;
}
/**
* Teleports a player to this teleporter's portal
*
* @param origin <p>The portal the player teleports from</p>
* @param event <p>The player move event triggering the event</p>
*/
public void teleport(Portal origin, PlayerMoveEvent event) {
Location traveller = player.getLocation();
Location exit = getExit(player, traveller);
//Rotate the player to face out from the portal
adjustRotation(exit);
//Call the StargatePlayerPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
exit = triggerPlayerPortalEvent(origin, exit, event);
if (exit == null) {
return;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
//If no event is passed in, assume it's a teleport, and act as such
if (event == null) {
player.teleport(exit);
} else {
//The new method to teleport in a move event is set the "to" field.
event.setTo(exit);
}
}
/**
* Triggers the player portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param exit <p>The exit location to teleport the player to</p>
* @param event <p>The player move event which triggered the teleportation</p>
* @return <p>The location the player should be teleported to, or null if the event was cancelled</p>
*/
private Location triggerPlayerPortalEvent(Portal origin, Location exit, PlayerMoveEvent event) {
StargatePlayerPortalEvent stargatePlayerPortalEvent = new StargatePlayerPortalEvent(player, origin, portal, exit);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargatePlayerPortalEvent);
//Teleport is cancelled. Teleport the player back to where it came from
if (stargatePlayerPortalEvent.isCancelled()) {
new PlayerTeleporter(origin, player).teleport(origin, event);
return null;
}
return stargatePlayerPortalEvent.getExit();
}
}

View File

@ -0,0 +1,289 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.Map;
/**
* This class represents a portal in space which points to one or several portals
*/
public class Portal {
private final String name;
private final String network;
private final PortalOwner portalOwner;
private final PortalOptions options;
private final PortalOpener portalOpener;
private final PortalLocation location;
private final PortalSignDrawer signDrawer;
private final PortalStructure structure;
private final PortalActivator portalActivator;
/**
* Instantiates a new portal
*
* @param portalLocation <p>Object containing locations of all relevant blocks</p>
* @param button <p>The location of the portal's open button</p>
* @param destination <p>The destination defined on the sign's destination line. "" for non-fixed gates</p>
* @param name <p>The name of the portal defined on the sign's first line</p>
* @param network <p>The network the portal belongs to, defined on the sign's third</p>
* @param gate <p>The gate type to use for this portal</p>
* @param portalOwner <p>The portal's owner</p>
* @param options <p>A map containing all possible portal options, with true for the ones enabled</p>
*/
public Portal(PortalLocation portalLocation, BlockLocation button, String destination, String name, String network,
Gate gate, PortalOwner portalOwner, Map<PortalOption, Boolean> options) {
this.location = portalLocation;
this.network = network;
this.name = name;
this.portalOwner = portalOwner;
this.options = new PortalOptions(options, destination.length() > 0);
this.signDrawer = new PortalSignDrawer(this);
this.portalOpener = new PortalOpener(this, destination);
this.structure = new PortalStructure(this, gate, button);
this.portalActivator = portalOpener.getPortalActivator();
}
/**
* Gets the location data for this portal
*
* @return <p>This portal's location data</p>
*/
public PortalLocation getLocation() {
return this.location;
}
/**
* Gets the structure of this portal
*
* <p>The structure contains information about the portal's gate, button and real locations of frames and
* entrances. The structure is also responsible for verifying built StarGates to make sure they match the gate.</p>
*
* @return <p>This portal's structure</p>
*/
public PortalStructure getStructure() {
return this.structure;
}
/**
* Gets this portal's activator
*
* <p>The activator is responsible for activating/de-activating the portal and contains information about
* available destinations and which player activated the portal.</p>
*
* @return <p>This portal's activator</p>
*/
public PortalActivator getPortalActivator() {
return this.portalActivator;
}
/**
* Re-draws the sign on this portal
*/
public void drawSign() {
this.signDrawer.drawSign();
}
/**
* Gets the portal options for this portal
*
* @return <p>This portal's portal options</p>
*/
public PortalOptions getOptions() {
return this.options;
}
/**
* Gets whether this portal is currently open
*
* @return <p>Whether this portal is open</p>
*/
public boolean isOpen() {
return portalOpener.isOpen();
}
/**
* Gets the player currently using this portal
*
* @return <p>The player currently using this portal</p>
*/
public Player getActivePlayer() {
return portalActivator.getActivePlayer();
}
/**
* Gets the network this portal belongs to
*
* @return <p>The network this portal belongs to</p>
*/
public String getNetwork() {
return network;
}
/**
* Gets the time this portal was triggered (activated/opened)
*
* <p>The time is given in the equivalent of a Unix timestamp. It's used to decide when a portal times out and
* automatically closes/deactivates.</p>
*
* @return <p>The time this portal was triggered (activated/opened)</p>
*/
public long getTriggeredTime() {
return portalOpener.getTriggeredTime();
}
/**
* Gets the name of this portal
*
* @return <p>The name of this portal</p>
*/
public String getName() {
return name;
}
/**
* Gets the portal opener used by this portal
*
* <p>The portal opener is responsible for opening and closing this portal.</p>
*
* @return <p>This portal's portal opener</p>
*/
public PortalOpener getPortalOpener() {
return portalOpener;
}
/**
* Gets the name of this portal's destination portal
*
* @return <p>The name of this portal's destination portal</p>
*/
public String getDestinationName() {
return portalOpener.getPortalActivator().getDestinationName();
}
/**
* Gets the gate type used by this portal
*
* @return <p>The gate type used by this portal</p>
*/
public Gate getGate() {
return structure.getGate();
}
/**
* Gets this portal's owner
*
* <p>The owner is the player which created the portal.</p>
*
* @return <p>This portal's owner</p>
*/
public PortalOwner getOwner() {
return portalOwner;
}
/**
* Checks whether a given player is the owner of this portal
*
* @param player <p>The player to check</p>
* @return <p>True if the player is the owner of this portal</p>
*/
public boolean isOwner(Player player) {
if (this.portalOwner.getUUID() != null) {
return player.getUniqueId().compareTo(this.portalOwner.getUUID()) == 0;
} else {
return player.getName().equalsIgnoreCase(this.portalOwner.getName());
}
}
/**
* Gets the world this portal belongs to
*
* @return <p>The world this portal belongs to</p>
*/
public World getWorld() {
return location.getWorld();
}
/**
* Gets the location of this portal's sign
*
* @return <p>The location of this portal's sign</p>
*/
public BlockLocation getSignLocation() {
return this.location.getSignLocation();
}
/**
* Gets the rotation (yaw) of this portal
*
* <p>The yaw is used to calculate all kinds of directions. See DirectionHelper to see how the yaw is used to
* calculate to/from other direction types.</p>
*
* @return <p>The rotation (yaw) of this portal</p>
*/
public float getYaw() {
return this.location.getYaw();
}
/**
* Gets the location of the top-left block of the portal
*
* @return <p>The location of the top-left portal block</p>
*/
public BlockLocation getTopLeft() {
return this.location.getTopLeft();
}
/**
* Gets the block at the given location relative to this portal's top-left block
*
* @param vector <p>The relative block vector explaining the position of the block</p>
* @return <p>The block at the given relative position</p>
*/
public BlockLocation getBlockAt(RelativeBlockVector vector) {
return getTopLeft().getRelativeLocation(vector, getYaw());
}
@Override
public String toString() {
return String.format("Portal [id=%s, network=%s name=%s, type=%s]", getSignLocation(), network, name,
structure.getGate().getFilename());
}
@Override
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());
return result;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
Portal other = (Portal) object;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equalsIgnoreCase(other.name)) {
return false;
}
//If none of the portals have a name, check if the network is the same
if (network == null) {
return other.network == null;
} else {
return network.equalsIgnoreCase(other.network);
}
}
}

View File

@ -0,0 +1,286 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateActivateEvent;
import net.knarcraft.stargate.event.StargateDeactivateEvent;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* The portal activator activates/de-activates portals and keeps track of a portal's destinations
*
* <p>The portal activator is responsible for activating/de-activating the portal and contains information about
* available destinations and which player activated the portal.</p>
*/
public class PortalActivator {
private final Portal portal;
private final PortalOpener opener;
private List<String> destinations = new ArrayList<>();
private String destination;
private String lastDestination = "";
private Player activePlayer;
/**
* Instantiates a new portal destinations object
*
* @param portal <p>The portal which this this object stores destinations for</p>
* @param portalOpener <p>The portal opener to trigger when the activation causes the portal to open</p>
* @param destination <p>The fixed destination specified on the portal's sign</p>
*/
public PortalActivator(Portal portal, PortalOpener portalOpener, String destination) {
this.portal = portal;
this.opener = portalOpener;
this.destination = destination;
}
/**
* Gets the player which this activator's portal is currently activated for
*
* @return <p>The player this activator's portal is currently activated for</p>
*/
public Player getActivePlayer() {
return activePlayer;
}
/**
* Gets the available portal destinations
*
* @return <p>The available portal destinations</p>
*/
public List<String> getDestinations() {
return new ArrayList<>(this.destinations);
}
/**
* Gets the portal destination given a player
*
* @param player <p>Used for random gates to determine which destinations are available</p>
* @return <p>The destination portal the player should teleport to</p>
*/
public Portal getDestination(Player player) {
String portalNetwork = portal.getNetwork();
if (portal.getOptions().isRandom()) {
//Find possible destinations
List<String> destinations = PortalHandler.getDestinations(portal, player, portalNetwork);
if (destinations.size() == 0) {
return null;
}
//Get one random destination
String destination = destinations.get((new Random()).nextInt(destinations.size()));
return PortalHandler.getByName(destination, portalNetwork);
} else {
//Just return the normal fixed destination
return PortalHandler.getByName(destination, portalNetwork);
}
}
/**
* Gets the portal's destination
*
* <p>For random portals, getDestination must be given a player to decide which destinations are valid. Without a
* player, or with a null player, behavior is only defined for a non-random gate.</p>
*
* @return <p>The portal destination</p>
*/
public Portal getDestination() {
return getDestination(null);
}
/**
* Sets the destination of this portal activator's portal
*
* @param destination <p>The new destination of this portal activator's portal</p>
*/
public void setDestination(Portal destination) {
setDestination(destination.getName());
}
/**
* Sets the destination of this portal activator's portal
*
* @param destination <p>The new destination of this portal activator's portal</p>
*/
public void setDestination(String destination) {
this.destination = destination;
}
/**
* Gets the name of the selected destination
*
* @return <p>The name of the selected destination</p>
*/
public String getDestinationName() {
return destination;
}
/**
* Activates this activator's portal for the given player
*
* @param player <p>The player to activate the portal for</p>
* @return <p>True if the portal was activated</p>
*/
boolean activate(Player player) {
//Clear previous destination data
this.destination = "";
this.destinations.clear();
//Adds the active gate to the active queue to allow it to be remotely deactivated
Stargate.getStargateConfig().getActivePortalsQueue().add(portal);
//Set the given player as the active player
activePlayer = player;
String network = portal.getNetwork();
destinations = PortalHandler.getDestinations(portal, player, network);
//Sort destinations if enabled
if (Stargate.getGateConfig().sortNetworkDestinations()) {
Collections.sort(destinations);
}
//Select last used destination if remember destination is enabled
if (Stargate.getGateConfig().rememberDestination() && !lastDestination.isEmpty() &&
destinations.contains(lastDestination)) {
destination = lastDestination;
}
//Trigger an activation event to allow the cancellation to be cancelled
return triggerStargateActivationEvent(player);
}
/**
* Triggers a stargate activation event to allow other plugins to cancel the activation
*
* <p>The event may also end up changing destinations.</p>
*
* @param player <p>The player trying to activate this activator's portal</p>
* @return <p>True if the portal was activated. False otherwise</p>
*/
private boolean triggerStargateActivationEvent(Player player) {
StargateActivateEvent event = new StargateActivateEvent(portal, player, destinations, destination);
Stargate.getInstance().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
Stargate.getStargateConfig().getActivePortalsQueue().remove(portal);
return false;
}
//Update destinations in case they changed, and update the sign
destination = event.getDestination();
destinations = event.getDestinations();
portal.drawSign();
return true;
}
/**
* Deactivates this portal
*/
public void deactivate() {
//Trigger a stargate deactivate event to allow other plugins to cancel the event
StargateDeactivateEvent event = new StargateDeactivateEvent(portal);
Stargate.getInstance().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
//Un-mark the portal as activated
Stargate.getStargateConfig().getActivePortalsQueue().remove(portal);
//Fixed portals are active by definition, but should never be de-activated
if (portal.getOptions().isFixed()) {
return;
}
//Clear destinations and the active player before re-drawing the sign to show that it's deactivated
destinations.clear();
destination = "";
activePlayer = null;
portal.drawSign();
}
/**
* Gets whether this portal activator's portal is active
*
* @return <p>Whether this portal activator's portal is active</p>
*/
public boolean isActive() {
return portal.getOptions().isFixed() || (destinations.size() > 0);
}
/**
* Cycles destination for a non-fixed gate by one forwards step
*
* @param player <p>The player to cycle the gate for</p>
*/
public void cycleDestination(Player player) {
cycleDestination(player, 1);
}
/**
* Cycles destination for a non-fixed gate
*
* @param player <p>The player cycling destinations</p>
* @param direction <p>The direction of the cycle (+1 for next, -1 for previous)</p>
*/
public void cycleDestination(Player player, int direction) {
//Only allow going exactly one step in either direction
if (direction != 1 && direction != -1) {
throw new IllegalArgumentException("The destination direction must be 1 or -1.");
}
boolean activate = false;
if (!isActive() || getActivePlayer() != player) {
//If not active or not active for the given player, and the activation is denied, just abort
if (!activate(player)) {
return;
}
activate = true;
Stargate.debug("cycleDestination", "Network Size: " +
PortalHandler.getNetwork(portal.getNetwork()).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"));
return;
}
//Cycle if destination remembering is disabled, if the portal was already active, or it has no last destination
if (!Stargate.getGateConfig().rememberDestination() || !activate || lastDestination.isEmpty()) {
cycleDestination(direction);
}
//Update the activated time to allow it to be deactivated after a timeout, and re-draw the sign to show the
// selected destination
opener.setTriggeredTime(System.currentTimeMillis() / 1000);
portal.drawSign();
}
/**
* Performs the actual destination cycling with no input checks
*
* @param direction <p>The direction of the cycle (+1 for next, -1 for previous)</p>
*/
private void cycleDestination(int direction) {
int index = destinations.indexOf(destination);
index += direction;
//Wrap around if the last destination has been reached
if (index >= destinations.size()) {
index = 0;
} else if (index < 0) {
index = destinations.size() - 1;
}
//Store selected destination
destination = destinations.get(index);
lastDestination = destination;
}
}

View File

@ -0,0 +1,336 @@
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.event.StargateCreateEvent;
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;
/**
* The portal creator can create and validate a new portal
*/
public class PortalCreator {
private Portal portal;
private final SignChangeEvent event;
private final Player player;
/**
* Instantiates a new portal creator
*
* @param event <p>The sign change event which initialized the creation</p>
* @param player <p>The player creating the portal</p>
*/
public PortalCreator(SignChangeEvent event, Player player) {
this.event = event;
this.player = player;
}
/**
* Creates a new portal
*
* @return <p>The created portal</p>
*/
public Portal createPortal() {
BlockLocation signLocation = new BlockLocation(event.getBlock());
Block idParent = 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) {
Stargate.debug("createPortal", "Control block not registered");
return null;
}
//The control block is already part of another portal
if (PortalHandler.getByBlock(idParent) != null) {
Stargate.debug("createPortal", "idParent belongs to existing stargate");
return null;
}
//Get necessary information from the gate's sign
String portalName = PortalHandler.filterName(event.getLine(0));
String destinationName = PortalHandler.filterName(event.getLine(1));
String network = PortalHandler.filterName(event.getLine(2));
String options = PortalHandler.filterName(event.getLine(3)).toLowerCase();
//Get portal options available to the player creating the portal
Map<PortalOption, Boolean> portalOptions = PortalHandler.getPortalOptions(player, destinationName, options);
//Get the yaw
float yaw = DirectionHelper.getYawFromLocationDifference(idParent.getLocation(), signLocation.getLocation());
//Get the direction the button should be facing
BlockFace buttonFacing = DirectionHelper.getBlockFaceFromYaw(yaw);
PortalLocation portalLocation = new PortalLocation();
portalLocation.setButtonFacing(buttonFacing).setYaw(yaw).setSignLocation(signLocation);
Stargate.debug("createPortal", "Finished getting all portal info");
//Try and find a gate matching the new portal
Gate gate = PortalHandler.findMatchingGate(portalLocation, player);
if ((gate == null) || (portalLocation.getButtonVector() == null)) {
Stargate.debug("createPortal", "Could not find matching gate layout");
return null;
}
//If the portal is a bungee portal and invalid, abort here
if (!PortalHandler.isValidBungeePortal(portalOptions, player, destinationName, network)) {
Stargate.debug("createPortal", "Portal is an invalid bungee portal");
return null;
}
//Debug
StringBuilder builder = new StringBuilder();
for (PortalOption option : portalOptions.keySet()) {
builder.append(option.getCharacterRepresentation()).append(" = ").append(portalOptions.get(option)).append(" ");
}
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)) {
network = Stargate.getDefaultNetwork();
}
boolean deny = false;
String denyMessage = "";
//Check if the player can create portals on this network. If not, create a personal portal
if (!portalOptions.get(PortalOption.BUNGEE) && !PermissionHelper.canCreateNetworkGate(player, network)) {
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);
}
Stargate.debug("createPortal", "Creating personal portal");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createPersonal"));
} else {
Stargate.debug("createPortal", "Player does not have access to network");
deny = true;
denyMessage = Stargate.getString("createNetDeny");
}
}
//Check if the player can create this gate layout
String gateName = gate.getFilename();
gateName = gateName.substring(0, gateName.indexOf('.'));
if (!deny && !PermissionHelper.canCreatePortal(player, gateName)) {
Stargate.debug("createPortal", "Player does not have access to gate layout");
deny = true;
denyMessage = Stargate.getString("createGateDeny");
}
//Check if the user can create portals to this world.
if (!portalOptions.get(PortalOption.BUNGEE) && !deny && destinationName.length() > 0) {
Portal portal = PortalHandler.getByName(destinationName, network);
if (portal != null) {
String world = portal.getWorld().getName();
if (PermissionHelper.cannotAccessWorld(player, world)) {
Stargate.debug("canCreateNetworkGate", "Player does not have access to destination world");
deny = true;
denyMessage = Stargate.getString("createWorldDeny");
}
}
}
//Check if a conflict exists
if (conflictsWithExistingPortal(gate, portalLocation.getTopLeft(), yaw, player)) {
return null;
}
PortalOwner owner = new PortalOwner(player);
this.portal = new Portal(portalLocation, null, destinationName, portalName, network, gate, owner,
portalOptions);
return validatePortal(denyMessage, event.getLines(), deny);
}
/**
* Validates the newly created portal assigned to this portal validator
*
* @param denyMessage <p>The deny message to displayed if the creation has already been denied</p>
* @param lines <p>The lines on the sign causing the portal to be created</p>
* @param deny <p>Whether the portal creation has already been denied</p>
* @return <p>The portal or null if its creation was denied</p>
*/
public Portal validatePortal(String denyMessage, String[] lines, boolean deny) {
PortalLocation portalLocation = portal.getLocation();
Gate gate = portal.getStructure().getGate();
PortalOptions portalOptions = portal.getOptions();
String portalName = portal.getName();
String destinationName = portal.getDestinationName();
int createCost = Stargate.getEconomyConfig().getCreateCost(player, gate);
//Call StargateCreateEvent to let other plugins cancel or overwrite denial
StargateCreateEvent stargateCreateEvent = new StargateCreateEvent(player, portal, lines, deny,
denyMessage, createCost);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargateCreateEvent);
if (stargateCreateEvent.isCancelled()) {
return null;
}
//Tell the user why it was denied from creating the portal
if (stargateCreateEvent.getDeny()) {
Stargate.getMessageSender().sendErrorMessage(player, stargateCreateEvent.getDenyReason());
return null;
}
createCost = stargateCreateEvent.getCost();
//Check if the new portal is valid
if (!checkIfNewPortalIsValid(createCost, portalName)) {
return null;
}
//Add button if the portal is not always on
if (!portalOptions.isAlwaysOn() && !portalOptions.isBungee()) {
generatePortalButton(portalLocation.getTopLeft(), portalLocation.getButtonVector(),
portalLocation.getButtonFacing());
}
//Register the new portal
PortalHandler.registerPortal(portal);
updateNewPortalOpenState(destinationName);
//Update portals pointing at this one if it's not a bungee portal
if (!portal.getOptions().isBungee()) {
PortalHandler.updatePortalsPointingAtNewPortal(portal);
}
PortalFileHelper.saveAllPortals(portal.getWorld());
return portal;
}
/**
* Checks whether the newly created, but unregistered portal is valid
*
* @param cost <p>The cost of creating the portal</p>
* @param portalName <p>The name of the newly created portal</p>
* @return <p>True if the portal is completely valid</p>
*/
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");
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) {
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) {
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());
int maxGates = Stargate.getGateConfig().maxGatesEachNetwork();
if (maxGates > 0 && networkList != null && networkList.size() >= maxGates) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createFull"));
return false;
}
}
if (cost > 0) {
//Deduct the required fee from the player
if (!Stargate.getEconomyConfig().chargePlayerIfNecessary(player, cost)) {
EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost);
Stargate.debug("createPortal", "Insufficient Funds");
return false;
} else {
EconomyHelper.sendDeductMessage(portalName, player, cost);
}
}
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
*
* @param destinationName <p>The name of the destination portal. Only used if set as always on</p>
*/
private void updateNewPortalOpenState(String destinationName) {
portal.drawSign();
if (portal.getOptions().isRandom() || portal.getOptions().isBungee()) {
//Open the implicitly always on portal
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());
if (destinationPortal != null) {
portal.getPortalOpener().openPortal(true);
destinationPortal.drawSign();
}
} else {
//Update the block type for the portal's opening to the closed block as the closed block can be anything,
// not just air or water
for (BlockLocation entrance : portal.getStructure().getEntrances()) {
entrance.setType(portal.getGate().getPortalClosedBlock());
}
}
}
/**
* Checks whether the new portal conflicts with an existing portal
*
* @param gate <p>The gate type of the new portal</p>
* @param topLeft <p>The top-left block of the new portal</p>
* @param yaw <p>The yaw when looking directly outwards from the portal</p>
* @param player <p>The player creating the new portal</p>
* @return <p>True if a conflict was found. False otherwise</p>
*/
private static boolean conflictsWithExistingPortal(Gate gate, BlockLocation topLeft, double yaw, Player player) {
for (RelativeBlockVector borderVector : gate.getLayout().getBorder()) {
BlockLocation borderBlockLocation = topLeft.getRelativeLocation(borderVector, yaw);
if (PortalHandler.getByBlock(borderBlockLocation.getBlock()) != null) {
Stargate.debug("createPortal", "Gate conflicts with existing gate");
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createConflict"));
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,444 @@
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.utility.PermissionHelper;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Keeps track of all loaded portals, and handles portal creation
*/
public class PortalHandler {
private PortalHandler() {
}
/**
* Gets a copy of all portal networks
*
* @return <p>A copy of all portal networks</p>
*/
public static Map<String, List<String>> getAllPortalNetworks() {
return PortalRegistry.getAllPortalNetworks();
}
/**
* Gets a copy of all bungee portals
*
* @return <p>A copy of all bungee portals</p>
*/
public static Map<String, Portal> getBungeePortals() {
return PortalRegistry.getBungeePortals();
}
/**
* Gets names of all portals within a network
*
* @param network <p>The network to get portals from</p>
* @return <p>A list of portal names</p>
*/
public static List<String> getNetwork(String network) {
return PortalRegistry.getNetwork(network);
}
/**
* Gets all destinations in the network viewable by the given player
*
* @param entrancePortal <p>The portal the user is entering from</p>
* @param player <p>The player who wants to see destinations</p>
* @param network <p>The network to get destinations from</p>
* @return <p>All destinations the player can go to</p>
*/
public static List<String> getDestinations(Portal entrancePortal, Player player, String network) {
List<String> destinations = new ArrayList<>();
for (String destination : PortalRegistry.getAllPortalNetworks().get(network.toLowerCase())) {
Portal portal = getByName(destination, network);
if (portal == null) {
continue;
}
//Check if destination is a random portal
if (portal.getOptions().isRandom()) {
continue;
}
//Check if destination is always open (Don't show if so)
if (portal.getOptions().isAlwaysOn() && !portal.getOptions().isShown()) {
continue;
}
//Check if destination is this portal
if (destination.equalsIgnoreCase(entrancePortal.getName())) {
continue;
}
//Check if destination is a fixed portal not pointing to this portal
if (portal.getOptions().isFixed() && !portal.getDestinationName().equalsIgnoreCase(entrancePortal.getName())) {
continue;
}
//Allow random use by non-players (Minecarts)
if (player == null) {
destinations.add(portal.getName());
continue;
}
//Check if this player can access the destination world
if (PermissionHelper.cannotAccessWorld(player, portal.getWorld().getName())) {
continue;
}
//The portal is visible to the player
if (PermissionHelper.canSeePortal(player, portal)) {
destinations.add(portal.getName());
}
}
return destinations;
}
/**
* Registers a portal
*
* @param portal <p>The portal to register</p>
*/
public static void registerPortal(Portal portal) {
PortalRegistry.registerPortal(portal);
}
/**
* Checks if the new portal is a valid bungee portal
*
* @param portalOptions <p>The enabled portal options</p>
* @param player <p>The player trying to create the new portal</p>
* @param destinationName <p>The name of the portal's destination</p>
* @param network <p>The name of the portal's network</p>
* @return <p>False if the portal is an invalid bungee portal. True otherwise</p>
*/
static boolean isValidBungeePortal(Map<PortalOption, Boolean> portalOptions, Player player,
String destinationName, String network) {
if (portalOptions.get(PortalOption.BUNGEE)) {
if (!PermissionHelper.hasPermission(player, "stargate.admin.bungee")) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDeny"));
return false;
} else if (!Stargate.getGateConfig().enableBungee()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
return false;
} else if (destinationName.isEmpty() || network.isEmpty()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeEmpty"));
return false;
}
}
return true;
}
/**
* 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>
* @return <p>The matching gate type, or null if no such gate could be found</p>
*/
static Gate findMatchingGate(PortalLocation portalLocation, Player player) {
Block signParent = portalLocation.getSignLocation().getParent();
BlockLocation parent = new BlockLocation(player.getWorld(), signParent.getX(), signParent.getY(),
signParent.getZ());
//Get all gates with the used type of control blocks
Gate[] possibleGates = GateHandler.getGatesByControlBlock(signParent);
double yaw = portalLocation.getYaw();
Gate gate = null;
for (Gate possibleGate : possibleGates) {
//Get gate controls
RelativeBlockVector[] vectors = possibleGate.getLayout().getControls();
portalLocation.setButtonVector(null);
for (RelativeBlockVector controlVector : vectors) {
//Assuming the top-left location is pointing to the gate's top-left location, check if it's a match
BlockLocation possibleTopLocation = parent.getRelativeLocation(controlVector.invert(), yaw);
if (possibleGate.matches(possibleTopLocation, portalLocation.getYaw(), true)) {
gate = possibleGate;
portalLocation.setTopLeft(possibleTopLocation);
} else {
portalLocation.setButtonVector(controlVector);
}
}
}
return gate;
}
/**
* Updates the sign and open state of portals pointing at the newly created portal
*
* @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());
if (origin == null ||
!origin.getDestinationName().equalsIgnoreCase(portal.getName()) ||
!origin.getStructure().isVerified()) {
continue;
}
//Update sign of fixed gates pointing at this gate
if (origin.getOptions().isFixed()) {
origin.drawSign();
}
//Open any always on portal pointing at this portal
if (origin.getOptions().isAlwaysOn()) {
origin.getPortalOpener().openPortal(true);
}
}
}
/**
* Gets all portal options to be applied to a new portal
*
* @param player <p>The player creating the portal</p>
* @param destinationName <p>The destination of the portal</p>
* @param options <p>The string on the option line of the sign</p>
* @return <p>A map containing all portal options and their values</p>
*/
static Map<PortalOption, Boolean> getPortalOptions(Player player, String destinationName, String options) {
Map<PortalOption, Boolean> portalOptions = new HashMap<>();
for (PortalOption option : PortalOption.values()) {
portalOptions.put(option, options.indexOf(option.getCharacterRepresentation()) != -1 &&
PermissionHelper.canUseOption(player, option));
}
//Can not create a non-fixed always-on portal
if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.length() == 0) {
portalOptions.put(PortalOption.ALWAYS_ON, false);
}
//Show isn't useful if always on is false
if (portalOptions.get(PortalOption.SHOW) && !portalOptions.get(PortalOption.ALWAYS_ON)) {
portalOptions.put(PortalOption.SHOW, false);
}
//Random portals are always on and can't be shown
if (portalOptions.get(PortalOption.RANDOM)) {
portalOptions.put(PortalOption.ALWAYS_ON, true);
portalOptions.put(PortalOption.SHOW, false);
}
//Bungee portals are always on and don't support Random
if (portalOptions.get(PortalOption.BUNGEE)) {
portalOptions.put(PortalOption.ALWAYS_ON, true);
portalOptions.put(PortalOption.RANDOM, false);
}
return portalOptions;
}
/**
* Gets a portal given its name
*
* @param name <p>The name of the portal</p>
* @param network <p>The network the portal is connected to</p>
* @return <p>The portal with the given name or null</p>
*/
public static Portal getByName(String name, String network) {
Map<String, Map<String, Portal>> lookupMap = PortalRegistry.getPortalLookupByNetwork();
if (!lookupMap.containsKey(network.toLowerCase())) {
return null;
}
return lookupMap.get(network.toLowerCase()).get(name.toLowerCase());
}
/**
* Gets a portal given its entrance
*
* @param location <p>The location of the portal's entrance</p>
* @return <p>The portal at the given location</p>
*/
public static Portal getByEntrance(Location location) {
return PortalRegistry.getLookupEntrances().get(new BlockLocation(location.getWorld(), location.getBlockX(),
location.getBlockY(), location.getBlockZ()));
}
/**
* Gets a portal given its entrance
*
* @param block <p>The block at the portal's entrance</p>
* @return <p>The portal at the given block's location</p>
*/
public static Portal getByEntrance(Block block) {
return PortalRegistry.getLookupEntrances().get(new BlockLocation(block));
}
/**
* Gets a portal given a location adjacent to its entrance
*
* @param location <p>A location adjacent to the portal's entrance</p>
* @return <p>The portal adjacent to the given location</p>
*/
public static Portal getByAdjacentEntrance(Location location) {
return getByAdjacentEntrance(location, 1);
}
/**
* Gets a portal given a location adjacent to its entrance
*
* @param location <p>A location adjacent to the portal's entrance</p>
* @param range <p>The range to scan for portals</p>
* @return <p>The portal adjacent to the given location</p>
*/
public static Portal getByAdjacentEntrance(Location location, int range) {
List<BlockLocation> adjacentPositions = new ArrayList<>();
BlockLocation centerLocation = new BlockLocation(location.getBlock());
adjacentPositions.add(centerLocation);
for (int index = 1; index <= range; index++) {
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, 0));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, 0));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(0, 0, index));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(0, 0, -index));
if (index < range) {
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, index));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, -index));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(index, 0, -index));
adjacentPositions.add(centerLocation.makeRelativeBlockLocation(-index, 0, index));
}
}
for (BlockLocation adjacentPosition : adjacentPositions) {
Portal portal = PortalRegistry.getLookupEntrances().get(adjacentPosition);
if (portal != null) {
return portal;
}
}
return null;
}
/**
* Gets a portal given its control block (the block type used for the sign and button)
*
* @param block <p>The portal's control block</p>
* @return <p>The portal with the given control block</p>
*/
public static Portal getByControl(Block block) {
return PortalRegistry.getLookupControls().get(new BlockLocation(block));
}
/**
* Gets a portal given a block
*
* @param block <p>One of the loaded lookup blocks</p>
* @return <p>The portal corresponding to the block</p>
*/
public static Portal getByBlock(Block block) {
return PortalRegistry.getLookupBlocks().get(new BlockLocation(block));
}
/**
* Gets a bungee portal given its name
*
* @param name <p>The name of the bungee portal to get</p>
* @return <p>A bungee portal</p>
*/
public static Portal getBungeePortal(String name) {
return PortalRegistry.getBungeePortals().get(name.toLowerCase());
}
/**
* Gets all portal options stored in the portal data
*
* @param portalData <p>The string list containing all information about a portal</p>
* @return <p>A map between portal options and booleans</p>
*/
public static Map<PortalOption, Boolean> getPortalOptions(String[] portalData) {
Map<PortalOption, Boolean> portalOptions = new HashMap<>();
for (PortalOption option : PortalOption.values()) {
int saveIndex = option.getSaveIndex();
portalOptions.put(option, portalData.length > saveIndex && Boolean.parseBoolean(portalData[saveIndex]));
}
return portalOptions;
}
/**
* Opens all always-on portals
*
* @return <p>The number of always open portals enabled</p>
*/
public static int openAlwaysOpenPortals() {
int alwaysOpenCount = 0;
for (Portal portal : PortalRegistry.getAllPortals()) {
//Open the gate if it's set as always open or if it's a bungee gate
if (portal.getOptions().isFixed() && (Stargate.getGateConfig().enableBungee() &&
portal.getOptions().isBungee() || portal.getPortalActivator().getDestination() != null &&
portal.getOptions().isAlwaysOn())) {
portal.getPortalOpener().openPortal(true);
alwaysOpenCount++;
}
}
return alwaysOpenCount;
}
/**
* Tries to verify all portals and un-registers non-verifiable portals
*/
public static void verifyAllPortals() {
List<Portal> invalidPortals = new ArrayList<>();
for (Portal portal : PortalRegistry.getAllPortals()) {
//Try and verify the portal. Invalidate it if it cannot be validated
PortalStructure structure = portal.getStructure();
if (!structure.wasVerified() && (!structure.isVerified() || !structure.checkIntegrity())) {
invalidPortals.add(portal);
}
}
//Un-register any invalid portals found
for (Portal portal : invalidPortals) {
unregisterInvalidPortal(portal);
}
}
/**
* Un-registers a portal which has failed its integrity tests
*
* @param portal <p>The portal of the star portal</p>
*/
private static void unregisterInvalidPortal(Portal portal) {
//Show debug information
for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) {
Block block = portal.getBlockAt(control).getBlock();
//Log control blocks not matching the gate layout
if (!block.getType().equals(portal.getGate().getControlBlock())) {
Stargate.debug("PortalHandler::destroyInvalidPortal", "Control Block Type == " +
block.getType().name());
}
}
PortalRegistry.unregisterPortal(portal, false);
Stargate.logInfo(String.format("Destroying stargate at %s", portal));
}
/**
* Closes all portals
*/
public static void closeAllPortals() {
Stargate.logInfo("Closing all stargates.");
for (Portal portal : PortalRegistry.getAllPortals()) {
if (portal != null) {
portal.getPortalOpener().closePortal(true);
}
}
}
/**
* Removes the special characters |, : and # from a portal name
*
* @param input <p>The name to filter</p>
* @return <p>The filtered name</p>
*/
public static String filterName(String input) {
if (input == null) {
return "";
}
return input.replaceAll("[|:#]", "").trim();
}
}

View File

@ -0,0 +1,145 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
import org.bukkit.Axis;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
/**
* Keeps track of location related data for a portal
*/
@SuppressWarnings("UnusedReturnValue")
public class PortalLocation {
private BlockLocation topLeft;
private float yaw;
private BlockLocation signLocation;
private RelativeBlockVector buttonVector;
private BlockFace buttonFacing;
/**
* Gets the top-left block of the portal
*
* @return <p>The top-left block of the portal</p>
*/
public BlockLocation getTopLeft() {
return topLeft;
}
/**
* Gets the yaw for looking outwards from the portal
*
* @return <p>The portal's yaw</p>
*/
public float getYaw() {
return yaw;
}
/**
* Gets the location of the portal's sign
*
* @return <p>The location of the portal's sign</p>
*/
public BlockLocation getSignLocation() {
return signLocation;
}
/**
* The relative block vector pointing to the portal's button
*
* @return <p>The relative location of the portal's button</p>
*/
public RelativeBlockVector getButtonVector() {
return buttonVector;
}
/**
* Gets the block face determining the button's direction
*
* @return <p>The button's block face</p>
*/
public BlockFace getButtonFacing() {
return buttonFacing;
}
/**
* Gets the rotation axis, which is the axis along which the gate is placed
* <p>The portal's rotation axis is the cross axis of the button's axis</p>
*
* @return <p>The portal's rotation axis</p>
*/
public Axis getRotationAxis() {
return getYaw() == 0.0F || getYaw() == 180.0F ? Axis.X : Axis.Z;
}
/**
* Gets the world this portal resides in
*
* @return <p>The world this portal resides in</p>
*/
public World getWorld() {
return topLeft.getWorld();
}
/**
* Sets the portal's top-left location
*
* <p>Assuming the portal is a square, the top-left block is the top-left block when looking at the portal at the
* side with the portal's sign.</p>
*
* @param topLeft <p>The new top-left block of the portal's square structure</p>
* @return <p>The portal location Object</p>
*/
public PortalLocation setTopLeft(BlockLocation topLeft) {
this.topLeft = topLeft;
return this;
}
/**
* Sets the portal's yaw
*
* <p>The portal's yaw is the yaw a player would get when looking directly out from the portal</p>
*
* @param yaw <p>The portal's new yaw</p>
* @return <p>The portal location Object</p>
*/
public PortalLocation setYaw(float yaw) {
this.yaw = yaw;
return this;
}
/**
* Sets the location of the portal's sign
*
* @param signLocation <p>The new sign location</p>
* @return <p>The portal location Object</p>
*/
public PortalLocation setSignLocation(BlockLocation signLocation) {
this.signLocation = signLocation;
return this;
}
/**
* Sets the relative location of the portal's button
*
* @param buttonVector <p>The new relative button location</p>
* @return <p>The portal location Object</p>
*/
public PortalLocation setButtonVector(RelativeBlockVector buttonVector) {
this.buttonVector = buttonVector;
return this;
}
/**
* Sets the block face for the direction the portal button is facing
*
* @param buttonFacing <p>The new block face of the portal's button</p>
* @return <p>The portal location Object</p>
*/
public PortalLocation setButtonFacing(BlockFace buttonFacing) {
this.buttonFacing = buttonFacing;
return this;
}
}

View File

@ -0,0 +1,231 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
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 org.bukkit.Axis;
import org.bukkit.Material;
import org.bukkit.block.data.Orientable;
import org.bukkit.entity.Player;
/**
* The portal opener is responsible for opening and closing a portal
*/
public class PortalOpener {
private boolean isOpen = false;
private final Portal portal;
private long triggeredTime;
private Player player;
private final PortalActivator portalActivator;
/**
* Instantiates a new portal opener
*
* @param portal <p>The portal this portal opener should open</p>
* @param destination <p>The fixed destination defined on the portal's sign</p>
*/
public PortalOpener(Portal portal, String destination) {
this.portal = portal;
this.portalActivator = new PortalActivator(portal, this, destination);
}
/**
* Gets whether this portal opener's portal is currently open
*
* @return <p>Whether this portal opener's portal is open</p>
*/
public boolean isOpen() {
return isOpen || portal.getOptions().isAlwaysOn();
}
/**
* Sets the time when this portal was triggered (activated/opened)
*
* @param triggeredTime <p>Unix timestamp when portal was triggered</p>
*/
public void setTriggeredTime(long triggeredTime) {
this.triggeredTime = triggeredTime;
}
/**
* Gets the portal activator belonging to this portal opener
*
* @return <p>The portal activator belonging to this portal opener</p>
*/
public PortalActivator getPortalActivator() {
return this.portalActivator;
}
/**
* Open this portal opener's portal
*
* @param force <p>Whether to force the portal open, even if it's already open for some player</p>
*/
public void openPortal(boolean force) {
openPortal(null, force);
}
/**
* Open this portal opener's portal
*
* @param openFor <p>The player to open the portal for</p>
* @param force <p>Whether to force the portal open, even if it's already open for some player</p>
*/
public void openPortal(Player openFor, boolean force) {
//Call the StargateOpenEvent to allow the opening to be cancelled
StargateOpenEvent event = new StargateOpenEvent(openFor, portal, force);
Stargate.getInstance().getServer().getPluginManager().callEvent(event);
if (event.isCancelled() || (isOpen() && !event.getForce())) {
return;
}
//Get the material to change the opening to
Material openType = portal.getGate().getPortalOpenBlock();
//Adjust orientation if applicable
Axis axis = (openType.createBlockData() instanceof Orientable) ? portal.getLocation().getRotationAxis() : null;
//Change the entrance blocks to the correct type
for (BlockLocation inside : portal.getStructure().getEntrances()) {
Stargate.addBlockChangeRequest(new BlockChangeRequest(inside, openType, axis));
}
//Update the portal state to make is actually open
updatePortalOpenState(openFor);
}
/**
* Updates this portal opener's portal to be recognized as open and opens its destination portal
*
* @param openFor <p>The player to open this portal opener's portal for</p>
*/
private void updatePortalOpenState(Player openFor) {
//Update the open state of this portal
isOpen = true;
triggeredTime = System.currentTimeMillis() / 1000;
//Change state from active to open
Stargate.getStargateConfig().getOpenPortalsQueue().add(portal);
Stargate.getStargateConfig().getActivePortalsQueue().remove(portal);
PortalOptions options = portal.getOptions();
//If this portal is always open, opening the destination is not necessary
if (options.isAlwaysOn()) {
return;
}
//Update the player the portal is open for
this.player = openFor;
Portal destination = portal.getPortalActivator().getDestination();
if (destination == null) {
return;
}
boolean thisIsDestination = destination.getDestinationName().equalsIgnoreCase(portal.getName());
//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
destination.getPortalOpener().openPortal(openFor, false);
//Set the destination portal to this opener's portal
destination.getPortalActivator().setDestination(portal);
//Update the destination's sign if it's verified
if (destination.getStructure().isVerified()) {
destination.drawSign();
}
}
}
/**
* Closes this portal opener's portal
*
* @param force <p>Whether to force the portal closed, even if it's set as always on</p>
*/
public void closePortal(boolean force) {
//No need to close a portal which is already closed
if (!isOpen()) {
return;
}
//Call the StargateCloseEvent to allow other plugins to cancel the closing, or change whether to force it closed
StargateCloseEvent event = new StargateCloseEvent(portal, force);
Stargate.getInstance().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
//Only close an always-open portal if forced to
if (portal.getOptions().isAlwaysOn() && !event.getForce()) {
return;
}
//Close the portal by requesting the opening blocks to change
Material closedType = portal.getGate().getPortalClosedBlock();
for (BlockLocation entrance : portal.getStructure().getEntrances()) {
Stargate.addBlockChangeRequest(new BlockChangeRequest(entrance, closedType, null));
}
//Update the portal state to make it actually closed
updatePortalClosedState();
//Finally, deactivate the portal
portalActivator.deactivate();
}
/**
* Updates this portal to be recognized as closed and closes its destination portal
*/
private void updatePortalClosedState() {
//Unset the stored player and set the portal to closed
player = null;
isOpen = false;
//Un-mark the portal as active and open
Stargate.getStargateConfig().getOpenPortalsQueue().remove(portal);
Stargate.getStargateConfig().getActivePortalsQueue().remove(portal);
//Close the destination portal if not always open
if (!portal.getOptions().isAlwaysOn()) {
Portal destination = portal.getPortalActivator().getDestination();
if (destination != null && destination.isOpen()) {
//De-activate and close the destination portal
destination.getPortalActivator().deactivate();
destination.getPortalOpener().closePortal(false);
}
}
}
/**
* Gets whether this portal opener's portal is open for the given player
*
* @param player <p>The player to check portal state for</p>
* @return <p>True if this portal opener's portal is open to the given player</p>
*/
public boolean isOpenFor(Player player) {
//If closed, it's closed for everyone
if (!isOpen) {
return false;
}
//If always on, or player is null which only happens with an always on portal, allow the player to pass
if (portal.getOptions().isAlwaysOn() || this.player == null) {
return true;
}
//If the player is the player which the portal opened for, allow it to pass
return player != null && player.getName().equalsIgnoreCase(this.player.getName());
}
/**
* Gets the time this portal opener's portal was triggered (activated/opened)
*
* @return <p>The time this portal opener's portal was triggered</p>
*/
public long getTriggeredTime() {
return triggeredTime;
}
}

View File

@ -0,0 +1,96 @@
package net.knarcraft.stargate.portal;
/**
* Each enum value represents one option a portal can have/use
*/
public enum PortalOption {
/**
* This option allows a portal to be hidden from others
*/
HIDDEN('h', "stargate.option.hidden", 11),
/**
* This option allows a portal that's always on and does not need to be activated or opened each time
*/
ALWAYS_ON('a', "stargate.option.alwayson", 12),
/**
* This option allows a portal that's private to the stargate's owner
*/
PRIVATE('p', "stargate.option.private", 13),
/**
* This option allows a portal that's free even if stargates usually are not
*/
FREE('f', "stargate.option.free", 15),
/**
* This option allows a portal where players exit through the back of the portal
*/
BACKWARDS('b', "stargate.option.backwards", 16),
/**
* This option shows the gate in the network list even if it's always on
*/
SHOW('s', "stargate.option.show", 17),
/**
* This option hides the network name on the sign
*/
NO_NETWORK('n', "stargate.option.nonetwork", 18),
/**
* This option allows a portal where players teleport to a random exit portal in the network
*/
RANDOM('r', "stargate.option.random", 19),
/**
* This option allows a portal to teleport to another server connected through BungeeCord
*/
BUNGEE('u', "stargate.admin.bungee", 20);
private final char characterRepresentation;
private final String permissionString;
private final int saveIndex;
/**
* Instantiates a new portal options
*
* @param characterRepresentation <p>The character representation used on the sign to allow this option</p>
* @param permissionString <p>The permission necessary to use this option</p>
*/
PortalOption(final char characterRepresentation, String permissionString, int saveIndex) {
this.characterRepresentation = characterRepresentation;
this.permissionString = permissionString;
this.saveIndex = saveIndex;
}
/**
* Gets the character representation used to enable this setting on the sign
*
* @return <p>The character representation of this option</p>
*/
public char getCharacterRepresentation() {
return this.characterRepresentation;
}
/**
* Gets the permission necessary to use this option
*
* @return <p>The permission necessary for this option</p>
*/
public String getPermissionString() {
return this.permissionString;
}
/**
* Gets the index of the save file this option is stored at
*
* @return <p>This option's save index</p>
*/
public int getSaveIndex() {
return this.saveIndex;
}
}

View File

@ -0,0 +1,168 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import java.util.Map;
/**
* Keeps track of all options for one portal
*/
public class PortalOptions {
private final Map<PortalOption, Boolean> options;
private boolean isFixed;
/**
* Instantiates a new portal options object
*
* @param options <p>All options to keep track of</p>
* @param hasDestination <p>Whether the portal has a fixed destination</p>
*/
public PortalOptions(Map<PortalOption, Boolean> options, boolean hasDestination) {
this.options = options;
isFixed = hasDestination || this.isRandom() || this.isBungee();
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");
}
if (this.isRandom() && !this.isAlwaysOn()) {
this.options.put(PortalOption.ALWAYS_ON, true);
Stargate.debug("Portal", "Gate marked as random, set to always-on");
}
}
/**
* Gets whether this portal is fixed
*
* <p>A fixed portal is a portal for which the player cannot choose destination. A portal with a set destination, a
* random portal and bungee portals are fixed. While the player has no choice regarding destinations, a fixed gate
* may still need to be activated if not set to always on.</p>
*
* @return <p>Whether this portal is fixed</p>
*/
public boolean isFixed() {
return this.isFixed;
}
/**
* Sets whether this portal is fixed
*
* @param fixed <p>Whether this gate should be fixed</p>
*/
public void setFixed(boolean fixed) {
this.isFixed = fixed;
}
/**
* Gets whether this portal is always on
*
* <p>An always on portal is always open for everyone, and always uses the open-block. It never needs to be
* activated or opened manually.</p>
*
* @return <p>Whether this portal is always on</p>
*/
public boolean isAlwaysOn() {
return this.options.get(PortalOption.ALWAYS_ON);
}
/**
* Gets whether this portal is hidden
*
* <p>A hidden portal will be hidden on a network for everyone but admins and the portal owner. In other words,
* when selecting a destination using a portal's sign, hidden gates will only be available in the list for the
* owner and players with the appropriate permission.</p>
*
* @return <p>Whether this portal is hidden</p>
*/
public boolean isHidden() {
return this.options.get(PortalOption.HIDDEN);
}
/**
* Gets whether this portal is private
*
* <p>A private portal can only be opened by the owner and players with the appropriate permission. A private gate
* is not hidden unless the hidden option is also enabled.</p>
*
* @return <p>Whether this portal is private</p>
*/
public boolean isPrivate() {
return this.options.get(PortalOption.PRIVATE);
}
/**
* Gets whether this portal is free
*
* <p>A free portal is exempt from any fees which would normally occur from using the portal. It does nothing if
* economy is disabled.</p>
*
* @return <p>Whether this portal is free</p>
*/
public boolean isFree() {
return this.options.get(PortalOption.FREE);
}
/**
* Gets whether this portal is backwards
*
* <p>A backwards portal is one where players exit through the back. It's important to note that the exit is
* mirrored, not rotated, when exiting backwards.</p>
*
* @return <p>Whether this portal is backwards</p>
*/
public boolean isBackwards() {
return this.options.get(PortalOption.BACKWARDS);
}
/**
* Gets whether this portal is shown on the network even if it's always on
*
* <p>Normally, always-on portals are not selectable on a network, but enabling this option allows the portal to be
* shown.</p>
*
* @return <p>Whether portal gate is shown</p>
*/
public boolean isShown() {
return this.options.get(PortalOption.SHOW);
}
/**
* Gets whether this portal shows no network
*
* <p>Enabling the no network option allows the portal's network to be hidden for whatever reason. If allowing
* normal players to create portals, this can be used to prevent random users from connecting gates to
* "protected networks".</p>
*
* @return <p>Whether this portal shows no network/p>
*/
public boolean isNoNetwork() {
return this.options.get(PortalOption.NO_NETWORK);
}
/**
* Gets whether this portal goes to a random location on the network
*
* <p>A random portal is always on and will teleport to a random destination within the same network.</p>
*
* @return <p>Whether this portal goes to a random location</p>
*/
public boolean isRandom() {
return this.options.get(PortalOption.RANDOM);
}
/**
* Gets whether this portal is a bungee portal
*
* <p>A bungee portal is able to teleport to a portal on another server. It works differently from other portals as
* it does not have a network, but instead the network line specifies the same of the server it connects to.</p>
*
* @return <p>Whether this portal is a bungee portal</p>
*/
public boolean isBungee() {
return this.options.get(PortalOption.BUNGEE);
}
}

View File

@ -0,0 +1,102 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.util.UUID;
/**
* The portal owner represents the owner of a portal
*/
public class PortalOwner {
private UUID ownerUUID;
private String ownerName;
/**
* Instantiates a new portal owner
*
* @param ownerIdentifier <p>A UUID, or a username for legacy support</p>
*/
public PortalOwner(String ownerIdentifier) {
parseIdentifier(ownerIdentifier);
}
/**
* Instantiates a new portal owner
*
* @param player <p>The player which is the owner of the portal</p>
*/
public PortalOwner(Player player) {
this.ownerUUID = player.getUniqueId();
this.ownerName = player.getName();
}
/**
* Gets the UUID of this owner
*
* @return <p>The UUID of this owner, or null if a UUID is not available</p>
*/
public UUID getUUID() {
return ownerUUID;
}
/**
* Gets the name of this owner
*
* @return <p>The name of this owner</p>
*/
public String getName() {
return ownerName;
}
/**
* Gets the one identifier used for saving the owner
*
* <p>If the UUID is available, a string representation of the UUID will be returned. If not, the owner's name will
* be returned.</p>
*
* @return <p>The owner's identifier</p>
*/
public String getIdentifier() {
if (ownerUUID != null) {
return ownerUUID.toString();
} else {
return ownerName;
}
}
/**
* Parses the identifier of a portal's owner
*
* <p>The identifier should be a valid UUID, but can be a username of max 16 characters for legacy support. Strings
* longer than 16 characters not parse-able as a UUID will silently fail by setting the owner name to the
* identifier.</p>
*
* @param ownerIdentifier <p>The identifier for a portal's owner</p>
*/
private void parseIdentifier(String ownerIdentifier) {
UUID ownerUUID = null;
String ownerName;
if (ownerIdentifier.length() > 16) {
//If more than 16 characters, the string cannot be a username, so it's probably a UUID
try {
ownerUUID = UUID.fromString(ownerIdentifier);
OfflinePlayer offlineOwner = Bukkit.getServer().getOfflinePlayer(ownerUUID);
ownerName = offlineOwner.getName();
} catch (IllegalArgumentException ex) {
//Invalid as UUID and username, so just keep it as owner name and hope the server owner fixes it
ownerName = ownerIdentifier;
Stargate.debug("loadAllPortals", "Invalid stargate owner string: " + ownerIdentifier);
}
} else {
//Old username from the pre-UUID times. Just keep it as the owner name
ownerName = ownerIdentifier;
}
this.ownerName = ownerName;
this.ownerUUID = ownerUUID;
}
}

View File

@ -0,0 +1,297 @@
package net.knarcraft.stargate.portal;
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;
import java.util.List;
import java.util.Map;
/**
* The portal registry keeps track of all registered portals and all their lookup blocks
*/
public class PortalRegistry {
private static final Map<BlockLocation, Portal> lookupBlocks = new HashMap<>();
private static final Map<BlockLocation, Portal> lookupEntrances = new HashMap<>();
private static final Map<BlockLocation, Portal> lookupControls = new HashMap<>();
private static final Map<String, Map<String, Portal>> portalLookupByNetwork = new HashMap<>();
private static final Map<String, List<String>> allPortalNetworks = new HashMap<>();
private static final Map<String, Portal> bungeePortals = new HashMap<>();
private static final List<Portal> allPortals = new ArrayList<>();
/**
* Clears all portals and all data held by the portal registry
*/
public static void clearPortals() {
lookupBlocks.clear();
portalLookupByNetwork.clear();
lookupEntrances.clear();
lookupControls.clear();
allPortals.clear();
allPortalNetworks.clear();
}
/**
* Clears all portals loaded in a given world
*
* @param world <p>The world containing the portals to clear</p>
*/
public static void clearPortals(World world) {
//Storing the portals to clear is necessary to avoid a concurrent modification exception
List<Portal> portalsToRemove = new ArrayList<>();
allPortals.forEach((portal) -> {
if (portal.getWorld().equals(world)) {
portalsToRemove.add(portal);
}
});
clearPortals(portalsToRemove);
}
/**
* Clears a given list of portals from all relevant variables
*
* @param portalsToRemove <p>A list of portals to remove</p>
*/
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()));
//Clear all the lookup locations for the portals
lookupBlocks.keySet().removeIf((key) -> portalsToRemove.contains(lookupBlocks.get(key)));
lookupEntrances.keySet().removeIf((key) -> portalsToRemove.contains(lookupEntrances.get(key)));
lookupControls.keySet().removeIf((key) -> portalsToRemove.contains(lookupControls.get(key)));
//Remove the portals from all networks, and then remove any empty networks. This is done for both network maps
portalLookupByNetwork.keySet().forEach((network) -> portalLookupByNetwork.get(network).keySet().removeIf((key) ->
portalsToRemove.contains(portalLookupByNetwork.get(network).get(key))));
portalLookupByNetwork.keySet().removeIf((key) -> portalLookupByNetwork.get(key).isEmpty());
allPortalNetworks.keySet().forEach((network) -> allPortalNetworks.get(network).removeIf(portalNames::contains));
allPortalNetworks.keySet().removeIf((network) -> allPortalNetworks.get(network).isEmpty());
//Finally, remove the portals from the portal list
allPortals.removeIf(portalsToRemove::contains);
}
/**
* Gets a copy of the list of all portals
*
* @return <p>A copy of the list of all portals</p>
*/
public static List<Portal> getAllPortals() {
return new ArrayList<>(allPortals);
}
/**
* Gets a copy of the lookup map for finding a portal by its frame
*
* @return <p>A copy of the frame block lookup map</p>
*/
public static Map<BlockLocation, Portal> getLookupBlocks() {
return new HashMap<>(lookupBlocks);
}
/**
* Gets a copy of the lookup map for finding a portal by its control block
*
* @return <p>A copy of the control block lookup map</p>
*/
public static Map<BlockLocation, Portal> getLookupControls() {
return new HashMap<>(lookupControls);
}
/**
* Gets a copy of the lookup map for finding all portals in a network
*
* @return <p>A copy of the network portal lookup map</p>
*/
public static Map<String, Map<String, Portal>> getPortalLookupByNetwork() {
return new HashMap<>(portalLookupByNetwork);
}
/**
* Gets a copy of all portal entrances available for lookup
*
* @return <p>A copy of all entrances to portal mappings</p>
*/
public static Map<BlockLocation, Portal> getLookupEntrances() {
return new HashMap<>(lookupEntrances);
}
/**
* Gets a copy of all portal networks
*
* @return <p>A copy of all portal networks</p>
*/
public static Map<String, List<String>> getAllPortalNetworks() {
return new HashMap<>(allPortalNetworks);
}
/**
* Gets a copy of all bungee portals
*
* @return <p>A copy of all bungee portals</p>
*/
public static Map<String, Portal> getBungeePortals() {
return new HashMap<>(bungeePortals);
}
/**
* Gets names of all portals within a network
*
* @param network <p>The network to get portals from</p>
* @return <p>A list of portal names</p>
*/
public static List<String> getNetwork(String network) {
return allPortalNetworks.get(network.toLowerCase());
}
/**
* Un-registers the given portal
*
* @param portal <p>The portal to un-register</p>
* @param removeAll <p>Whether to remove the portal from the list of all portals</p>
*/
public static void unregisterPortal(Portal portal, boolean removeAll) {
Stargate.debug("Unregister", "Unregistering gate " + portal.getName());
portal.getPortalOpener().closePortal(true);
String portalName = portal.getName().toLowerCase();
String networkName = portal.getNetwork().toLowerCase();
//Remove portal from lookup blocks
for (BlockLocation block : portal.getStructure().getFrame()) {
lookupBlocks.remove(block);
}
//Remove registered info about the lookup controls and blocks
lookupBlocks.remove(portal.getSignLocation());
lookupControls.remove(portal.getSignLocation());
BlockLocation button = portal.getStructure().getButton();
if (button != null) {
lookupBlocks.remove(button);
lookupControls.remove(button);
}
//Remove entrances
for (BlockLocation entrance : portal.getStructure().getEntrances()) {
lookupEntrances.remove(entrance);
}
//Remove the portal from the list of all portals
if (removeAll) {
allPortals.remove(portal);
}
if (portal.getOptions().isBungee()) {
//Remove the bungee listing
bungeePortals.remove(portalName);
} else {
//Remove from network lists
portalLookupByNetwork.get(networkName).remove(portalName);
allPortalNetworks.get(networkName).remove(portalName);
//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());
if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) ||
!origin.getStructure().isVerified()) {
continue;
}
//Update the portal's sign
if (origin.getOptions().isFixed()) {
origin.drawSign();
}
//Close portal without destination
if (origin.getOptions().isAlwaysOn()) {
origin.getPortalOpener().closePortal(true);
}
}
}
//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();
}
PortalFileHelper.saveAllPortals(portal.getWorld());
}
/**
* Registers a portal
*
* @param portal <p>The portal to register</p>
*/
static void registerPortal(Portal portal) {
portal.getOptions().setFixed(portal.getDestinationName().length() > 0 || portal.getOptions().isRandom() ||
portal.getOptions().isBungee());
String portalName = portal.getName().toLowerCase();
String networkName = portal.getNetwork().toLowerCase();
//Bungee portals are stored in their own list
if (portal.getOptions().isBungee()) {
bungeePortals.put(portalName, portal);
} 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");
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");
allPortalNetworks.put(networkName, new ArrayList<>());
}
//Register the portal
portalLookupByNetwork.get(networkName).put(portalName, portal);
if (!allPortalNetworks.get(networkName).contains(portalName)) {
allPortalNetworks.get(networkName).add(portalName);
} else {
Stargate.logSevere(String.format("Portal %s on network %s was registered twice. Check your portal " +
"database for duplicates.", portal.getName(), portal.getNetwork()));
}
}
//Register all frame blocks to the lookup list
for (BlockLocation block : portal.getStructure().getFrame()) {
lookupBlocks.put(block, portal);
}
//Register the sign and button to the lookup lists
lookupBlocks.put(portal.getSignLocation(), portal);
lookupControls.put(portal.getSignLocation(), portal);
BlockLocation button = portal.getStructure().getButton();
if (button != null) {
lookupBlocks.put(button, portal);
lookupControls.put(button, portal);
}
//Register entrances to the lookup list
for (BlockLocation entrance : portal.getStructure().getEntrances()) {
lookupEntrances.put(entrance, portal);
}
allPortals.add(portal);
}
}

View File

@ -0,0 +1,240 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.utility.PermissionHelper;
import org.bukkit.ChatColor;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
/**
* The portal sign drawer draws the sing of a given portal
*/
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 mainColor;
private static ChatColor highlightColor;
/**
* Instantiates a new portal sign drawer
*
* @param portal <p>The portal whose sign this portal sign drawer is responsible for drawing</p>
*/
public PortalSignDrawer(Portal portal) {
this.portal = portal;
}
/**
* Sets the main 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>
*
* @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;
highlightColor = newHighlightColor;
}
/**
* 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()));
return;
}
drawSign(sign);
}
/**
* Draws the sign of the portal this sign drawer is responsible for
*
* @param sign <p>The sign re-draw</p>
*/
private void drawSign(Sign sign) {
//Clear sign
for (int index = 0; index <= 3; index++) {
sign.setLine(index, "");
}
setLine(sign, 0, highlightColor + "-" + mainColor +
portal.getName() + highlightColor + "-");
if (!portal.getPortalActivator().isActive()) {
//Default sign text
drawInactiveSign(sign);
} else {
if (portal.getOptions().isBungee()) {
//Bungee sign
drawBungeeSign(sign);
} else if (portal.getOptions().isFixed()) {
//Sign pointing at one other portal
drawFixedSign(sign);
} else {
//Networking stuff
drawNetworkSign(sign);
}
}
sign.update();
}
/**
* Draws a sign with choose-able network locations
*
* @param sign <p>The sign to re-draw</p>
*/
private void drawNetworkSign(Sign sign) {
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();
//Last, and not only entry. Draw the entry two back
if ((destinationIndex == maxIndex) && (maxIndex > 1)) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex - 2);
}
//Not first entry. Draw the previous entry
if (destinationIndex > 0) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex - 1);
}
//Draw the chosen entry (line 2 or 3)
drawNetworkSignChosenLine(freeGatesGreen, sign, ++signLineIndex);
//Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 1)) {
drawNetworkSignLine(freeGatesGreen, sign, ++signLineIndex, destinationIndex + 1);
}
//Has another entry and space on the sign
if ((maxIndex >= destinationIndex + 2) && (++signLineIndex <= 3)) {
drawNetworkSignLine(freeGatesGreen, sign, 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>
*/
private void drawNetworkSignChosenLine(boolean freeGatesGreen, Sign sign, int signLineIndex) {
if (freeGatesGreen) {
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 + "<");
} else {
setLine(sign, signLineIndex, highlightColor + ">" + mainColor + 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>
*/
public void setLine(Sign sign, int index, String text) {
sign.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 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) {
PortalActivator destinations = portal.getPortalActivator();
String destinationName = destinations.getDestinations().get(destinationIndex);
if (freeGatesGreen) {
Portal destination = PortalHandler.getByName(destinationName, portal.getNetwork());
boolean green = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination);
setLine(sign, signLineIndex, (green ? freeColor : mainColor) + destinationName);
} else {
setLine(sign, signLineIndex, mainColor + destinationName);
}
}
/**
* Draws a bungee sign
*
* @param sign <p>The sign to re-draw</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 + "]");
}
/**
* Draws an inactive sign
*
* @param sign <p>The sign to re-draw</p>
*/
private void drawInactiveSign(Sign sign) {
setLine(sign, 1, Stargate.getString("signRightClick"));
setLine(sign, 2, Stargate.getString("signToUse"));
if (!portal.getOptions().isNoNetwork()) {
setLine(sign, 3, highlightColor + "(" + mainColor + portal.getNetwork() + highlightColor + ")");
} else {
setLine(sign, 3, "");
}
}
/**
* Draws a sign pointing to a fixed location
*
* @param sign <p>The sign to re-draw</p>
*/
private void drawFixedSign(Sign sign) {
String destinationName = portal.getOptions().isRandom() ? Stargate.getString("signRandom") :
portal.getDestinationName();
setLine(sign, 1, highlightColor + ">" + mainColor + destinationName + highlightColor + "<");
if (portal.getOptions().isNoNetwork()) {
setLine(sign, 2, "");
} else {
setLine(sign, 2, highlightColor + "(" + mainColor + portal.getNetwork() + highlightColor + ")");
}
Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork());
if (destination == null && !portal.getOptions().isRandom()) {
setLine(sign, 3, errorColor + Stargate.getString("signDisconnected"));
} else {
setLine(sign, 3, "");
}
}
/**
* Marks a portal with an invalid gate by changing its sign and writing to the console
*
* @param portalLocation <p>The location of the portal with an invalid gate</p>
* @param gateName <p>The name of the invalid gate type</p>
* @param lineIndex <p>The index of the line the invalid portal was found at</p>
*/
public static void markPortalWithInvalidGate(PortalLocation portalLocation, String gateName, int lineIndex) {
Sign sign = (Sign) portalLocation.getSignLocation().getBlock().getState();
sign.setLine(3, errorColor + Stargate.getString("signInvalidGate"));
sign.update();
Stargate.logInfo(String.format("Gate layout on line %d does not exist [%s]", lineIndex, gateName));
}
}

View File

@ -0,0 +1,148 @@
package net.knarcraft.stargate.portal;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.RelativeBlockVector;
/**
* The portal structure is responsible for the physical properties of a portal
*
* <p>The portal structure knows which gate type is used, where the real locations of buttons, frames and entrances are
* and whether the portal is verified.</p>
*/
public class PortalStructure {
private final Portal portal;
private final Gate gate;
private BlockLocation button;
private BlockLocation[] frame;
private BlockLocation[] entrances;
private boolean verified;
/**
* Instantiates a new portal structure
*
* @param portal <p>The portal whose structure to store</p>
* @param gate <p>The gate type used by this portal structure</p>
* @param button <p>The real location of the portal's button</p>
*/
public PortalStructure(Portal portal, Gate gate, BlockLocation button) {
this.portal = portal;
this.gate = gate;
this.verified = false;
this.button = button;
}
/**
* Gets the gate used by this portal structure
*
* @return <p>The gate used by this portal structure</p>
*/
public Gate getGate() {
return gate;
}
/**
* Gets the location of this portal's button
*
* @return <p>The location of this portal's button</p>
*/
public BlockLocation getButton() {
return button;
}
/**
* Sets the location of this portal's button
*
* @param button <p>The location of this portal's button</p>
*/
public void setButton(BlockLocation button) {
this.button = button;
}
/**
* Verifies that all control blocks in this portal follows its gate template
*
* @return <p>True if all control blocks were verified</p>
*/
public boolean isVerified() {
boolean verified = true;
if (!Stargate.getGateConfig().verifyPortals()) {
return true;
}
for (RelativeBlockVector control : gate.getLayout().getControls()) {
verified = verified && portal.getBlockAt(control).getBlock().getType().equals(gate.getControlBlock());
}
this.verified = verified;
return verified;
}
/**
* Gets the result of the last portal verification
*
* @return <p>True if this portal was verified</p>
*/
public boolean wasVerified() {
if (!Stargate.getGateConfig().verifyPortals()) {
return true;
}
return verified;
}
/**
* Checks if all blocks in a gate matches the gate template
*
* @return <p>True if all blocks match the gate template</p>
*/
public boolean checkIntegrity() {
if (Stargate.getGateConfig().verifyPortals()) {
return gate.matches(portal.getTopLeft(), portal.getYaw());
} else {
return true;
}
}
/**
* Gets a list of block locations from a list of relative block vectors
*
* <p>The block locations will be calculated by using this portal's top-left block as the origin for the relative
* vectors..</p>
*
* @param vectors <p>The relative block vectors to convert</p>
* @return <p>A list of block locations</p>
*/
private BlockLocation[] relativeBlockVectorsToBlockLocations(RelativeBlockVector[] vectors) {
BlockLocation[] locations = new BlockLocation[vectors.length];
for (int i = 0; i < vectors.length; i++) {
locations[i] = portal.getBlockAt(vectors[i]);
}
return locations;
}
/**
* Gets the locations of this portal's entrances
*
* @return <p>The locations of this portal's entrances</p>
*/
public BlockLocation[] getEntrances() {
if (entrances == null) {
//Get the locations of the entrances once, and only if necessary as it's an expensive operation
entrances = relativeBlockVectorsToBlockLocations(gate.getLayout().getEntrances());
}
return entrances;
}
/**
* Gets the locations of this portal's frame
*
* @return <p>The locations of this portal's frame</p>
*/
public BlockLocation[] getFrame() {
if (frame == null) {
//Get the locations of the frame blocks once, and only if necessary as it's an expensive operation
frame = relativeBlockVectorsToBlockLocations(gate.getLayout().getBorder());
}
return frame;
}
}

View File

@ -0,0 +1,250 @@
package net.knarcraft.stargate.portal;
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.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EntityHelper;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Slab;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Entity;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.ArrayList;
import java.util.List;
/**
* The portal teleporter takes care of common teleportation logic
*/
public abstract class Teleporter {
/**
* The portal the entity is teleporting to
*/
protected final Portal portal;
/**
* The scheduler to use for delaying tasks
*/
protected final BukkitScheduler scheduler;
/**
* Instantiates a new portal teleporter
*
* @param portal <p>The portal which is the target of the teleportation</p>
*/
public Teleporter(Portal portal) {
this.portal = portal;
this.scheduler = Stargate.getInstance().getServer().getScheduler();
}
/**
* Adjusts the rotation of the exit to make the teleporting entity face directly out from the portal
*
* @param exit <p>The location the entity will exit from</p>
*/
protected void adjustRotation(Location exit) {
int adjust = 0;
if (portal.getOptions().isBackwards()) {
adjust = 180;
}
float newYaw = (portal.getYaw() + adjust) % 360;
Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw);
exit.setYaw(newYaw);
}
/**
* Gets the exit location for a given entity and current location
*
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
* @param traveller <p>The location of the entity travelling</p>
* @return <p>The location the entity should be teleported to.</p>
*/
public Location getExit(Entity entity, Location traveller) {
Location exitLocation = null;
RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit();
if (relativeExit != null) {
BlockLocation exit = portal.getBlockAt(relativeExit);
//Move one block out to prevent exiting inside the portal
float portalYaw = portal.getYaw();
if (portal.getOptions().isBackwards()) {
portalYaw += 180;
}
exitLocation = exit.getRelativeLocation(0D, 0D, 1, portalYaw);
if (entity != null) {
double entitySize = EntityHelper.getEntityMaxSize(entity);
//Prevent exit suffocation for players riding horses or similar
if (entitySize > 1) {
exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity);
}
}
} else {
Stargate.logWarning(String.format("Missing destination point in .gate file %s",
portal.getGate().getFilename()));
}
//Adjust pitch and height
return adjustExitLocation(traveller, exitLocation);
}
/**
* Adjusts the positioning of the portal exit to prevent the given entity from suffocating
*
* @param relativeExit <p>The relative exit defined as the portal's exit</p>
* @param exitLocation <p>The currently calculated portal exit</p>
* @param entity <p>The travelling entity</p>
* @return <p>A location which won't suffocate the entity inside the portal</p>
*/
private Location preventExitSuffocation(RelativeBlockVector relativeExit, Location exitLocation, Entity entity) {
//Go left to find start of opening
RelativeBlockVector openingLeft = getPortalExitEdge(relativeExit, -1);
//Go right to find the end of the opening
RelativeBlockVector openingRight = getPortalExitEdge(relativeExit, 1);
//Get the width to check if the entity fits
int openingWidth = openingRight.getRight() - openingLeft.getRight() + 1;
int existingOffset = relativeExit.getRight() - openingLeft.getRight();
double newOffset = (openingWidth - existingOffset) / 2D;
//Remove the half offset for better centering
if (openingWidth > 1) {
newOffset -= 0.5;
}
exitLocation = DirectionHelper.moveLocation(exitLocation, newOffset, 0, 0, portal.getYaw());
//Move large entities further from the portal
return moveExitLocationOutwards(exitLocation, entity);
}
/**
* Moves the exit location out from the portal to prevent the entity from entering a teleportation loop
*
* @param exitLocation <p>The current exit location to adjust</p>
* @param entity <p>The entity to adjust the exit location for</p>
* @return <p>The adjusted exit location</p>
*/
private Location moveExitLocationOutwards(Location exitLocation, Entity entity) {
double entitySize = EntityHelper.getEntityMaxSize(entity);
int entityBoxSize = EntityHelper.getEntityMaxSizeInt(entity);
if (entitySize > 1) {
double entityOffset;
if (portal.getOptions().isAlwaysOn()) {
entityOffset = entityBoxSize / 2D;
} else {
entityOffset = (entitySize / 2D) - 1;
}
//If a horse has a player riding it, the player will spawn inside the roof of a standard portal unless it's
// moved one block out.
if (entity instanceof AbstractHorse) {
entityOffset += 1;
}
exitLocation = DirectionHelper.moveLocation(exitLocation, 0, 0, entityOffset, portal.getYaw());
}
return exitLocation;
}
/**
* Gets one of the edges of a portal's opening/exit
*
* @param relativeExit <p>The known exit to start from</p>
* @param direction <p>The direction to move (+1 for right, -1 for left)</p>
* @return <p>The right or left edge of the opening</p>
*/
private RelativeBlockVector getPortalExitEdge(RelativeBlockVector relativeExit, int direction) {
RelativeBlockVector openingEdge = relativeExit;
do {
RelativeBlockVector possibleOpening = new RelativeBlockVector(openingEdge.getRight() + direction,
openingEdge.getDown(), openingEdge.getOut());
if (portal.getGate().getLayout().getExits().contains(possibleOpening)) {
openingEdge = possibleOpening;
} else {
break;
}
} while (true);
return openingEdge;
}
/**
* Adjusts an exit location by setting pitch and adjusting height
*
* <p>If the exit location is a slab or water, the exit location will be changed to arrive one block above. The
* slab check is necessary to prevent the player from clipping through the slab and spawning beneath it. The water
* check is necessary when teleporting boats to prevent it from becoming a submarine.</p>
*
* @param traveller <p>The location of the travelling entity</p>
* @param exitLocation <p>The exit location generated</p>
* @return <p>The location the travelling entity should be teleported to</p>
*/
private Location adjustExitLocation(Location traveller, Location exitLocation) {
if (exitLocation != null) {
BlockData blockData = portal.getWorld().getBlockAt(exitLocation).getBlockData();
if ((blockData instanceof Bisected && ((Bisected) blockData).getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab) && ((Slab) blockData).getType() == Slab.Type.BOTTOM) {
//Prevent traveller from spawning inside a slab
Stargate.debug("adjustExitLocation", "Added a block to get above a slab");
exitLocation.add(0, 1, 0);
} else if (blockData.getMaterial() == Material.WATER) {
//If there's water outside, go one up to allow for boat teleportation
Stargate.debug("adjustExitLocation", "Added a block to get above a block of water");
exitLocation.add(0, 1, 0);
}
exitLocation.setPitch(traveller.getPitch());
return exitLocation;
} else {
Stargate.logWarning("Unable to generate exit location");
return traveller;
}
}
/**
* Loads the chunks outside the portal's entrance
*/
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));
}
}
/**
* Gets all relevant chunks near this teleporter's portal's entrance which need to be loaded before teleportation
*
* @return <p>A list of chunks to load</p>
*/
private List<Chunk> getChunksToLoad() {
List<Chunk> chunksToLoad = new ArrayList<>();
for (RelativeBlockVector vector : portal.getGate().getLayout().getEntrances()) {
BlockLocation entranceLocation = portal.getBlockAt(vector);
Chunk chunk = entranceLocation.getChunk();
//Make sure not to load chunks twice
if (!chunksToLoad.contains(chunk)) {
chunksToLoad.add(chunk);
}
//Get the chunk in front of the gate entrance
int blockOffset = portal.getOptions().isBackwards() ? -5 : 5;
Location fiveBlocksForward = DirectionHelper.moveLocation(entranceLocation, 0, 0, blockOffset,
portal.getYaw());
//Load the chunk five blocks forward to make sure the teleported entity will never spawn in unloaded chunks
Chunk forwardChunk = fiveBlocksForward.getChunk();
if (!chunksToLoad.contains(forwardChunk)) {
chunksToLoad.add(forwardChunk);
}
}
return chunksToLoad;
}
}

View File

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

View File

@ -0,0 +1,76 @@
package net.knarcraft.stargate.thread;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockChangeRequest;
import org.bukkit.Axis;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.EndGateway;
import org.bukkit.block.data.Orientable;
/**
* This thread changes gate blocks to display a gate as open or closed
*
* <p>This thread fetches some entries from blockPopulateQueue each time it's called.</p>
*/
public class BlockChangeThread implements Runnable {
@Override
public void run() {
long sTime = System.nanoTime();
//Repeat for at most 0.025 seconds
while (System.nanoTime() - sTime < 25000000) {
pollQueue();
}
}
/**
* Polls the block change request queue for any waiting requests
*/
public static void pollQueue() {
//Abort if there's no work to be done
BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll();
if (blockChangeRequest == null) {
return;
}
//Change the material of the pulled block
Block block = blockChangeRequest.getBlockLocation().getBlock();
block.setType(blockChangeRequest.getMaterial(), false);
if (blockChangeRequest.getMaterial() == Material.END_GATEWAY &&
block.getWorld().getEnvironment() == World.Environment.THE_END) {
//Force a specific location to prevent exit gateway generation
fixEndGatewayGate(block);
} else if (blockChangeRequest.getAxis() != null) {
//If orientation is relevant, adjust the block's orientation
orientBlock(block, blockChangeRequest.getAxis());
}
}
/**
* Prevents end gateway portal from behaving strangely
*
* @param block <p>The block to fix</p>
*/
private static void fixEndGatewayGate(Block block) {
EndGateway gateway = (EndGateway) block.getState();
gateway.setExitLocation(block.getLocation());
gateway.setExactTeleport(true);
gateway.update(false, false);
}
/**
* Sets the orientation axis of the placed block
*
* @param block <p>The block to orient</p>
* @param axis <p>The axis to use for orienting the block</p>
*/
private static void orientBlock(Block block, Axis axis) {
Orientable orientable = (Orientable) block.getBlockData();
orientable.setAxis(axis);
block.setBlockData(orientable);
}
}

View File

@ -0,0 +1,31 @@
package net.knarcraft.stargate.thread;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.ChunkUnloadRequest;
import org.bukkit.Chunk;
import java.util.Queue;
/**
* Unloads chunks which should no longer be forced to stay loaded
*/
public class ChunkUnloadThread implements Runnable {
@Override
public void run() {
long systemNanoTime = System.nanoTime();
Queue<ChunkUnloadRequest> unloadQueue = Stargate.getChunkUnloadQueue();
//Peek at the first element to check if the chunk should be unloaded
ChunkUnloadRequest firstElement = unloadQueue.peek();
//Repeat until all un-loadable chunks have been processed
while (firstElement != null && firstElement.getUnloadNanoTime() < systemNanoTime) {
unloadQueue.remove();
Chunk chunkToUnload = firstElement.getChunkToUnload();
//Allow the chunk to be unloaded
chunkToUnload.removePluginChunkTicket(Stargate.getInstance());
firstElement = unloadQueue.peek();
}
}
}

View File

@ -0,0 +1,67 @@
package net.knarcraft.stargate.thread;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
/**
* This class contains the function used to close servers which should no longer be open/active
*/
public class StarGateThread implements Runnable {
@Override
public void run() {
long time = System.currentTimeMillis() / 1000;
closeOpenPortals(time);
deactivateActivePortals(time);
}
/**
* Closes portals which are open and have timed out
*
* @param time <p>The current time</p>
*/
private void closeOpenPortals(long time) {
List<Portal> closedPortals = new ArrayList<>();
Queue<Portal> openPortalsQueue = Stargate.getStargateConfig().getOpenPortalsQueue();
for (Portal portal : openPortalsQueue) {
//Skip always open and non-open gates
if (portal.getOptions().isAlwaysOn() || portal.getOptions().isRandom() || portal.getOptions().isBungee() ||
!portal.isOpen()) {
continue;
}
if (time > portal.getTriggeredTime() + Stargate.getGateConfig().getOpenTime()) {
portal.getPortalOpener().closePortal(false);
closedPortals.add(portal);
}
}
openPortalsQueue.removeAll(closedPortals);
}
/**
* De-activates portals which are active and have timed out
*
* @param time <p>The current time</p>
*/
private void deactivateActivePortals(long time) {
List<Portal> deactivatedPortals = new ArrayList<>();
Queue<Portal> activePortalsQueue = Stargate.getStargateConfig().getActivePortalsQueue();
for (Portal portal : activePortalsQueue) {
//Skip portals which aren't active
if (!portal.getPortalActivator().isActive()) {
continue;
}
if (time > portal.getTriggeredTime() + Stargate.getGateConfig().getActiveTime()) {
portal.getPortalActivator().deactivate();
deactivatedPortals.add(portal);
}
}
activePortalsQueue.removeAll(deactivatedPortals);
}
}

View File

@ -0,0 +1,172 @@
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 org.bukkit.entity.Player;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* This class contains helpful functions to help with sending and receiving BungeeCord plugin messages
*/
public final class BungeeHelper {
private final static String bungeeSubChannel = "SGBungee";
private final static String bungeeChannel = "BungeeCord";
private final static String teleportMessageDelimiter = "#@#";
private final static Map<UUID, String> bungeeQueue = new HashMap<>();
private BungeeHelper() {
}
/**
* Get the plugin message channel use for BungeeCord messages
*
* @return <p>The bungee plugin channel</p>
*/
public static String getBungeeChannel() {
return bungeeChannel;
}
/**
* Removes a player from the queue of players teleporting through BungeeCord
*
* <p>Whenever a BungeeCord teleportation message is received and the player is not currently connected to this
* server, it'll be added to this queue. Once the player joins this server, the player should be removed from the
* queue and teleported to the destination.</p>
*
* @param playerUUID <p>The UUID of the player to remove</p>
* @return <p>The name of the destination portal the player should be teleported to</p>
*/
public static String removeFromQueue(UUID playerUUID) {
return bungeeQueue.remove(playerUUID);
}
/**
* Sends a plugin message to BungeeCord allowing the target server to catch it
*
* @param player <p>The teleporting player</p>
* @param entrancePortal <p>The portal the player is teleporting from</p>
* @return <p>True if the message was successfully sent</p>
*/
public static boolean sendTeleportationMessage(Player player, Portal entrancePortal) {
try {
//Build the teleportation message, format is <player identifier>delimiter<destination>
String message = player.getUniqueId() + teleportMessageDelimiter + entrancePortal.getDestinationName();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
//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());
//Specify the sub-channel/tag to make it recognizable on arrival
dataOutputStream.writeUTF(bungeeSubChannel);
//Write the length of the message
dataOutputStream.writeShort(message.length());
//Write the actual message
dataOutputStream.writeBytes(message);
//Send the plugin message
player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray());
} catch (IOException ex) {
Stargate.logSevere("Error sending BungeeCord teleport packet");
ex.printStackTrace();
return false;
}
return true;
}
/**
* Sends the bungee message necessary to make a player connect to another server
*
* @param player <p>The player to teleport</p>
* @param entrancePortal <p>The bungee portal the player is teleporting from</p>
* @return <p>True if the plugin message was sent successfully</p>
*/
public static boolean changeServer(Player player, Portal entrancePortal) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
//Send a connect-message to connect the player to the server defined in the entrance portal's network line
dataOutputStream.writeUTF("Connect");
dataOutputStream.writeUTF(entrancePortal.getNetwork());
//Send the plugin message
player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray());
} catch (IOException ex) {
Stargate.logSevere("Error sending BungeeCord connect packet");
ex.printStackTrace();
return false;
}
return true;
}
/**
* Reads a plugin message byte array to a string if it's sent from another stargate plugin
*
* @param message <p>The byte array to read</p>
* @return <p>The message contained in the byte array, or null on failure</p>
*/
public static String readPluginMessage(byte[] message) {
byte[] data;
try {
DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(message));
String subChannel = dataInputStream.readUTF();
//Only listen for the SGBungee channel
if (!subChannel.equals(bungeeSubChannel)) {
return null;
}
//Get the length of the contained message
short dataLength = dataInputStream.readShort();
//Prepare a byte array for the sent message
data = new byte[dataLength];
//Read the message to the prepared array
dataInputStream.readFully(data);
} catch (IOException ex) {
Stargate.logSevere("Error receiving BungeeCord message");
ex.printStackTrace();
return null;
}
return new String(data);
}
/**
* Handles the receival of a teleport message
*
* @param receivedMessage <p>The received teleport message</p>
*/
public static void handleTeleportMessage(String receivedMessage) {
//Get the player id and destination from the message
String[] messageParts = receivedMessage.split(teleportMessageDelimiter);
UUID playerUUID = UUID.fromString(messageParts[0]);
String destination = messageParts[1];
//Check if the player is online, if so, teleport, otherwise, queue
Player player = Stargate.getInstance().getServer().getPlayer(playerUUID);
if (player == null) {
bungeeQueue.put(playerUUID, destination);
} else {
Portal destinationPortal = PortalHandler.getBungeePortal(destination);
//If teleporting to an invalid portal, let the server decide where the player arrives
if (destinationPortal == null) {
Stargate.logInfo(String.format("Bungee portal %s does not exist", destination));
return;
}
new PlayerTeleporter(destinationPortal, player).teleport(destinationPortal, null);
}
}
}

View File

@ -0,0 +1,142 @@
package net.knarcraft.stargate.utility;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.util.Vector;
/**
* This class helps with direction-related calculations
*/
public final class DirectionHelper {
private DirectionHelper() {
}
/**
* Gets a yaw by comparing two locations
*
* <p>The yaw here is the direction an observer a the first location has to look to face the second location.
* The yaw is only meant to be calculated for locations where both have either the same x value or the same z value.
* Equal locations, or locations with equal x and equal z will throw an exception.</p>
*
* @param location1 <p>The first location, which works as the origin</p>
* @param location2 <p>The second location, which the yaw will point towards</p>
* @return <p>The yaw pointing from the first location to the second location</p>
*/
public static float getYawFromLocationDifference(Location location1, Location location2) {
Location difference = location1.clone().subtract(location2.clone());
if (difference.getX() > 0) {
return 90;
} else if (difference.getX() < 0) {
return 270;
} else if (difference.getZ() > 0) {
return 180;
} else if (difference.getZ() < 0) {
return 0;
}
throw new IllegalArgumentException("Locations given are equal or at the same x and y axis");
}
/**
* Gets a block face given a yaw value
*
* <p>The supplied yaw must be a value such that (yaw mod 90) = 0. If not, an exception is thrown.</p>
*
* @param yaw <p>The yaw value to convert</p>
* @return <p>The block face the yaw corresponds to</p>
*/
public static BlockFace getBlockFaceFromYaw(double yaw) {
//Make sure the yaw is between 0 and 360
yaw = normalizeYaw(yaw);
if (yaw == 0) {
return BlockFace.SOUTH;
} else if (yaw == 90) {
return BlockFace.WEST;
} else if (yaw == 180) {
return BlockFace.NORTH;
} else if (yaw == 270) {
return BlockFace.EAST;
} else {
throw new IllegalArgumentException("Invalid yaw given. Yaw must be divisible by 90.");
}
}
/**
* Gets a direction vector given a yaw
*
* @param yaw <p>The yaw to convert to a direction vector</p>
* @return <p>The direction vector pointing in the same direction as the yaw</p>
*/
public static Vector getDirectionVectorFromYaw(double yaw) {
//Make sure the yaw is between 0 and 360
yaw = normalizeYaw(yaw);
if (yaw == 0) {
return new Vector(0, 0, 1);
} else if (yaw == 90) {
return new Vector(-1, 0, 0);
} else if (yaw == 180) {
return new Vector(0, 0, -1);
} else if (yaw == 270) {
return new Vector(1, 0, 0);
} else {
throw new IllegalArgumentException(String.format("Invalid yaw %f given", yaw));
}
}
/**
* Moves a location by the given amounts
*
* <p>The right, down and out work the same as for the relative block vector. Looking a the front of a portal,
* right goes rightwards, down goes downwards and out goes towards the observer.</p>
*
* @param location <p>The location to start at</p>
* @param right <p>The amount to go right</p>
* @param down <p>The amount to go downward</p>
* @param out <p>The amount to go outward</p>
* @param yaw <p>The yaw when looking directly outwards from a portal</p>
* @return <p>A location relative to the given location</p>
*/
public static Location moveLocation(Location location, double right, double down, double out, double yaw) {
return location.add(getCoordinateVectorFromRelativeVector(right, down, out, yaw));
}
/**
* Gets a vector in Minecraft's normal X,Y,Z-space from a relative block vector
*
* @param right <p>The amount of rightward steps from the top-left origin</p>
* @param down <p>The amount of downward steps from the top-left origin</p>
* @param out <p>The distance outward from the top-left origin</p>
* @param yaw <p>The yaw when looking directly outwards from a portal</p>
* @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);
}
/**
* Normalizes a yaw to make it positive and no larger than 360 degrees
*
* @param yaw <p>The yaw to normalize</p>
* @return <p>The normalized yaw</p>
*/
private static double normalizeYaw(double yaw) {
while (yaw < 0) {
yaw += 360;
}
yaw = yaw % 360;
return yaw;
}
}

View File

@ -0,0 +1,134 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalOwner;
import org.bukkit.entity.Player;
import java.util.UUID;
/**
* The economy helper class has helper functions for player payment
*/
public final class EconomyHelper {
private EconomyHelper() {
}
/**
* Tries to make the given user pay the teleport fee
*
* @param entrancePortal <p>The portal the player is entering</p>
* @param player <p>The player wishing to teleport</p>
* @param cost <p>The cost of teleportation</p>
* @return <p>False if payment was successful. True if the payment was unsuccessful</p>
*/
public static boolean cannotPayTeleportFee(Portal entrancePortal, Player player, int cost) {
boolean success;
//Try to charge the player. Paying the portal owner is only possible if a UUID is available
UUID ownerUUID = entrancePortal.getOwner().getUUID();
if (ownerUUID == null) {
Stargate.logWarning(String.format("The owner of the portal %s does not have a UUID and payment to owner " +
"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);
} else {
success = Stargate.getEconomyConfig().chargePlayerIfNecessary(player, cost);
}
//Send the insufficient funds message
if (!success) {
sendInsufficientFundsMessage(entrancePortal.getName(), player, cost);
entrancePortal.getPortalOpener().closePortal(false);
return true;
}
//Send the deduct-message to the player
sendDeductMessage(entrancePortal.getName(), player, cost);
if (entrancePortal.getGate().getToOwner()) {
PortalOwner owner = entrancePortal.getOwner();
Player portalOwner;
if (owner.getUUID() != null) {
portalOwner = Stargate.getInstance().getServer().getPlayer(owner.getUUID());
} else {
portalOwner = Stargate.getInstance().getServer().getPlayer(owner.getName());
}
//Notify the gate owner of received payment
if (portalOwner != null) {
sendObtainMessage(entrancePortal.getName(), portalOwner, cost);
}
}
return false;
}
/**
* Sends a message to the gate owner telling him/her how much he/she earned from a player using his/her gate
*
* @param portalName <p>The name of the used portal</p>
* @param portalOwner <p>The owner of the portal</p>
* @param earnings <p>The amount the owner earned</p>
*/
public static void sendObtainMessage(String portalName, Player portalOwner, int earnings) {
String obtainedMsg = Stargate.getString("ecoObtain");
obtainedMsg = replaceVars(obtainedMsg, portalName, earnings);
Stargate.getMessageSender().sendSuccessMessage(portalOwner, obtainedMsg);
}
/**
* Sends a message telling the user how much they paid for interacting with a portal
*
* @param portalName <p>The name of the portal interacted with</p>
* @param player <p>The interacting player</p>
* @param cost <p>The cost of the interaction</p>
*/
public static void sendDeductMessage(String portalName, Player player, int cost) {
String deductMsg = Stargate.getString("ecoDeduct");
deductMsg = replaceVars(deductMsg, portalName, cost);
Stargate.getMessageSender().sendSuccessMessage(player, deductMsg);
}
/**
* Sends a message telling the user they don't have enough funds to do a portal interaction
*
* @param portalName <p>The name of the portal interacted with</p>
* @param player <p>The interacting player</p>
* @param cost <p>The cost of the interaction</p>
*/
public static void sendInsufficientFundsMessage(String portalName, Player player, int cost) {
String inFundMsg = Stargate.getString("ecoInFunds");
inFundMsg = replaceVars(inFundMsg, portalName, cost);
Stargate.getMessageSender().sendErrorMessage(player, inFundMsg);
}
/**
* Sends a message telling the user how much they are refunded for breaking their portal
*
* @param portalName <p>The name of the broken portal</p>
* @param player <p>The player breaking the portal</p>
* @param cost <p>The amount the user has to pay for destroying the portal. (expects a negative value)</p>
*/
public static void sendRefundMessage(String portalName, Player player, int cost) {
String refundMsg = Stargate.getString("ecoRefund");
refundMsg = replaceVars(refundMsg, portalName, -cost);
Stargate.getMessageSender().sendSuccessMessage(player, refundMsg);
}
/**
* Replaces the cost and portal variables in a string
*
* @param message <p>The message to replace variables in</p>
* @param portalName <p>The name of the relevant portal</p>
* @param cost <p>The cost for a given interaction</p>
* @return <p>The same string with cost and portal variables replaced</p>
*/
private static String replaceVars(String message, String portalName, int cost) {
return Stargate.replaceVars(message, new String[]{"%cost%", "%portal%"},
new String[]{Stargate.getEconomyConfig().format(cost), portalName});
}
}

View File

@ -0,0 +1,38 @@
package net.knarcraft.stargate.utility;
import org.bukkit.entity.Entity;
/**
* This helper class helps with entity properties not immediately available
*/
public final class EntityHelper {
private EntityHelper() {
}
/**
* Gets the max size of an entity along its x and z axis
*
* <p>This function gets the ceiling of the max size of an entity, thus calculating the smallest box, using whole
* blocks as unit, needed to contain the entity. Assuming n is returned, an (n x n) box is needed to contain the
* entity.</p>
*
* @param entity <p>The entity to get max size for</p>
* @return <p>The max size of the entity</p>
*/
public static int getEntityMaxSizeInt(Entity entity) {
return (int) Math.ceil((float) getEntityMaxSize(entity));
}
/**
* Gets the max size of an entity along its x and z axis
*
* @param entity <p>The entity to get max size for</p>
* @return <p>The max size of the entity</p>
*/
public static double getEntityMaxSize(Entity entity) {
return Math.max(entity.getBoundingBox().getWidthX(), entity.getBoundingBox().getWidthZ());
}
}

View File

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

View File

@ -0,0 +1,210 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import org.bukkit.Material;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
/**
* Helper class for reading gate files
*/
public final class GateReader {
private GateReader() {
}
/**
* Reads a gate file
*
* @param scanner <p>The scanner to read from</p>
* @param characterMaterialMap <p>The map of characters to store valid symbols in</p>
* @param fileName <p>The filename of the loaded gate config file</p>
* @param design <p>The list to store the loaded design/layout to</p>
* @param frameTypes <p>The set to store frame/border materials to</p>
* @param config <p>The map of config values to store to</p>
* @return <p>The column count/width of the loaded gate</p>
*/
public static int readGateFile(Scanner scanner, Map<Character, Material> characterMaterialMap, String fileName,
List<List<Character>> design, Set<Material> frameTypes, Map<String, String> config) {
boolean designing = false;
int columns = 0;
try {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (designing) {
//If we have reached the gate's layout/design, read it
columns = readGateDesignLine(line, columns, characterMaterialMap, fileName, design);
if (columns < 0) {
return -1;
}
} else {
if (!line.isEmpty() && !line.startsWith("#")) {
//Read a normal config value
readGateConfigValue(line, characterMaterialMap, frameTypes, config);
} else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) {
//An empty line marks the start of the gate's layout/design
designing = true;
}
}
}
} catch (Exception exception) {
Stargate.logSevere(String.format("Could not load Gate %s - %s", fileName, exception.getMessage()));
return -1;
} finally {
if (scanner != null) {
scanner.close();
}
}
return columns;
}
/**
* Reads one design line of the gate layout file
*
* <p>The max columns value is sent through this method in such a way that when the last gate design line is read,
* the max columns value contains the largest amount of columns (character) found in any of the design's lines.</p>
*
* @param line <p>The line to read</p>
* @param maxColumns <p>The current max columns value of the design</p>
* @param characterMaterialMap <p>The map between characters and the corresponding materials to use</p>
* @param fileName <p>The filename of the loaded gate config file</p>
* @param design <p>The two-dimensional list to store the loaded design to</p>
* @return <p>The new max columns value of the design</p>
*/
private static int readGateDesignLine(String line, int maxColumns, Map<Character, Material> characterMaterialMap,
String fileName, List<List<Character>> design) {
List<Character> row = new ArrayList<>();
//Update the max columns number if this line has more columns
if (line.length() > maxColumns) {
maxColumns = line.length();
}
for (Character symbol : line.toCharArray()) {
//Refuse read gate designs with unknown characters
if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol))) {
Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName,
symbol));
return -1;
}
//Add the read character to the row
row.add(symbol);
}
//Add this row of the gate's design to the two-dimensional design list
design.add(row);
return maxColumns;
}
/**
* Reads one config value from the gate layout file
*
* @param line <p>The line to read</p>
* @param characterMaterialMap <p>The character to material map to store to</p>
* @param frameTypes <p>The set to store gate frame/border types to</p>
* @param config <p>The config value map to store to</p>
* @throws Exception <p>If an invalid material is encountered</p>
*/
private static void readGateConfigValue(String line, Map<Character, Material> characterMaterialMap,
Set<Material> frameTypes, Map<String, String> config) throws Exception {
String[] split = line.split("=");
String key = split[0].trim();
String value = split[1].trim();
if (key.length() == 1) {
//Read a gate frame material
Character symbol = key.charAt(0);
Material material = Material.getMaterial(value);
if (material == null) {
throw new Exception("Invalid material in line: " + line);
}
//Register the map between the read symbol and the corresponding material
characterMaterialMap.put(symbol, material);
//Save the material as one of the frame materials used for this kind of gate
frameTypes.add(material);
} else {
//Read a normal config value
config.put(key, value);
}
}
/**
* Reads an integer configuration value
*
* @param config <p>The configuration to read</p>
* @param fileName <p>The filename of the config file</p>
* @param key <p>The config key to read</p>
* @return <p>The read value, or -1 if it could not be read</p>
*/
public static int readGateConfig(Map<String, String> config, String fileName, String key) {
if (config.containsKey(key)) {
try {
return Integer.parseInt(config.get(key));
} catch (NumberFormatException ex) {
Stargate.logWarning(String.format("%s reading %s: %s is not numeric", ex.getClass().getName(),
fileName, key));
}
}
return -1;
}
/**
* Reads a material configuration value
*
* @param config <p>The configuration to read</p>
* @param fileName <p>The filename of the config file</p>
* @param key <p>The config key to read</p>
* @param defaultMaterial <p>The default material to use, in case the config is invalid</p>
* @return <p>The material specified in the config, or the default material if it could not be read</p>
*/
public static Material readGateConfig(Map<String, String> config, String fileName, String key,
Material defaultMaterial) {
if (config.containsKey(key)) {
Material material = Material.getMaterial(config.get(key));
if (material != null) {
return material;
} else {
Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key));
}
}
return defaultMaterial;
}
/**
* Generates a matrix containing the gate layout
*
* <p>This basically changes the list of lists into a primitive matrix. Additionally, spaces are added to the end of
* each row which to too short relative to the longest row.</p>
*
* @param design <p>The design of the gate layout</p>
* @param columns <p>The largest amount of columns in the design</p>
* @return <p>A matrix containing the gate's layout</p>
*/
public static Character[][] generateLayoutMatrix(List<List<Character>> design, int columns) {
Character[][] layout = new Character[design.size()][columns];
for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) {
List<Character> row = design.get(lineIndex);
Character[] result = new Character[columns];
for (int rowIndex = 0; rowIndex < columns; rowIndex++) {
if (rowIndex < row.size()) {
result[rowIndex] = row.get(rowIndex);
} else {
//Add spaces to all lines which are too short
result[rowIndex] = ' ';
}
}
layout[lineIndex] = result;
}
return layout;
}
}

View File

@ -0,0 +1,42 @@
package net.knarcraft.stargate.utility;
import org.bukkit.Material;
import org.bukkit.Tag;
/**
* This class helps decide properties of materials not already present in the Spigot API
*/
public final class MaterialHelper {
private MaterialHelper() {
}
/**
* Checks whether the given material is a dead or alive wall coral
*
* @param material <p>The material to check</p>
* @return <p>True if the material is a wall coral</p>
*/
public static boolean isWallCoral(Material material) {
//Unfortunately, there is no tag for dead wall corals, so they need to be checked manually
return Tag.WALL_CORALS.isTagged(material) ||
material.equals(Material.DEAD_BRAIN_CORAL_WALL_FAN) ||
material.equals(Material.DEAD_BUBBLE_CORAL_WALL_FAN) ||
material.equals(Material.DEAD_FIRE_CORAL_WALL_FAN) ||
material.equals(Material.DEAD_HORN_CORAL_WALL_FAN) ||
material.equals(Material.DEAD_TUBE_CORAL_WALL_FAN);
}
/**
* Checks whether the given material can be used as a button
*
* @param material <p>The material to check</p>
* @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;
}
}

View File

@ -0,0 +1,403 @@
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 org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent;
/**
* Helper class for deciding which actions a player is allowed to perform
*/
public final class PermissionHelper {
private PermissionHelper() {
}
/**
* Opens a portal if the given player is allowed to, and if the portal is not already open
*
* @param player <p>The player opening the portal</p>
* @param portal <p>The portal to open</p>
*/
public static void openPortal(Player player, Portal portal) {
Portal destination = portal.getPortalActivator().getDestination();
//For an always open portal, no action is necessary
if (portal.getOptions().isAlwaysOn() || portal.getOptions().isRandom() || portal.getOptions().isBungee()) {
return;
}
//Destination is invalid or the same portal. Send an error message
if (destination == null || destination == portal) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg"));
return;
}
//Portal is already open
if (portal.isOpen()) {
//Close the portal if this player opened the portal
if (portal.getActivePlayer() == player) {
portal.getPortalOpener().closePortal(false);
}
return;
}
//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"));
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"));
return;
}
//Destination is currently in use by another player, blocking teleportation
if (destination.isOpen() && !destination.getOptions().isAlwaysOn()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("blockMsg"));
return;
}
//Open the portal
portal.getPortalOpener().openPortal(player, false);
}
/**
* Creates a StargateAccessEvent and gets the updated deny value
*
* <p>The event is used for other plugins to bypass the permission checks.</p>
*
* @param player <p>The player trying to use the portal</p>
* @param portal <p>The portal the player is trying to use</p>
* @param deny <p>Whether the player's access has already been denied by a previous check</p>
* @return <p>False if the player should be allowed through the portal</p>
*/
public static boolean portalAccessDenied(Player player, Portal portal, boolean deny) {
StargateAccessEvent event = new StargateAccessEvent(player, portal, deny);
Stargate.getInstance().getServer().getPluginManager().callEvent(event);
return event.getDeny();
}
/**
* Checks whether a given user cannot travel between two portals
*
* @param player <p>The player to check</p>
* @param entrancePortal <p>The portal the user wants to enter</p>
* @param destination <p>The portal the user wants to exit from</p>
* @return <p>False if the user is allowed to access the portal</p>
*/
public static boolean cannotAccessPortal(Player player, Portal entrancePortal, Portal destination) {
boolean deny = false;
if (entrancePortal.getOptions().isBungee()) {
if (!PermissionHelper.canAccessServer(player, entrancePortal.getNetwork())) {
//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())) {
//If the player does not have access to the network, deny
Stargate.debug("cannotAccessPortal", "Cannot access network");
deny = true;
} else if (PermissionHelper.cannotAccessWorld(player, destination.getWorld().getName())) {
//If the player does not have access to the portal's world, deny
Stargate.debug("cannotAccessPortal", "Cannot access world");
deny = true;
}
//Allow other plugins to override whether the player can access the portal
return portalAccessDenied(player, entrancePortal, deny);
}
/**
* Checks whether a player has the given permission
*
* <p>This is the same as player.hasPermission(), but this function allows for printing permission debugging info.</p>
*
* @param player <p>The player to check</p>
* @param permission <p>The permission to check</p>
* @return <p>True if the player has the permission</p>
*/
public static boolean hasPermission(Player player, String permission) {
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) {
Stargate.debug("hasPerm::Permission(" + player.getName() + ")", permission + " => " +
player.hasPermission(permission));
}
return player.hasPermission(permission);
}
/**
* Check if a player has been given a permission implicitly
*
* <p>This should be run if a player has a parent permission to check for the child permission. It is assumed the
* player has the child permission unless it's explicitly set to false.</p>
*
* @param player <p>The player to check</p>
* @param permission <p>The permission to check</p>
* @return <p>True if the player has the permission implicitly or explicitly</p>
*/
public static boolean hasPermissionImplicit(Player player, String permission) {
if (!player.isPermissionSet(permission)) {
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) {
Stargate.debug("hasPermissionImplicit::Permission", permission + " => implicitly true");
}
return true;
}
if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) {
Stargate.debug("hasPermissionImplicit::Permission", permission + " => " +
player.hasPermission(permission));
}
return player.hasPermission(permission);
}
/**
* Checks whether a player can access the given world
*
* @param player <p>The player trying to access the world</p>
* @param world <p>The world the player is trying to access</p>
* @return <p>False if the player should be allowed to access the world</p>
*/
public static boolean cannotAccessWorld(Player player, String world) {
//The player can access all worlds
if (hasPermission(player, "stargate.world")) {
//Check if the world permission has been explicitly denied
return !hasPermissionImplicit(player, "stargate.world." + world);
}
//The player can access the destination world
return !hasPermission(player, "stargate.world." + world);
}
/**
* Checks whether a player can access the given network
*
* @param player <p>The player to check</p>
* @param network <p>The network to check</p>
* @return <p>True if the player is denied from accessing the network</p>
*/
public static boolean cannotAccessNetwork(Player player, String network) {
//The player can access all networks
if (hasPermission(player, "stargate.network")) {
//Check if the world permission has been explicitly denied
return !hasPermissionImplicit(player, "stargate.network." + network);
}
//Check if the player can access this network
if (hasPermission(player, "stargate.network." + network)) {
return false;
}
//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);
}
return !network.equals(playerName) || !hasPermission(player, "stargate.create.personal");
}
/**
* Checks whether a player can access the given bungee server
*
* @param player <p>The player trying to teleport</p>
* @param server <p>The server the player is trying to connect to</p>
* @return <p>True if the player is allowed to access the given server</p>
*/
public static boolean canAccessServer(Player player, String server) {
//The player can access all servers
if (hasPermission(player, "stargate.server")) {
//Check if the server permission has been explicitly denied
return hasPermissionImplicit(player, "stargate.server." + server);
}
//The player can access the destination server
return hasPermission(player, "stargate.server." + server);
}
/**
* Checks whether the given player can teleport the given stretch for free
*
* @param player <p>The player trying to teleport</p>
* @param src <p>The portal the player is entering</p>
* @param dest <p>The portal the player wants to teleport to</p>
* @return <p>True if the player can travel for free</p>
*/
public static boolean isFree(Player player, Portal src, Portal dest) {
//This portal is free
if (src.getOptions().isFree()) {
return true;
}
//Player can use this portal for free
if (hasPermission(player, "stargate.free.use")) {
return true;
}
//Don't charge for free destinations unless specified in the config
return dest != null && !Stargate.getEconomyConfig().chargeFreeDestination() && dest.getOptions().isFree();
}
/**
* Checks whether the player can see this gate (Hidden property check)
*
* <p>This decides if the player can see the gate on the network selection screen</p>
*
* @param player <p>The player to check</p>
* @param portal <p>The portal to check</p>
* @return <p>True if the given player can see the given portal</p>
*/
public static boolean canSeePortal(Player player, Portal portal) {
//The portal is not hidden
if (!portal.getOptions().isHidden()) {
return true;
}
//The player can see all hidden portals
if (hasPermission(player, "stargate.admin.hidden")) {
return true;
}
//The player is the owner of the portal
return portal.isOwner(player);
}
/**
* Checks if the given player is allowed to use the given private portal
*
* @param player <p>The player trying to use the portal</p>
* @param portal <p>The private portal used</p>
* @return <p>True if the player is allowed to use the portal</p>
*/
public static boolean canUsePrivatePortal(Player player, Portal portal) {
//Check if the player is the owner of the gate
if (portal.isOwner(player)) {
return true;
}
//The player is an admin with the ability to use private gates
return hasPermission(player, "stargate.admin.private");
}
/**
* Checks if the given player has access to the given portal option
*
* @param player <p>The player trying to use the option</p>
* @param option <p>The option the player is trying to use</p>
* @return <p>True if the player is allowed to create a portal with the given option</p>
*/
public static boolean canUseOption(Player player, PortalOption option) {
return hasPermission(player, option.getPermissionString());
}
/**
* Checks if the given player is allowed to create gates on the given network
*
* @param player <p>The player trying to create a new gate</p>
* @param network <p>The network the player is trying to create a gate on</p>
* @return <p>True if the player is allowed to create the new gate</p>
*/
public static boolean canCreateNetworkGate(Player player, String network) {
//Check if the player is allowed to create a portal on any network
if (hasPermission(player, "stargate.create.network")) {
//Check if the network has been explicitly denied
return hasPermissionImplicit(player, "stargate.create.network." + network);
}
//Check if the player is allowed to create on this specific network
return hasPermission(player, "stargate.create.network." + network);
}
/**
* Checks whether the given player is allowed to create a personal gate
*
* @param player <p>The player trying to create the new gate</p>
* @return <p>True if the player is allowed</p>
*/
public static boolean canCreatePersonalPortal(Player player) {
return hasPermission(player, "stargate.create.personal");
}
/**
* Checks if the given player can create a portal with the given gate layout
*
* @param player <p>The player trying to create a portal</p>
* @param gate <p>The gate type of the new portal</p>
* @return <p>True if the player is allowed to create a portal with the given gate layout</p>
*/
public static boolean canCreatePortal(Player player, String gate) {
//Check if the player is allowed to create all gates
if (hasPermission(player, "stargate.create.gate")) {
//Check if the gate type has been explicitly denied
return hasPermissionImplicit(player, "stargate.create.gate." + gate);
}
//Check if the player can create the specific gate type
return hasPermission(player, "stargate.create.gate." + gate);
}
/**
* Checks if the given player can destroy the given portal
*
* @param player <p>The player trying to destroy the portal</p>
* @param portal <p>The portal to destroy</p>
* @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();
//Use a special check for bungee portals
if (portal.getOptions().isBungee()) {
return hasPermission(player, "stargate.admin.bungee");
}
//Check if the player is allowed to destroy on all networks
if (hasPermission(player, "stargate.destroy.network")) {
//Check if the network has been explicitly denied
return hasPermissionImplicit(player, "stargate.destroy.network." + network);
}
//Check if the player is allowed to destroy on the network
if (hasPermission(player, "stargate.destroy.network." + network)) {
return true;
}
//Check if personal portal and if the player is allowed to destroy it
return portal.isOwner(player) && hasPermission(player, "stargate.destroy.personal");
}
/**
* Decide of the player can teleport through a portal
*
* @param entrancePortal <p>The portal the player is entering from</p>
* @param destination <p>The destination of the portal the player is inside</p>
* @param player <p>The player wanting to teleport</p>
* @param event <p>The move event causing the teleportation</p>
* @return <p>True if the player cannot teleport. False otherwise</p>
*/
public static boolean playerCannotTeleport(Portal entrancePortal, Portal destination, Player player, PlayerMoveEvent event) {
//No portal or not open
if (entrancePortal == null || !entrancePortal.isOpen()) {
return true;
}
//Not open for this player
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
return true;
}
//No destination
if (!entrancePortal.getOptions().isBungee() && destination == null) {
return true;
}
//Player cannot access portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) {
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);
if (cost > 0) {
return EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost);
}
return false;
}
}

View File

@ -0,0 +1,260 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.portal.Gate;
import net.knarcraft.stargate.portal.GateHandler;
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 org.bukkit.World;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import static net.knarcraft.stargate.portal.PortalSignDrawer.markPortalWithInvalidGate;
/**
* Helper class for saving and loading portal save files
*/
public final class PortalFileHelper {
private PortalFileHelper() {
}
/**
* Saves all portals for the given world
*
* @param world <p>The world to save portals for</p>
*/
public static void saveAllPortals(World world) {
Stargate.getStargateConfig().addManagedWorld(world.getName());
String saveFileLocation = Stargate.getPortalFolder() + "/" + world.getName() + ".db";
try {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(saveFileLocation, false));
for (Portal portal : PortalRegistry.getAllPortals()) {
//Skip portals in other worlds
String worldName = portal.getWorld().getName();
if (!worldName.equalsIgnoreCase(world.getName())) {
continue;
}
//Save the portal
savePortal(bufferedWriter, portal);
}
bufferedWriter.close();
} catch (Exception e) {
Stargate.logSevere(String.format("Exception while writing stargates to %s: %s", saveFileLocation, e));
}
}
/**
* Saves one portal
*
* @param bufferedWriter <p>The buffered writer to write to</p>
* @param portal <p>The portal to save</p>
* @throws IOException <p>If unable to write to the buffered writer</p>
*/
private static void savePortal(BufferedWriter bufferedWriter, Portal portal) throws IOException {
StringBuilder builder = new StringBuilder();
BlockLocation button = portal.getStructure().getButton();
//WARNING: Because of the primitive save format, any change in order will break everything!
builder.append(portal.getName()).append(':');
builder.append(portal.getSignLocation().toString()).append(':');
builder.append((button != null) ? button.toString() : "").append(':');
//Add removes config values to keep indices consistent
builder.append(0).append(':');
builder.append(0).append(':');
builder.append(portal.getYaw()).append(':');
builder.append(portal.getTopLeft().toString()).append(':');
builder.append(portal.getGate().getFilename()).append(':');
//Only save the destination name if the gate is fixed as it doesn't matter otherwise
builder.append(portal.getOptions().isFixed() ? portal.getDestinationName() : "").append(':');
builder.append(portal.getNetwork()).append(':');
//Name is saved as a fallback if the UUID is unavailable
builder.append(portal.getOwner().getIdentifier());
//Save all the portal options
savePortalOptions(portal, builder);
bufferedWriter.append(builder.toString());
bufferedWriter.newLine();
}
/**
* Saves all portal options for the given portal
*
* @param portal <p>The portal to save</p>
* @param builder <p>The string builder to append to</p>
*/
private static void savePortalOptions(Portal portal, StringBuilder builder) {
PortalOptions options = portal.getOptions();
builder.append(':');
builder.append(options.isHidden()).append(':');
builder.append(options.isAlwaysOn()).append(':');
builder.append(options.isPrivate()).append(':');
builder.append(portal.getWorld().getName()).append(':');
builder.append(options.isFree()).append(':');
builder.append(options.isBackwards()).append(':');
builder.append(options.isShown()).append(':');
builder.append(options.isNoNetwork()).append(':');
builder.append(options.isRandom()).append(':');
builder.append(options.isBungee());
}
/**
* Loads all portals for the given world
*
* @param world <p>The world to load portals for</p>
* @return <p>True if portals could be loaded</p>
*/
public static boolean loadAllPortals(World world) {
String location = Stargate.getPortalFolder();
File database = new File(location, world.getName() + ".db");
if (database.exists()) {
return loadPortals(world, database);
} else {
Stargate.logInfo(String.format("{%s} No stargates for world ", world.getName()));
}
return false;
}
/**
* Loads all the given portals
*
* @param world <p>The world to load portals for</p>
* @param database <p>The database file containing the portals</p>
* @return <p>True if the portals were loaded successfully</p>
*/
private static boolean loadPortals(World world, File database) {
int lineIndex = 0;
try {
Scanner scanner = new Scanner(database);
while (scanner.hasNextLine()) {
//Read the line and do whatever needs to be done
readPortalLine(scanner, ++lineIndex, world);
}
scanner.close();
//Do necessary tasks after all portals have loaded
doPostLoadTasks(world);
return true;
} catch (Exception e) {
Stargate.logSevere(String.format("Exception while reading stargates from %s: %d", database.getName(),
lineIndex));
e.printStackTrace();
}
return false;
}
/**
* Reads one file line containing information about one portal
*
* @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>
*/
private static void readPortalLine(Scanner scanner, int lineIndex, World world) {
String line = scanner.nextLine().trim();
//Ignore empty and comment lines
if (line.startsWith("#") || line.isEmpty()) {
return;
}
//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;
}
//Load the portal defined in the current line
loadPortal(portalData, world, lineIndex);
}
/**
* Performs tasks which must be run after portals have loaded
*
* <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>
*/
private static void doPostLoadTasks(World world) {
//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();
int openCount = PortalHandler.openAlwaysOpenPortals();
//Print info about loaded stargates so that admins can see if all stargates loaded
Stargate.logInfo(String.format("{%s} Loaded %d stargates with %d set as always-on", world.getName(),
portalCount, openCount));
//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();
}
}
/**
* Loads one portal from a data array
*
* @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>
*/
private static void 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;
//Load the portal's location
PortalLocation portalLocation = new PortalLocation();
portalLocation.setSignLocation(new BlockLocation(world, portalData[1]));
portalLocation.setYaw(Float.parseFloat(portalData[5]));
portalLocation.setTopLeft(new BlockLocation(world, portalData[6]));
//Check if the portal's gate type exists and is loaded
Gate gate = GateHandler.getGateByName(portalData[7]);
if (gate == null) {
//Mark the sign as invalid to reduce some player confusion
markPortalWithInvalidGate(portalLocation, portalData[7], lineIndex);
return;
}
//Load extra portal data
String destination = (portalData.length > 8) ? portalData[8] : "";
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
PortalOwner owner = new PortalOwner(ownerString);
//Create the new portal
Portal portal = new Portal(portalLocation, button, destination, name, network, gate, owner,
PortalHandler.getPortalOptions(portalData));
//Register the portal, and close it in case it wasn't properly closed when the server stopped
PortalHandler.registerPortal(portal);
portal.getPortalOpener().closePortal(true);
}
}

View File

@ -0,0 +1,27 @@
lang=language
portal-folder=folders.portalFolder
gate-folder=folders.gateFolder
default-gate-network=gates.defaultGateNetwork
destroyexplosion=gates.integrity.destroyedByExplosion
maxgates=gates.maxGatesEachNetwork
destMemory=gates.cosmetic.rememberDestination
ignoreEntrance=
gates.integrity.ignoreEntrance=
handleVehicles=gates.functionality.handleVehicles
sortLists=gates.cosmetic.sortNetworkDestinations
protectEntrance=gates.integrity.protectEntrance
enableBungee=gates.functionality.enableBungee
verifyPortals=gates.integrity.verifyPortals
signColor=gates.cosmetic.signColor
gates.cosmetic.signColor=gates.cosmetic.mainSignColor
debug=debugging.debug
permdebug=debugging.permissionDebug
useiconomy=economy.useEconomy
useeconomy=economy.useEconomy
createcost=economy.createCost
destroycost=economy.destroyCost
usecost=economy.useCost
toowner=economy.toOwner
chargefreedestination=economy.chargeFreeDestination
freegatesgreen=economy.freeGatesGreen
CheckUpdates=

View File

@ -0,0 +1,62 @@
# stargate Configuration File
# Main stargate config
# I----------I----------I #
# portalFolder - The folder for storing portals
# gateFolder - The folder for storing gate layouts
# defaultGateNetwork - The default gate network
# destroyedByExplosion - Whether to destroy gates with explosions (Creeper, TNT, etc.)
# maxGatesEachNetwork - The maximum number of gates allowed on a network - 0 for unlimited
# language - The language file to load for messages
# rememberDestination - Whether to remember the cursor location between uses
# handleVehicles - Whether to allow vehicles through gates
# sortNetworkDestinations - Whether to sort network lists alphabetically
# protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material)
# mainSignColor - The color used for drawing signs (Default: BLACK).
# highlightSignColor - The color used for sign markings (Default: WHITE)
# verifyPortals - Whether all the non-sign blocks are checked to match the gate layout when a stargate is loaded.
# I------------I-------------I #
# stargate economy options #
# I------------I-------------I #
# useEconomy - Whether to use an economy plugin
# createCost - The cost to create a gate
# destroyCost - The cost to destroy a gate
# useCost - The cost to use a gate
# toOwner - Whether the charge for using a gate goes to the gate's owner
# chargeFreeDestination - Whether a gate whose destination is a free gate is still charged
# freeGatesGreen - Whether a free gate in the destination list is drawn green
# I-------I-------I #
# Debug options #
# I-------I-------I #
# debug - Debug -- Only enable if you have issues, massive console output
# permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
language: en
folders:
portalFolder: plugins/Stargate/portals/
gateFolder: plugins/Stargate/gates/
gates:
maxGatesEachNetwork: 0
defaultGateNetwork: central
cosmetic:
rememberDestination: false
sortNetworkDestinations: false
mainSignColor: BLACK
highlightSignColor: WHITE
integrity:
destroyedByExplosion: false
verifyPortals: false
protectEntrance: false
functionality:
enableBungee: false
handleVehicles: true
economy:
useEconomy: false
createCost: 0
destroyCost: 0
useCost: 0
toOwner: false
chargeFreeDestination: true
freeGatesGreen: false
debugging:
debug: false
permissionDebug: false

View File

@ -0,0 +1,12 @@
portal-open=END_GATEWAY
portal-closed=AIR
button=BIRCH_BUTTON
toowner=false
X=END_STONE_BRICKS
-=END_STONE_BRICKS
XX
X..X
-..-
X*.X
XX

View File

@ -0,0 +1,24 @@
#This is the default gate type. You can copy this and make as many .gate files as you need.
#The portal-open block can be most blocks which do not fill the entire block or otherwise prevent the player from
#entering the portal, but NETHER_PORTAL, AIR, WATER, LAVA, KELP_PLANT, OAK_FENCE, IRON_BARS, CHAIN, BAMBOO, SUGAR_CANE,
#COBWEB and VINE gives an impression of which blocks will work.
portal-open=NETHER_PORTAL
#The portal-closed block can be any of the blocks used for portal-open, but also any solid, full-size block such as DIRT.
portal-closed=AIR
#The button can be the following: A chest (CHEST), any type of button (STONE_BUTTON, OAK_BUTTON), any type of shulker
#box (LIME_SHULKER_BOX), or any wall coral (DEAD_TUBE_CORAL_WALL_FAN, TUBE_CORAL_WALL_FAN, DEAD_BRAIN_CORAL_WALL_FAN,
#BRAIN_CORAL_WALL_FAN, etc.)
button=STONE_BUTTON
#Whether payment for entry should go to this gate's owner
toowner=false
#The material to use for the normal frame
X=OBSIDIAN
#The material to use for the sign and button blocks of the frame
-=OBSIDIAN
#The description of the required portal blocks. X = Frame block. - = Sign/button position. . = Empty blocks. * = Exit
XX
X..X
-..-
X*.X
XX

View File

@ -0,0 +1,12 @@
portal-open=KELP_PLANT
portal-closed=WATER
button=BRAIN_CORAL_WALL_FAN
toowner=false
X=SEA_LANTERN
-=SEA_LANTERN
XX
X..X
-..-
X*.X
XX

View File

@ -1,28 +1,28 @@
author=EduardBaer
prefix=[Stargate]
teleportMsg=Du wurdest Teleportiert.
destroyMsg=Gate zerstört
invalidMsg=Ungültiges Ziel
blockMsg=Ziel blockiert
destEmpty=Zielliste leer
denyMsg=Zugriff verweigert
ecoDeduct=%cost% abgezogen
ecoRefund=%cost% zurückerstattet
ecoObtain=%cost% von Stargate %portal% erhalten
ecoInFunds=Das kannst du dir nicht leisten.
createMsg=Gate erstellt.
createNetDeny=Du hast keinen Zugriff auf dieses Netzwerk.
createGateDeny=Du hast keinen Zugriff auf dieses Gate-Layout.
createPersonal=Gate im persönlichen Netzwerk erstellt.
createNameLength=Name zu kurz oder zu lang.
createExists=Ein Gate mit diesem Name existiert bereits.
createFull=Dieses Netzwerk ist voll.
createWorldDeny=Du hast keinen Zugriff auf diese Welt.
createConflict=Dieses Gate steht im Konflikt mit einem bereits existierenden.
signRightClick=Right click
signToUse=to use gate
signRandom=Random
author=EduardBaer
prefix=[Stargate]
teleportMsg=Du wurdest Teleportiert.
destroyMsg=Gate zerstört
invalidMsg=Ungültiges Ziel
blockMsg=Ziel blockiert
destEmpty=Zielliste leer
denyMsg=Zugriff verweigert
ecoDeduct=%cost% abgezogen
ecoRefund=%cost% zurückerstattet
ecoObtain=%cost% von Stargate %portal% erhalten
ecoInFunds=Das kannst du dir nicht leisten.
createMsg=Gate erstellt.
createNetDeny=Du hast keinen Zugriff auf dieses Netzwerk.
createGateDeny=Du hast keinen Zugriff auf dieses Gate-Layout.
createPersonal=Gate im persönlichen Netzwerk erstellt.
createNameLength=Name zu kurz oder zu lang.
createExists=Ein Gate mit diesem Name existiert bereits.
createFull=Dieses Netzwerk ist voll.
createWorldDeny=Du hast keinen Zugriff auf diese Welt.
createConflict=Dieses Gate steht im Konflikt mit einem bereits existierenden.
signRightClick=Right click
signToUse=to use gate
signRandom=Random
signDisconnected=Disconnected

View File

@ -1,32 +1,37 @@
prefix=[Stargate]
teleportMsg=Teleported
destroyMsg=Gate Destroyed
invalidMsg=Invalid Destination
blockMsg=Destination Blocked
destEmpty=Destination List Empty
denyMsg=Access Denied
ecoDeduct=Deducted %cost%
ecoRefund=Refunded %cost%
ecoObtain=Obtained %cost% from Stargate %portal%
ecoInFunds=Insufficient Funds
createMsg=Gate Created
createNetDeny=You do not have access to that network
createGateDeny=You do not have access to that gate layout
createPersonal=Creating gate on personal network
createNameLength=Name too short or too long.
createExists=A gate by that name already exists
createFull=This network is full
createWorldDeny=You do not have access to that world
createConflict=Gate conflicts with existing gate
signRightClick=Right click
signToUse=to use gate
signRandom=Random
signDisconnected=Disconnected
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
prefix=[Stargate]
teleportMsg=Teleported
destroyMsg=Gate Destroyed
invalidMsg=Invalid Destination
blockMsg=Destination Blocked
destEmpty=Destination List Empty
denyMsg=Access Denied
reloaded=Stargate Reloaded
ecoDeduct=Deducted %cost%
ecoRefund=Refunded %cost%
ecoObtain=Obtained %cost% from Stargate %portal%
ecoInFunds=Insufficient Funds
ecoLoadError=Vault was loaded, but no economy plugin could be hooked into
vaultLoadError=Economy is enabled but Vault could not be loaded. Economy disabled
vaultLoaded=Vault v%version% found
createMsg=Gate Created
createNetDeny=You do not have access to that network
createGateDeny=You do not have access to that gate layout
createPersonal=Creating gate on personal network
createNameLength=Name too short or too long.
createExists=A gate by that name already exists
createFull=This network is full
createWorldDeny=You do not have access to that world
createConflict=Gate conflicts with existing gate
signRightClick=Right click
signToUse=to use gate
signRandom=Random
signDisconnected=Disconnected
signInvalidGate=Invalid gate
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

View File

@ -1,10 +1,10 @@
author=Manuestaire
prefix=[Stargate]
teleportMsg=Teletransportado
destroyMsg=Portal Destruído
destroyMsg=Portal Destruído
invalidMsg=Elige Destino
blockMsg=Destino Bloqueado
destEmpty=La lista de destinos está vacía
destEmpty=La lista de destinos está vacía
denyMsg=Acceso denegado
ecoDeduct=Pagaste %cost%
@ -14,11 +14,11 @@ ecoInFunds=No tienes suficiente dinero
createMsg=Portal creado
createNetDeny=No tienes acceso a esta red
createGateDeny=No tienes acceso a este diseño de portal
createGateDeny=No tienes acceso a este diseño de portal
createPersonal=Creando el portal en una red personal
createNameLength=Nombre demasiado largo o demasiado corto
createExists=Ya existe una puerta con este nombre
createFull=Esta red está llena
createFull=Esta red está llena
createWorldDeny=No tienes permisos para acceder a ese mundo
createConflict=El portal entra en conflicto con un portal ya existente

View File

@ -1,4 +1,4 @@
author=Dauphin14
author=Dauphin14
prefix=[Stargate]
teleportMsg=Téléportation Réussie.
destroyMsg=Portail detruit.

View File

@ -0,0 +1,37 @@
author=EpicKnarvik97
prefix=[Stjerneport]
teleportMsg=Teleporterte
destroyMsg=Port Ødelagt
invalidMsg=Ugyldig Destinasjon
blockMsg=Destinasjon Blokkert
destEmpty=Destinasjonslisten Er Tom
denyMsg=Tilgang Avslått
reloaded=Stjerneport Ble Lastet Inn På Nytt
ecoDeduct=Fratrekk %cost%
ecoRefund=Refundert %cost%
ecoObtain=Fikk %cost% fra Stjerneport %portal%
ecoInFunds=Manglende Midler
ecoLoadError=Vault ble lastet inn men ingen brukbar økonomi-utvidelse ble funnet
vaultLoadError=Økonomi er skrudd på, men Vault kunne ikke lastes inn. Økonomi er skrudd av
vaultLoaded=Vault v%version% funnet
createMsg=Port opprettet
createNetDeny=Du har ikke tilgang til det nettverket
createGateDeny=Du har ikke tilgang til den portutformingen
createPersonal=Oppretter port på personlig nettverk
createNameLength=Navnet er for kort eller for langt.
createExists=En port ved det navnet eksisterer allerede
createFull=Dette nettverket er fullt
createWorldDeny=Du har ikke tilgang til den verdenen
createConflict=Port er i konflikt med en eksisterende port
signRightClick=Høyreklikk
signToUse=for å bruke port
signRandom=Tilfeldig
signDisconnected=Koblet fra
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

View File

@ -16,7 +16,7 @@ createMsg=Gate gemaakt
createNetDeny=Je hebt geen toegang tot dat netwerk.
createGateDeny=Je mag die Gate-Layout niet gebruiken
createPersonal=Gate op persoonlijk netwerk gemaakt.
createNameLength=Naam te lang of te kort.
createNameLength=Naam te chosenLanguage of te kort.
createExists=Er bestaat al een gate met die naam
createFull=Dit netwerk is vol.
createWorldDeny=Je mag niet in die wereld komen.

View File

@ -0,0 +1,37 @@
author=EpicKnarvik97
prefix=[Stjerneport]
teleportMsg=Teleporterte
destroyMsg=Port Øydelagd
invalidMsg=Ugyldig Destinasjon
blockMsg=Destinasjon Blokkert
destEmpty=Destinasjonslista Er Tom
denyMsg=Tilgang Avslått
reloaded=Stjerneport Vart Lasta Inn På Nytt
ecoDeduct=Fråtrekk %cost%
ecoRefund=Refundert %cost%
ecoObtain=Mottok %cost% frå Stjerneport %portal%
ecoInFunds=Manglande Midlar
ecoLoadError=Vault vart lasta inn men inga brukbar økonomi-utviding vart funnen
vaultLoadError=Økonomi er skrudd på, men Vault kunne ikkje lastas inn. Økonomi er skrudd av
vaultLoaded=Vault v%version% funnen
createMsg=Port oppretta
createNetDeny=Du har ikkje tilgang til det nettverket
createGateDeny=Du har ikkje tilgang til den portutforminga
createPersonal=Opprettar port på personleg nettverk
createNameLength=Namnet er for kort eller for langt.
createExists=Ein port med det namnet eksisterar allereie
createFull=Dette nettverket er fullt
createWorldDeny=Du har ikkje tilgang til den verda
createConflict=Port er i konflikt med ein eksisterande port
signRightClick=Høgreklikk
signToUse=for å bruke port
signRandom=Tilfeldig
signDisconnected=Kopla frå
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

View File

@ -17,7 +17,7 @@ createNetDeny=Voce nao tem acesso a essa rede.
createGateDeny=Voce nao tem acesso a esse portal layout.
createPersonal=Criando portal em rede pessoal.
createNameLength=Nome muito curto ou muito grande.
createExists=Já existe um portal com esse nome.
createExists=Já existe um portal com esse nome.
createFull=Esta rede esta cheia.
createWorldDeny=Voce nao tem acesso a esse mundo.
createConflict=Portal em conflito com um portal ja existente.

View File

@ -1,4 +1,4 @@
author=ckr@jk
author=ckr@jk
prefix=[Портал]
teleportMsg=Вы перемещены
destroyMsg=Портал уничтожен

View File

@ -0,0 +1,143 @@
name: Stargate
main: net.knarcraft.stargate.Stargate
version: 0.9.0.2
description: Stargate mod for Bukkit Revived
author: EpicKnarvik97
authors: [ Drakia, PseudoKnight, EpicKnarvik97 ]
website: https://git.knarcraft.net/EpicKnarvik97/Stargate
api-version: 1.17
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
permissions:
stargate.*:
description: Wildcard permission which gives all Stargate permissions
default: false
children:
stargate.admin: true
stargate.use: true
stargate.create: true
stargate.destroy: true
stargate.free: true
stargate.option: true
stargate.server: true
stargate.reload:
description: Allows reloading the plugin
default: false
stargate.use:
description: Allow use of all stargates linking to any world in any network
default: true
children:
stargate.world: true
stargate.network: true
stargate.server: true
stargate.world:
description: Allow use of stargates in any world
default: false
stargate.network:
description: Allows use of stargates in any network
default: false
stargate.create:
description: Allow creating stargates on any network using any gate type
default: op
children:
stargate.create.personal: true
stargate.create.network: true
stargate.create.gate: true
stargate.create.personal:
description: Allows the creation of a personal stargate if a player is missing permission for the given network
default: false
stargate.create.network:
description: Allows the creation of a stargate on any network
default: false
stargate.create.gate:
description: Allows the creation of a stargate using any gate type
default: false
stargate.destroy:
description: Allows the destruction of all stargates
default: op
children:
stargate.destroy.network: true
stargate.destroy.personal: true
stargate.destroy.network:
description: Allows the destruction of stargates on any network
default: false
stargate.destroy.personal:
description: Allows the destruction of any personal stargates the player has created
default: false
stargate.free:
description: Allow free use/creation/destruction of stargates
default: op
children:
stargate.free.use: true
stargate.free.create: true
stargate.free.destroy: true
stargate.free.use:
description: Allows free usage of all stargates
default: false
stargate.free.create:
description: Allows creating stargates for free
default: false
stargate.free.destroy:
description: Allows destroying stargates for free
default: false
stargate.option:
description: Allows use of all options
default: op
children:
stargate.option.hidden: true
stargate.option.alwayson: true
stargate.option.private: true
stargate.option.free: true
stargate.option.backwards: true
stargate.option.show: true
stargate.option.nonetwork: true
stargate.option.random: true
stargate.option.hidden:
description: Allows the creation of a hidden stargate
default: false
stargate.option.alwayson:
description: Allows the creation of an always open stargate
default: false
stargate.option.private:
description: Allows the creation of a private stargate
default: false
stargate.option.free:
description: Allows the creation of a stargate which is free regardless of any set prices
default: false
stargate.option.backwards:
description: Allows the creation of a stargate where players will exit through the back
default: false
stargate.option.show:
description: Allows the creation of a stargate which is shown on the network, even if always on
default: false
stargate.option.nonetwork:
description: Allows the creation of a stargate with a hidden network name
default: false
stargate.option.random:
description: Allows the creation of a stargate with a random destination
default: false
stargate.admin.hidden:
description: Allows this player to see all hidden stargates
default: false
stargate.admin.private:
description: Allows this player to use all private stargates
default: false
stargate.admin.bungee:
description: Allows the creation and destruction of a stargate between BungeeCord servers
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)
default: op
children:
stargate.admin.reload: true
stargate.admin.hidden: true
stargate.admin.private: true
stargate.admin.bungee: true

View File

@ -1,173 +0,0 @@
package net.TheDgtl.Stargate;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011 Shaun (sturmeh)
* Copyright (C) 2011 Dinnerbone
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class Blox {
private final int x;
private final int y;
private final int z;
private final World world;
private Blox parent = null;
public Blox (World world, int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
this.world = world;
}
public Blox (Block block) {
this.x = block.getX();
this.y = block.getY();
this.z = block.getZ();
this.world = block.getWorld();
}
public Blox (Location location) {
this.x = location.getBlockX();
this.y = location.getBlockY();
this.z = location.getBlockZ();
this.world = location.getWorld();
}
public Blox (World world, String string) {
String[] split = string.split(",");
this.x = Integer.parseInt(split[0]);
this.y = Integer.parseInt(split[1]);
this.z = Integer.parseInt(split[2]);
this.world = world;
}
public Blox makeRelative(int x, int y, int z) {
return new Blox(this.world, this.x + x, this.y + y, this.z + z);
}
public Location makeRelativeLoc(double x, double y, double z, float rotX, float rotY) {
return new Location(this.world, (double)this.x + x, (double)this.y + y, (double)this.z + z, rotX, rotY);
}
public Blox modRelative(int right, int depth, int distance, int modX, int modY, int modZ) {
return makeRelative(-right * modX + distance * modZ, -depth * modY, -right * modZ + -distance * modX);
}
public Location modRelativeLoc(double right, double depth, double distance, float rotX, float rotY, int modX, int modY, int modZ) {
return makeRelativeLoc(0.5 + -right * modX + distance * modZ, depth, 0.5 + -right * modZ + -distance * modX, rotX, 0);
}
public void setType(Material type) {
world.getBlockAt(x, y, z).setType(type);
}
public Material getType() {
return world.getBlockAt(x, y, z).getType();
}
public Block getBlock() {
return world.getBlockAt(x, y, z);
}
public Location getLocation() {
return new Location(world, x, y, z);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
public World getWorld() {
return world;
}
public Block getParent() {
if (parent == null) findParent();
if (parent == null) return null;
return parent.getBlock();
}
private void findParent() {
int offsetX = 0;
int offsetY = 0;
int offsetZ = 0;
BlockData blk = getBlock().getBlockData();
if (blk instanceof WallSign) {
BlockFace facing = ((WallSign) blk).getFacing().getOppositeFace();
offsetX = facing.getModX();
offsetZ = facing.getModZ();
} else if (blk instanceof Sign) {
offsetY = -1;
} else {
return;
}
parent = new Blox(world, getX() + offsetX, getY() + offsetY, getZ() + offsetZ);
}
public String toString() {
StringBuilder builder = new StringBuilder();
//builder.append(world.getName());
//builder.append(',');
builder.append(x);
builder.append(',');
builder.append(y);
builder.append(',');
builder.append(z);
return builder.toString();
}
@Override
public int hashCode() {
int result = 18;
result = result * 27 + x;
result = result * 27 + y;
result = result * 27 + z;
result = result * 27 + world.getName().hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Blox blox = (Blox) obj;
return (x == blox.x) && (y == blox.y) && (z == blox.z) && (world.getName().equals(blox.world.getName()));
}
}

View File

@ -1,47 +0,0 @@
package net.TheDgtl.Stargate;
import org.bukkit.Axis;
import org.bukkit.Material;
public class BloxPopulator {
private Blox blox;
private Material nextMat;
private Axis nextAxis;
public BloxPopulator(Blox b, Material m) {
blox = b;
nextMat = m;
nextAxis = null;
}
public BloxPopulator(Blox b, Material m, Axis a) {
blox = b;
nextMat = m;
nextAxis = a;
}
public void setBlox(Blox b) {
blox = b;
}
public void setMat(Material m) {
nextMat = m;
}
public void setAxis(Axis a) {
nextAxis = a;
}
public Blox getBlox() {
return blox;
}
public Material getMat() {
return nextMat;
}
public Axis getAxis() {
return nextAxis;
}
}

View File

@ -1,106 +0,0 @@
package net.TheDgtl.Stargate;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.UUID;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class EconomyHandler {
public static boolean economyEnabled = false;
public static Economy economy = null;
public static Plugin vault = null;
public static int useCost = 0;
public static int createCost = 0;
public static int destroyCost = 0;
public static boolean toOwner = false;
public static boolean chargeFreeDestination = true;
public static boolean freeGatesGreen = false;
public static double getBalance(Player player) {
if (!economyEnabled) return 0;
return economy.getBalance(player);
}
public static boolean chargePlayer(Player player, String target, double amount) {
if (!economyEnabled) return true;
if(player.getName().equals(target)) return true;
if(economy != null) {
if(!economy.has(player, amount)) return false;
economy.withdrawPlayer(player, amount);
economy.depositPlayer(target, amount);
}
return true;
}
public static boolean chargePlayer(Player player, UUID target, double amount) {
if (!economyEnabled) return true;
if(player.getUniqueId().compareTo(target) == 0) return true;
if(economy != null) {
if(!economy.has(player, amount)) return false;
economy.withdrawPlayer(player, amount);
economy.depositPlayer(Bukkit.getOfflinePlayer(target), amount);
}
return true;
}
public static boolean chargePlayer(Player player, double amount) {
if (!economyEnabled) return true;
if(economy != null) {
if(!economy.has(player, amount)) return false;
economy.withdrawPlayer(player, amount);
}
return true;
}
public static String format(int amt) {
if (economyEnabled) {
return economy.format(amt);
}
return "";
}
public static boolean setupEconomy(PluginManager pm) {
if (!economyEnabled) return false;
// Check for Vault
Plugin p = pm.getPlugin("Vault");
if (p != null && p.isEnabled()) {
RegisteredServiceProvider<Economy> economyProvider = Stargate.server.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
vault = p;
return true;
}
}
economyEnabled = false;
return false;
}
public static boolean useEconomy() {
return economyEnabled && economy != null;
}
}

View File

@ -1,513 +0,0 @@
package net.TheDgtl.Stargate;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011 Shaun (sturmeh)
* Copyright (C) 2011 Dinnerbone
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class Gate {
private static final Character ANYTHING = ' ';
private static final Character ENTRANCE = '.';
private static final Character EXIT = '*';
private static final HashMap<String, Gate> gates = new HashMap<>();
private static final HashMap<Material, ArrayList<Gate>> controlBlocks = new HashMap<>();
private static final HashSet<Material> frameBlocks = new HashSet<>();
private final String filename;
private final Character[][] layout;
private final HashMap<Character, Material> types;
private RelativeBlockVector[] entrances = new RelativeBlockVector[0];
private RelativeBlockVector[] border = new RelativeBlockVector[0];
private RelativeBlockVector[] controls = new RelativeBlockVector[0];
private RelativeBlockVector exitBlock = null;
private final HashMap<RelativeBlockVector, Integer> exits = new HashMap<>();
private Material portalBlockOpen = Material.NETHER_PORTAL;
private Material portalBlockClosed = Material.AIR;
private Material button = Material.STONE_BUTTON;
// Economy information
private int useCost = -1;
private int createCost = -1;
private int destroyCost = -1;
private boolean toOwner = false;
public Gate(String filename, Character[][] layout, HashMap<Character, Material> types) {
this.filename = filename;
this.layout = layout;
this.types = types;
populateCoordinates();
}
private void populateCoordinates() {
ArrayList<RelativeBlockVector> entranceList = new ArrayList<>();
ArrayList<RelativeBlockVector> borderList = new ArrayList<>();
ArrayList<RelativeBlockVector> controlList = new ArrayList<>();
RelativeBlockVector[] relativeExits = new RelativeBlockVector[layout[0].length];
int[] exitDepths = new int[layout[0].length];
RelativeBlockVector lastExit = null;
for (int y = 0; y < layout.length; y++) {
for (int x = 0; x < layout[y].length; x++) {
Character key = layout[y][x];
if (key.equals('-')) {
controlList.add(new RelativeBlockVector(x, y, 0));
}
if (key.equals(ENTRANCE) || key.equals(EXIT)) {
entranceList.add(new RelativeBlockVector(x, y, 0));
exitDepths[x] = y;
if (key.equals(EXIT)) {
this.exitBlock = new RelativeBlockVector(x, y, 0);
}
} else if (!key.equals(ANYTHING)) {
borderList.add(new RelativeBlockVector(x, y, 0));
}
}
}
for (int x = 0; x < exitDepths.length; x++) {
relativeExits[x] = new RelativeBlockVector(x, exitDepths[x], 0);
}
for (int x = relativeExits.length - 1; x >= 0; x--) {
if (relativeExits[x] != null) {
lastExit = relativeExits[x];
} else {
relativeExits[x] = lastExit;
}
if (exitDepths[x] > 0) this.exits.put(relativeExits[x], x);
}
this.entrances = entranceList.toArray(this.entrances);
this.border = borderList.toArray(this.border);
this.controls = controlList.toArray(this.controls);
}
public void save(String gateFolder) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(gateFolder + filename));
writeConfig(bw, "portal-open", portalBlockOpen.name());
writeConfig(bw, "portal-closed", portalBlockClosed.name());
writeConfig(bw, "button", button.name());
if (useCost != -1)
writeConfig(bw, "usecost", useCost);
if (createCost != -1)
writeConfig(bw, "createcost", createCost);
if (destroyCost != -1)
writeConfig(bw, "destroycost", destroyCost);
writeConfig(bw, "toowner", toOwner);
for (Map.Entry<Character, Material> entry : types.entrySet()) {
Character type = entry.getKey();
Material value = entry.getValue();
// Skip control values
if (type.equals(ANYTHING) || type.equals(ENTRANCE) || type.equals(EXIT)) {
continue;
}
bw.append(type);
bw.append('=');
if(value != null) {
bw.append(value.toString());
}
bw.newLine();
}
bw.newLine();
for(Character[] aLayout : layout) {
for(Character symbol : aLayout) {
bw.append(symbol);
}
bw.newLine();
}
bw.close();
} catch (IOException ex) {
Stargate.log.log(Level.SEVERE, "Could not save Gate " + filename + " - " + ex.getMessage());
}
}
private void writeConfig(BufferedWriter bw, String key, int value) throws IOException {
bw.append(String.format("%s=%d", key, value));
bw.newLine();
}
private void writeConfig(BufferedWriter bw, String key, boolean value) throws IOException {
bw.append(String.format("%s=%b", key, value));
bw.newLine();
}
private void writeConfig(BufferedWriter bw, String key, String value) throws IOException {
bw.append(String.format("%s=%s", key, value));
bw.newLine();
}
public Character[][] getLayout() {
return layout;
}
public HashMap<Character, Material> getTypes() {
return types;
}
public RelativeBlockVector[] getEntrances() {
return entrances;
}
public RelativeBlockVector[] getBorder() {
return border;
}
public RelativeBlockVector[] getControls() {
return controls;
}
public HashMap<RelativeBlockVector, Integer> getExits() {
return exits;
}
public RelativeBlockVector getExit() {
return exitBlock;
}
public Material getControlBlock() {
return types.get('-');
}
public String getFilename() {
return filename;
}
public Material getPortalBlockOpen() {
return portalBlockOpen;
}
public void setPortalBlockOpen(Material type) {
portalBlockOpen = type;
}
public Material getPortalBlockClosed() {
return portalBlockClosed;
}
public void setPortalBlockClosed(Material type) {
portalBlockClosed = type;
}
public Material getButton() {
return button;
}
public int getUseCost() {
if (useCost < 0) return EconomyHandler.useCost;
return useCost;
}
public Integer getCreateCost() {
if (createCost < 0) return EconomyHandler.createCost;
return createCost;
}
public Integer getDestroyCost() {
if (destroyCost < 0) return EconomyHandler.destroyCost;
return destroyCost;
}
public Boolean getToOwner() {
return toOwner;
}
public boolean matches(Blox topleft, int modX, int modZ) {
return matches(topleft, modX, modZ, false);
}
public boolean matches(Blox topleft, int modX, int modZ, boolean onCreate) {
HashMap<Character, Material> portalTypes = new HashMap<>(types);
for (int y = 0; y < layout.length; y++) {
for (int x = 0; x < layout[y].length; x++) {
Character key = layout[y][x];
if (key.equals(ENTRANCE) || key.equals(EXIT)) {
if (Stargate.ignoreEntrance) continue;
Material type = topleft.modRelative(x, y, 0, modX, 1, modZ).getType();
// Ignore entrance if it's air and we're creating a new gate
if (onCreate && type == Material.AIR) continue;
if (type != portalBlockClosed && type != portalBlockOpen) {
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
return false;
}
} else if (!key.equals(ANYTHING)) {
Material id = portalTypes.get(key);
if(id == null) {
portalTypes.put(key, topleft.modRelative(x, y, 0, modX, 1, modZ).getType());
} else if(topleft.modRelative(x, y, 0, modX, 1, modZ).getType() != id) {
Stargate.debug("Gate::Matches", "Block Type Mismatch: " + topleft.modRelative(x, y, 0, modX, 1, modZ).getType() + " != " + id);
return false;
}
}
}
}
return true;
}
public static void registerGate(Gate gate) {
gates.put(gate.getFilename(), gate);
Material blockID = gate.getControlBlock();
if (!controlBlocks.containsKey(blockID)) {
controlBlocks.put(blockID, new ArrayList<>());
}
controlBlocks.get(blockID).add(gate);
}
public static Gate loadGate(File file) {
Scanner scanner = null;
boolean designing = false;
ArrayList<ArrayList<Character>> design = new ArrayList<>();
HashMap<Character, Material> types = new HashMap<>();
HashMap<String, String> config = new HashMap<>();
HashSet<Material> frameTypes = new HashSet<>();
int cols = 0;
// Init types map
types.put(ENTRANCE, Material.AIR);
types.put(EXIT, Material.AIR);
types.put(ANYTHING, Material.AIR);
try {
scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (designing) {
ArrayList<Character> row = new ArrayList<>();
if (line.length() > cols) {
cols = line.length();
}
for (Character symbol : line.toCharArray()) {
if ((symbol.equals('?')) || (!types.containsKey(symbol))) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + file.getName() + " - Unknown symbol '" + symbol + "' in diagram");
return null;
}
row.add(symbol);
}
design.add(row);
} else {
if ((line.isEmpty()) || (!line.contains("="))) {
designing = true;
} else {
String[] split = line.split("=");
String key = split[0].trim();
String value = split[1].trim();
if (key.length() == 1) {
Character symbol = key.charAt(0);
Material id = Material.getMaterial(value);
if(id == null) {
throw new Exception("Invalid material in line: " + line);
}
types.put(symbol, id);
frameTypes.add(id);
} else {
config.put(key, value);
}
}
}
}
} catch (Exception ex) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + file.getName() + " - " + ex.getMessage());
return null;
} finally {
if (scanner != null) scanner.close();
}
Character[][] layout = new Character[design.size()][cols];
for (int y = 0; y < design.size(); y++) {
ArrayList<Character> row = design.get(y);
Character[] result = new Character[cols];
for (int x = 0; x < cols; x++) {
if (x < row.size()) {
result[x] = row.get(x);
} else {
result[x] = ' ';
}
}
layout[y] = result;
}
Gate gate = new Gate(file.getName(), layout, types);
gate.portalBlockOpen = readConfig(config, gate, file, "portal-open", gate.portalBlockOpen);
gate.portalBlockClosed = readConfig(config, gate, file, "portal-closed", gate.portalBlockClosed);
gate.button = readConfig(config, gate, file, "button", gate.button);
gate.useCost = readConfig(config, gate, file, "usecost", -1);
gate.destroyCost = readConfig(config, gate, file, "destroycost", -1);
gate.createCost = readConfig(config, gate, file, "createcost", -1);
gate.toOwner = (config.containsKey("toowner") ? Boolean.valueOf(config.get("toowner")) : EconomyHandler.toOwner);
if (gate.getControls().length != 2) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + file.getName() + " - Gates must have exactly 2 control points.");
return null;
}
if (!Tag.BUTTONS.isTagged(gate.button)) {
Stargate.log.log(Level.SEVERE, "Could not load Gate " + file.getName() + " - Gate button must be a type of button.");
return null;
}
// Merge frame types, add open mat to list
frameBlocks.addAll(frameTypes);
gate.save(file.getParent() + "/"); // Updates format for version changes
return gate;
}
private static int readConfig(HashMap<String, String> config, Gate gate, File file, String key, int def) {
if (config.containsKey(key)) {
try {
return Integer.parseInt(config.get(key));
} catch (NumberFormatException ex) {
Stargate.log.log(Level.WARNING, String.format("%s reading %s: %s is not numeric", ex.getClass().getName(), file, key));
}
}
return def;
}
private static Material readConfig(HashMap<String, String> config, Gate gate, File file, String key, Material def) {
if (config.containsKey(key)) {
Material mat = Material.getMaterial(config.get(key));
if(mat != null) {
return mat;
}
Stargate.log.log(Level.WARNING, String.format("Error reading %s: %s is not a material", file, key));
}
return def;
}
public static void loadGates(String gateFolder) {
File dir = new File(gateFolder);
File[] files;
if (dir.exists()) {
files = dir.listFiles(new StargateFilenameFilter());
} else {
files = new File[0];
}
if (files == null || files.length == 0) {
if (dir.mkdir()) {
populateDefaults(gateFolder);
}
} else {
for (File file : files) {
Gate gate = loadGate(file);
if (gate != null) registerGate(gate);
}
}
}
public static void populateDefaults(String gateFolder) {
Character[][] layout = new Character[][] {
{' ', 'X','X', ' '},
{'X', '.', '.', 'X'},
{'-', '.', '.', '-'},
{'X', '*', '.', 'X'},
{' ', 'X', 'X', ' '},
};
HashMap<Character, Material> types = new HashMap<>();
types.put(ENTRANCE, Material.AIR);
types.put(EXIT, Material.AIR);
types.put(ANYTHING, Material.AIR);
types.put('X', Material.OBSIDIAN);
types.put('-', Material.OBSIDIAN);
Gate gate = new Gate("nethergate.gate", layout, types);
gate.save(gateFolder);
registerGate(gate);
}
public static Gate[] getGatesByControlBlock(Block block) {
return getGatesByControlBlock(block.getType());
}
public static Gate[] getGatesByControlBlock(Material type) {
Gate[] result = new Gate[0];
ArrayList<Gate> lookup = controlBlocks.get(type);
if (lookup != null) result = lookup.toArray(result);
return result;
}
public static Gate getGateByName(String name) {
return gates.get(name);
}
public static int getGateCount() {
return gates.size();
}
public static boolean isGateBlock(Material type) {
return frameBlocks.contains(type);
}
static class StargateFilenameFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return name.endsWith(".gate");
}
}
public static void clearGates() {
gates.clear();
controlBlocks.clear();
frameBlocks.clear();
}
}

View File

@ -1,226 +0,0 @@
package net.TheDgtl.Stargate;
import org.bukkit.ChatColor;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class LangLoader {
private final String UTF8_BOM = "\uFEFF";
// Variables
private final String datFolder;
private String lang;
private HashMap<String, String> strList;
private final HashMap<String, String> defList;
public LangLoader(String datFolder, String lang) {
this.lang = lang;
this.datFolder = datFolder;
File tmp = new File(datFolder, lang + ".txt");
if (!tmp.exists()) {
tmp.getParentFile().mkdirs();
}
updateLanguage(lang);
strList = load(lang);
// We have a default hashMap used for when new text is added.
InputStream is = Stargate.class.getResourceAsStream("resources/" + lang + ".txt");
if (is != null) {
defList = load("en", is);
} else {
defList = null;
Stargate.log.severe("[Stargate] Error loading backup language. There may be missing text ingame");
}
}
public boolean reload() {
// This extracts/updates the language as needed
updateLanguage(lang);
strList = load(lang);
return true;
}
public String getString(String name) {
String val = strList.get(name);
if (val == null && defList != null) val = defList.get(name);
if (val == null) return "";
return val;
}
public void setLang(String lang) {
this.lang = lang;
}
// This function updates on-disk language files
// with missing lines from the in-JAR files
private void updateLanguage(String lang) {
// Load the current language file
ArrayList<String> keyList = new ArrayList<>();
ArrayList<String> valList = new ArrayList<>();
HashMap<String, String> curLang = load(lang);
InputStream is = Stargate.class.getResourceAsStream("resources/" + lang + ".txt");
if (is == null) return;
boolean updated = false;
FileOutputStream fos = null;
try {
// Input stuff
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
boolean firstLine = true;
while (line != null) {
// Strip UTF BOM
if (firstLine) line = removeUTF8BOM(line);
firstLine = false;
// Split at first "="
int eq = line.indexOf('=');
if (eq == -1) {
keyList.add("");
valList.add("");
line = br.readLine();
continue;
}
String key = line.substring(0, eq);
String val = line.substring(eq);
if (curLang == null || curLang.get(key) == null) {
keyList.add(key);
valList.add(val);
updated = true;
} else {
keyList.add(key);
valList.add("=" + curLang.get(key).replace('\u00A7', '&'));
curLang.remove(key);
}
line = br.readLine();
}
br.close();
// Save file
fos = new FileOutputStream(datFolder + lang + ".txt");
OutputStreamWriter out = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
BufferedWriter bw = new BufferedWriter(out);
// Output normal Language data
for (int i = 0; i < keyList.size(); i++) {
bw.write(keyList.get(i) + valList.get(i));
bw.newLine();
}
bw.newLine();
// Output any custom language strings the user had
if(curLang != null) {
for (String key : curLang.keySet()) {
bw.write(key + "=" + curLang.get(key));
bw.newLine();
}
}
bw.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (fos != null) {
try {fos.close();} catch (Exception ex) {}
}
}
if (updated)
Stargate.log.info("[Stargate] Your language file (" + lang + ".txt) has been updated");
}
private HashMap<String, String> load(String lang) {
return load(lang, null);
}
private HashMap<String, String> load(String lang, InputStream is) {
HashMap<String, String> strings = new HashMap<>();
FileInputStream fis = null;
InputStreamReader isr = null;
try {
if (is == null) {
fis = new FileInputStream(datFolder + lang + ".txt");
isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
} else {
isr = new InputStreamReader(is, StandardCharsets.UTF_8);
}
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
boolean firstLine = true;
while (line != null) {
// Strip UTF BOM
if (firstLine) line = removeUTF8BOM(line);
firstLine = false;
// Split at first "="
int eq = line.indexOf('=');
if (eq == -1) {
line = br.readLine();
continue;
}
String key = line.substring(0, eq);
String val = ChatColor.translateAlternateColorCodes('&', line.substring(eq + 1));
strings.put(key, val);
line = br.readLine();
}
} catch (Exception ex) {
return null;
} finally {
if (fis != null) {
try {
fis.close();
} catch (Exception ex) {}
}
}
return strings;
}
public void debug() {
Set<String> keys = strList.keySet();
for (String key : keys) {
Stargate.debug("LangLoader::Debug::strList", key + " => " + strList.get(key));
}
if (defList == null) return;
keys = defList.keySet();
for (String key : keys) {
Stargate.debug("LangLoader::Debug::defList", key + " => " + defList.get(key));
}
}
private String removeUTF8BOM(String s) {
if (s.startsWith(UTF8_BOM)) {
s = s.substring(1);
}
return s;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
package net.TheDgtl.Stargate;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011 Shaun (sturmeh)
* Copyright (C) 2011 Dinnerbone
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class RelativeBlockVector {
private int right = 0;
private int depth = 0;
private int distance = 0;
public RelativeBlockVector(int right, int depth, int distance) {
this.right = right;
this.depth = depth;
this.distance = distance;
}
public int getRight() {
return right;
}
public int getDepth() {
return depth;
}
public int getDistance() {
return distance;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +0,0 @@
package net.TheDgtl.Stargate.event;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import net.TheDgtl.Stargate.Portal;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class StargateAccessEvent extends StargateEvent {
private final Player player;
private boolean deny;
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public StargateAccessEvent(Player player, Portal portal, boolean deny) {
super("StargateAccessEvent", portal);
this.player = player;
this.deny = deny;
}
public boolean getDeny() {
return this.deny;
}
public void setDeny(boolean deny) {
this.deny = deny;
}
public Player getPlayer() {
return this.player;
}
}

View File

@ -1,69 +0,0 @@
package net.TheDgtl.Stargate.event;
import java.util.ArrayList;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import net.TheDgtl.Stargate.Portal;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class StargateActivateEvent extends StargateEvent {
private final Player player;
private ArrayList<String> destinations;
private String destination;
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public StargateActivateEvent(Portal portal, Player player, ArrayList<String> destinations, String destination) {
super("StargatActivateEvent", portal);
this.player = player;
this.destinations = destinations;
this.destination = destination;
}
public Player getPlayer() {
return player;
}
public ArrayList<String> getDestinations() {
return destinations;
}
public void setDestinations(ArrayList<String> destinations) {
this.destinations = destinations;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
}

View File

@ -1,50 +0,0 @@
package net.TheDgtl.Stargate.event;
import org.bukkit.event.HandlerList;
import net.TheDgtl.Stargate.Portal;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class StargateCloseEvent extends StargateEvent {
private boolean force;
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public StargateCloseEvent(Portal portal, boolean force) {
super("StargateCloseEvent", portal);
this.force = force;
}
public boolean getForce() {
return force;
}
public void setForce(boolean force) {
this.force = force;
}
}

View File

@ -1,83 +0,0 @@
package net.TheDgtl.Stargate.event;
import net.TheDgtl.Stargate.Portal;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
/**
* Stargate - A portal plugin for Bukkit
* Copyright (C) 2011, 2012 Steven "Drakia" Scott <Contact@TheDgtl.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class StargateCreateEvent extends StargateEvent {
private final Player player;
private boolean deny;
private String denyReason;
private final String[] lines;
private int cost;
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public StargateCreateEvent(Player player, Portal portal, String[] lines, boolean deny, String denyReason, int cost) {
super("StargateCreateEvent", portal);
this.player = player;
this.lines = lines;
this.deny = deny;
this.denyReason = denyReason;
this.cost = cost;
}
public Player getPlayer() {
return player;
}
public String getLine(int index) throws IndexOutOfBoundsException {
return lines[index];
}
public boolean getDeny() {
return deny;
}
public void setDeny(boolean deny) {
this.deny = deny;
}
public String getDenyReason() {
return denyReason;
}
public void setDenyReason(String denyReason) {
this.denyReason = denyReason;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
}

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