diff --git a/LICENSE.md b/LICENSE.md index a7f82fe4c..95f77abeb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,3 +1,4 @@ +[Prison Documents - Table of Contents](docs/prison_docs_000_toc.md) # GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 diff --git a/README.md b/README.md index d2782bf11..bba1119d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -[![Documentation](https://static.ultz.co.uk/PrisonMoveDoc.png)](https://confluence.onultz.com/display/PRIS) -![Prison Banner](https://faizaand.github.io/prison/img/prison-banner.png) - [![License](https://img.shields.io/badge/license-GPL%20License%20v3-blue.svg)](LICENSE.md) [![Build Status](https://travis-ci.org/MC-Prison/Prison.svg?branch=master)](https://travis-ci.org/MC-Prison/Prison) [![Discord](https://discordapp.com/api/guilds/332602419483770890/widget.png)](https://discord.gg/396ed5F) @@ -9,21 +6,31 @@ 2. [Why should I use Prison?](#why-should-i-use-prison) 3. [Get Prison](#get-prison) +## Prison Documentation + +[Prison Documents - Table of Contents](docs/prison_docs_000_toc.md) + +The Prison documentation covers the important details on how to setup a usable Prison server. +These documents are in a state of development, so more will be added. + ## What is Prison? -Prison is a Minecraft plugin that adds all the features necessary for [prison servers](https://woodycraft.net/threads/what-is-a-prison-server-a-general-explanation.21161/). It runs on multiple platforms, including the popular [Spigot](http://spigotmc.org) and the rapidly growing [Sponge](http://spongepowered.org). Plugins for prison servers exist already, but many of them are poorly maintained, and the solid ones are paid resources. Prison eliminates the paywall and the updating hassle, and puts a one-stop solution in their place. +Prison is a Minecraft plugin that adds all the features necessary for prison servers. It runs on the popular [Spigot](http://spigotmc.org). Plugins for prison servers exist already, but many of them are poorly maintained, and the solid ones are paid resources. Prison eliminates the paywall and the updating hassle, and puts a one-stop solution in their place. ### History of Prison Prison began as a plugin with the goal of combining two main prison server features: resetting mines and purchasable ranks. In June 2014, Prison v2.0 was released with the goal of providing a single solution for prison servers. Prison v2.0 has had numerous problems and is not the solution that we had envisioned for an all-in-one package to be. The solution is the current version - Prison v3.0. +Starting with prison v3.2.0 new development efforts were began with a focus on adding new features +and improving the performance and stability. + ## Why should I use Prison? If hassle-free updates and zero payments is not enough for you to consider switching to Prison, it doesn't stop there. * **Prison is modular.** If you don't want to use a certain feature on your server, you can disable it in just a few keystrokes. * **Prison is completely, 100% open source.** This means that your developers can make changes to the plugin to customize your server's experience, if you so desire. -* **Prison is and always will be free.** While we do ask for donations, all of our code, resources, and binaries are provided free of charge to the Minecraft community. +* **Prison is and always will be free.** All of our code, resources, and binaries are provided free of charge to the Minecraft community. We do not accept donations. * **New releases are heavily bug-tested.** We test every single aspect of every new release of Prison before releasing it to the community. If a release doesn't pass our rigorous update checklist, it is iterated upon until it does. What does this mean for you? Solid updates with minimal bugs for your server. * **Prison is always expanding.** We are always looking for ways to expand Prison with features that make your server run more smoothly. Additionally, we are always looking for contributions from the community, be it code contributions, feature requests, bug reports, translations, or documentation. @@ -31,7 +38,5 @@ If hassle-free updates and zero payments is not enough for you to consider switc | Stable | Experimental (Nightly) | | ------ | ---------------------- | -| [![Stable Download](https://img.shields.io/badge/download-stable-44cc11.svg)](https://github.com/MC-Prison/Prison/releases/latest) | [![Experimental Download](https://img.shields.io/badge/download-experimental-red.svg)](https://ci.appveyor.com/project/faizaand/prison/build/artifacts) | - +| [![Stable Download](https://img.shields.io/badge/download-stable-44cc11.svg)](https://www.spigotmc.org/resources/prison.1223/) | [![Experimental Download](https://img.shields.io/badge/download-experimental-red.svg)](https://ci.appveyor.com/project/faizaand/prison/build/artifacts) | - diff --git a/build.gradle b/build.gradle index 4b1f79275..1bcd27706 100644 --- a/build.gradle +++ b/build.gradle @@ -15,23 +15,30 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +import java.text.SimpleDateFormat +import java.util.Date +apply plugin: 'java' + +/* Two different ways to enable utf-8 encoding within gradle, but not sure its + * even needed at this time. Keeping for a few builds to make sure all is ok. +compileJava.options.encoding = 'UTF-8' +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} +*/ + + println """ -================================ -You are building Prison. - -Note that you are probably using -a development build and therefore -a buggy build. Don\'t use it on a -production server unless you know -what you are doing! You have been -WARNED. - -Output files will be in -[subproject]/build/libs. -The runnable JAR is usually -named Prison[ModuleName].jar. -================================ +========================================================================== +You are building the Prison plugin + Version: ${project.version} + Build time: ${getCurrentTimestamp()} + +Output files are located in [subproject]/build/libs. +The runnable JAR is usually named prison.jar. +Example: The spigot build artifact is prison-spigot/build/libs/prison.jar +========================================================================== """ buildscript { @@ -39,10 +46,11 @@ buildscript { repositories { mavenCentral() jcenter() + maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2' + classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' } } @@ -56,7 +64,6 @@ subprojects { archivesBaseName = 'Prison' group = 'tech.mcprison.prison' - version = '3.2.0' sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -72,9 +79,9 @@ subprojects { } dependencies { - compile 'org.apache.commons:commons-lang3:3.9' - compile 'com.google.guava:guava:19.0' - compile 'com.google.code.gson:gson:2.8.6' + implementation 'org.apache.commons:commons-lang3:3.10' + implementation 'com.google.guava:guava:19.0' + implementation 'com.google.code.gson:gson:2.8.6' testCompile 'junit:junit:4.12' } @@ -83,3 +90,9 @@ subprojects { wrapper { distributionType = Wrapper.DistributionType.ALL } + +def getCurrentTimestamp() +{ + SimpleDateFormat df = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss") + return df.format (new Date()) +} diff --git a/changelog_v3.2.x.md b/changelog_v3.2.x.md index 655ad2e42..ec3982327 100644 --- a/changelog_v3.2.x.md +++ b/changelog_v3.2.x.md @@ -1,3 +1,4 @@ +[Prison Documents - Table of Contents](docs/prison_docs_000_toc.md) ## Prison Build Logs for v3.2.x @@ -7,6 +8,983 @@ is going on in each build so you have a better idea if it may be something that you need. +## tag v3.2.1-alpha.20 - 2020-09-27 + + + +* **Fixed issue with sorting of the mine name in the gui.** + They are now strictly by alphabetical on the mine name. + + + +* **New Feature: Custom sorting of mines, ladders, and ranks.** +Currently mines and ranks are alphabetical in sort order. +Ladders sorts default first, prestige last, and everything else is alphabetical between them. +The PrisonSortableLaddersRanks will first sort ranks by ladders, then within each ladder, they will preserve the ladder rank order, then all ranks that have no associated ladders will be sorted alphabetically and added to the last of the list. +This new feature has not been hooked up yet to any interfaces, but it's ready. +Need to add mines, but right now there is no way to link mines to ranks. + + + +* **Bug Fix: Prevent mines from being created that have spaces or no names** +A bug was found where the admin was able to create a mine with the name of two spaces. Changed create mine function to disallow any spaces within the name, or an empty name. Also setup the parms to "require" a name, but not sure if that will work correctly. If it does, then there will be multiple layers of protection to prevent spaces. + + + +* **New Feature: GUI Works with Spigot 1.8 now!** +Prison's GUI now works with spigot 1.8. + + +* **New Feature: The command /prison now includes all other root commands!** +When the command prison is used, it shows a list of all sub-commands. Now it also includes all other Prison registered root commands so the uses do not have to guess what other commands are available within prison. This will provide a listing so they can explore others. +Also disabled the prison troubleshoot command since its not of any benefit. + + +* **Moved the spiget project in to the prison project's class path** +Move the spiget in to the project's directory with shadowJar since there has been a few issues with the build. + + +* **New Feature: /mines tp Can Now teleport players within a rank command** +Allow only admins or console commands or rank commands to teleport someone by name. +This allows rank commands to use this to teleport a player when they rank up. + + + +* **Alpha 20 - 2020-09-16** +Yeah. I know. Alpha 20 and not beta! +But many releases have happened under Alpha 19 including some bug fixes. This is incrementing to help ensure when someone grabs the most recent, they know if they have the most recent. +Beta will follow shortly, but there are still a few more additions and adjustments. + + +* **Tweaks to auto manager** +Found duplication on durability processing. Eliminated the duplication, but now it will only be applied when auto pickup is enabled. Not 100% sure if that is the best logic, but figuring if prison does not handle the block break event, then do not apply the durability. +May have to revisit and tweak how smelt and blocking is working too. +Changed the ALL pickup to be the first processed since that is probably going to be the most common situation so bypass the many block checks to save some CPU time. + + +* **Attempts to force the plugin's config.yml file to be reloaded** +It "should" work, but it does not. +Keeping this code since I will expand upon it and get it working, even if I have to go outside the bukkit handling of that file. + + +* **Bug fix: Duplication of mine blocks. Only impacts more recent Alpha.19 releases.** +Duplicate blocks are now "fixed" when loading a mine. If a duplication fix is applied, the fixed mine data will be saved and a message will be shown that an inconsistency was detected and fixed. +Overall this did not cause any crashes or corruptions of mines, but it did duplicate internal data for the blocks associated with a given mine. +Symptoms were loss of air blocks (if you had an air percentage) or if enough blocks were duplicated, the GUI would give you an error message indicating there were too many blocks and it could not display them all. +The issue was initially thought to be related to a lambda function, but that was rewritten and confirmed that it wasn't there. The issue was ultimately tracked back to a typo on the new block handling. So this would have only impacted the more recent alpha.19 releases (not all). + + +* **New Feature!! A Reset Threshold Percentage has been added.** +Before the reset threshold was set to zero blocks. Now, if enabled, it will reset the mine at another predetermined percentage of blocks remaining. +A value of zero disables this feature since that is the same as a zero block reset. + + +* **Changed mines placeholder for time remaining formatted** +Dropped the decimals since it does not look good when updating every second or less. + + +* **New Features! PLAYERMINES placeholders!! Added 24 new placeholders including aliases!!** +These placeholders are a hybrid between player and mine placeholders. These placeholders can only return results when a player is actually within a mine, and these are the mine's stats. In other words, whichever a mine a player is in, it can show all of that mine's stats. +This will not work in holographic displays since they are not associated with any players. It will work with chat prefixes, but these are stats you generally don't want as chat prefixes (maybe just the mine's name?). +But where these really can be awesome is used in the player's scoreboard. So when they hop in a mine, they can pull up in their scoreboard stats on the mine itself. + + +* **Bug fix: Placeholder progress bar configs in config.yml not working** +This addresses some issues with being able to load the placeholder progress bar configs from the config.yml file. +Ran in to issues with symbols not being quoted. Strings normally do not have to be quoted, but yaml was interpreting them incorrectly and was trying to treat them as special values. So quoting prevents that. +Also had unicode characters in the comments which also may have be causing issues indirectly, so removed them. + + +* **New Feature! Placeholder progress bars!** +prison_rankup_cost_bar, prison_rankup_cost_bar_laddername, prison_mines_timeleft_bar_minename, prison_mines_remaining_bar_minename, plus their aliases. +This adds a configurable progress bar to the list of placeholders. The /plugins/Prison/config.yml file can be reset to load the parameters to customize the progress bars. For now, if the configs do not exist, they will fall back on to the default values. + + +* **Minor fix: Found that some placeholder key values were not being generate with all lower case characters.** +This could have resulted in problems if the users were trying to use all lower case. +Also provided a placeholder flags exclusion filter to remove the wrong compound types within mines. + + +* **Minor improvement to placeholder search when using Players** +Added player information when it's supplied with the placeholder search. This provides important feedback to the user that the supplied player is valid. + + +* **Bug fix: For the PlayerManager was not allowing PlaceHolderFlags types of LADDERS to be processed.** +So ladder based placeholders were not being translated correctly (sometimes they were). This was fixed. + + +* **Eliminate the obsolete message and .meta checks.** +This version of prison cannot perform any updates on the old obsolete versions of data. That functionality was removed prior to the v3.1.1 release. To upgrade from v2.x they will first have to upgrade to v3.0.0 and then to the current release. + + + +* **Move the player mine cache to the PrisonMines object** +This will allow it to be accessible from other areas within the prison plugin. This will need to be used with more advanced placeholders that will be added shortly. + + +* **Enhancement: Optimize the generation of placeholders** +OPtimizing placeholders by eliminating the generated placeholder that lacks the prison_ prefix. Instead, adding the prison_ prefix to all identifiers that are lacking it so it can get a hit on the real entry. Fix the display of the stats... was calling wrong function. + + +* **New Features! Added 4 new placeholders - Cost Remaining** +These pertain to cost remaining for rankup. +prison_rankup_cost_remaining, prison_rcr, prison_rankup_cost_remaining_laddername, prison_rcr_laddername +Now you can count down the amount of money needed for players to rank up! Remaining value is never negative, but it shows zero instead. + + +* **Few Feature! Reload Placeholders** +Provided a reset function for placeholders. The new command is registered twice to provide the most flexibility. +**/prison placeholders reload** and **/prison reload placeholders** + + +* **Realigning source for Spigot Placeholder code.** +Moved a ton of placeholder code to the tech.mcprison.prison.spigot package to get it out of the SpigotPlatform class. +There was way too much business knowledge being exposed within SpigotPlatform and wanted to group it all under a placeholder's package. +The real meat of placeholders has to come together within spigot because core has no knowledge or access to the mines module, or the rank module. +This required creating a new interface and pulling the majority of the functions out of the Platform interface. + + +* **Change in SpigotListener to use the singleton that already exists** +Changed to use the singleton instead of the passed object. Cleaner. + + +* **Bug Fix: Fixed a mine's block type loading issue.** +The blocks are now checked against a greater range of possible values to improve match rates when reloading mines. +If a block cannot be mapped, then an error message is now printed to the console so it can be addressed. +There was an issue with QUARTZ_PILLAR not being able to be mapped back to the correct block type. + + +* **New Feature: Add the internal placeholder counts on startup.** +These counts are the total generated placeholders and some placeholders have more than one key. + + +* **New Feature: Added reset notification permissions** +Now only players with the mine's permission can be notified if the feature is enabled. +This setting is on a per mine basis, and the permission used is displayed in the /mines info listing. + + +* **Admins and console are able to view player's past names with /ranks player** +When admins and console use `/ranks player` command it will show past names for all players that have more than one name recorded. + + +* **This implements the tracking of the player's name** +This now tracks and detects when player names are changed so it can record the change. +This is passive in that it does not try to actively detect name changes unless the player is active within the prison and using features that require the use of the player ranks and associated details. +Offline players will never have their names updated if they are changed and they never return to prison. It is possible that a player could change their names a few times and if they do not log in to prison, then it will not be able to track all the interim name changes, only the current name they just logged in with. + + +* **Setup the internals for tracking the blocks mined for each player** +Established the core internals on tracking blocks mined per player. Nothing uses this yet. + + +* **Add support for recording player names within the RankPlayer.** +This will help admins manage their data especially if they have to work with the raw data. +Multiple names can be tracked, with the last name in the list being the current name. Players can change their name numerous times and always go back to the same name multiple times and this will record them. +This only establishes support for names, but this commit does not get the actual names. This update is able to save and read the data from the saved file. + + +* **Enhanced the /ranks info details** +Changed around the /ranks info to show the actual rank name and tag so it's obvious. Also moved the player count outside of the admin block so all players can see it. + + +* **Added GUI Compatibility interfaces and support classes** +Restructure the compatibility classes to separate the blocks from non-blocks and added in GUI support. +It maybe a little odd how the interfaces extend from other interfaces, but the chaining allows separation of functionalities to keep the code cleaner at the interface and implementation layers. + + +* **Alpha.19 - 2020-09-08** +Final alpha version prior to beta! Yeah I know I said that before, but I did not expect so much awesome stuff to be added! Need to have a clean transition to beta, so this has to happen first. + + +* **New Features: Show all ranks and player counts upon startup and on demand** +For each ladder, all ranks with the number of players at each rank is now displayed at startup of the plugin. +Also a new command was created to be able to display this information at any time. The command has the option to change the parameters to fine tune the results. The command is /ranks players. +When the rank of a player is viewed, it is now displayed to the log. An admin reported that all ranks were changed on their server, but no transactions appeared in the logs. It was shown in the logs when players checked their ranks, but it is unknown what thoes ranks were. SO this tries to record that. + + +* **New Feature: Language files are written to the prison's plugin directory by defaul** +If the language files do not exist at startup, then they will be written to the proper lang directories. This helps admins find the files and edit them without having to try to pull them from the jar file. +The core module, which is not a true Prison module is now correctly being remapped to the /plugins/Prison/module_conf/core/ directory for all language file uses. This keeps things consistent and clean. +The normal use case is that the jar file language files will be loaded first, then the external files will be loaded and replace any key value pairs that may have been loaded previously. +Warning... the language files ignore the package names and goes strictly by the key value in the files. Therefore if the same key exists in more than one language file, then the last one loaded will replace any other previously loaded values. +This has been tested and it works great. No new mapping have been added. + + +* **Added a unique permission for /rankupmax so it can be disabled.** +Its `ranks.rankupmax.[ladderName]` but the player must also have `ranks.user`. + + +* **Added more details to Ladder save files** +Added Rank names to the ladder's save file. This will help figure out what is what and could help eliminate possible errors. If a ladder is loaded and a name is added, then it will flag the ladder file to be resaved after being fully loaded to ensure the contents of the file is up to date. +No one should ever make manual changes to these files, but this at least give more information due to the use of the magic numbers with the rankIds. +Added a link to the actual Rank object to simplify the use of the objects. + + +* **New Feature: Broadcast Rankups and Demotions** +There is a now a new feature that will broadcast to the whole server when someone gets a Rankup Or a Demotion. +This can be disabled within the config.yml file. If the entry is not there, it is the same as if it has a true value. Any other value is considered false. + + +* **Alpha.18 - 2020-09-01** +Final alpha version prior to beta! + + +* **New Feature: Mine Reset Paging** +Finished hooking up the paged mine reset code. And it works too! Tweaked some of the timing settings such as milliseconds and block thresholds which will better tune this to be more responsive to paging. +Had to add comparable to the MineTargetBlockKey for the map to work correctly. +To enable use `/mines set resetpaging help`. + + +* **Bug fix: Correctly using magic values with XMaterials** +This fixes the usage of the magic value with bukkit/spigot versions prior to 1.13. The issue was that the only way to set the data value was through the BlockState and I did not force the update. Now the block type and rawData are being set only within the BlockState and then the update is forced. This resolves the issue and should work for all block types that rely on using the magic values. +The way the mapping to the XMaterial was changed to try use the name matching first since they have a high correlation. Also the the legacy matching is the last resort now. + + +* **Added v1.16.1 now officially supported and added to the test environments** +Working fine. No issues found. Now have a working v1.16.1 environment to test from to ensure issues are addressed. + + +* **Reporting for supported block types** +Added a raw spigot/bukkit test to round out the test to see how many blocks are available to prison. +Running this test on any version should give a fairly accurate idea of how many more blocks are gained with the newer versions of spigot. +Pre 1.13 versions of spigot cannot be fully tested to identify all possible block types due to lack of ensuring individual Material types with different data values within the ItemStack cannot be identified as being invalid since that is left for the client software to decide. +This test adds no new functionality, but provides a reporting on how the newer infrastructure will work. + + +* **More work on new block type support** +Got a lot of the questionable code working better so 1.8.x and 1.16.x will not have problems with class or method not found exceptions. Pushed a lot of version specifics to the compatibility classes. +This does not add new blocks to prison, but lays the foundation and support for new block types. + + +* **Major New Feature: Work In Progress! New handling of Blocks and Materials!!** +Starting major changes to how the block types and materials are processed. +This is a Work In Progress! Use with caution until stabilized. +Upgrade the spigot API to 1.13.2!!! +This should make Prison more 1.13+ compatible and support more block types, and transition to supporting all block types that exist for a given version. +Add in the cryptomorin's XSeries to better deal with newer block types. Trying to get rid of deprecated function usage, except for within spigot 1.8 specific code sets. +Finally got lapis lazuli working! So this is heading in the right direction. + + +* **Alpha.17 - 2020-07-28** + + +* **New Feature: Sellall has been added** + + +* **Worked on enhancements for the auto features.** +Enhancements and hooking up fortune, silk (does nothing... yet), durability, and additonal drops. +Enhancements to improve stability and quality +Eliminate the switch statements when dealing with Materials since there are a few that cannot be hard coded and instead must use the Material.matchMaterial() function since the type may not exist in all versions. It's also preparing to transition to another way of dealing with materials, but cannot use them until after prison is migrated to 1.13.x. + + +* **Fixing some block types so they are not hard coded.** +Initial setup. This will help transition to 1.13.x which will better position to supporting 1.15.x and 1.16.x block types. +Not all issues have been addressed yet, like the GUI. Want to confirm this is working before expanding. + + +* **Prevent the default ladders from being deleted** +The default ladder was actually deleted by a user. Could prevent a ton of issues by preventing these from being removed in error. + + +* **Enhanced the handling of unloaded modules.** +They were not behaving correctly and causing issues. Also better log the warnings that the module was not loaded. + + +* **Add documentation for Mine Commands** +Added documents for mine commands. Document how to setup a Parent Mine with Child Mines. + + +* **New Enhancement: Mine Commands Enabled and Enhanced!** +Mine commands have been enabled. They have been enhanced by adding the ability to have some commands ran before the reset, or after the reset. This gives a great control over customization and integrating external commands to operate most efficiently with actual block resets. One example is to have a real forest of trees reset and completely rebuilt each time. The other example of usage is to setup up a parent mine to control the resetting of all child mines. + + +* **Alpha.16 - 2020-07-04** + + +* **More Enhancements to AutoManager** +Some refactoring and some enhancements to AutoManager. +Added isCalculateDurabilityEnabled, isCalculateFortuneEnabled, isCalculateSilkEnabled, and lore durability resistance. +Feature enhancement: Permissions added to config file so they can be customized as needed. Added these custom permissions to the /prison automanager command so they always reflect the correct permissions. If changed online, everything reflects the changes right away. The AutoFeatures now are able to record their parents so it makes possible the ability to get all permissions related to a given section. For example all permissions, or all autoPickup permissions. + + +* **Enhance Auto Manager Features** +Still a work in progress, but becoming more mature. +**Added new feature**: Durability able to be turned on/off. +**Added new feature**: Durability resistance is now able to be used as a lore. With no integer value it will have 100% effect and disable durability calculations on the item that has the lore. If less than 100, then it's a percent chance durability will be skipped. +**To do:** Hook up a new block drop calculation and use the new fortune and silk functions. +Removed some hard coded Material types to make it more compatible with newer releases of spigot. +Reworked how lapis lazuli auto block is processed to work with v1.13.x and higher. + + +* **Upgrade the Spigot API to v1.12.2 from v1.9.4** +Prison was built on spigot api v1.9.4 for the longest time. Looking in to trying better support newer block types, had to upgrade the version to something newer. Tried v1.13.2 but it failed to compile. V1.12.2 works well. + + +* **Fixes to AutoManager and new file IO** +Got the new file IO working better since there were a few issues with getting the actual values from the spigot yaml reader. Also made changes to the ValueNode objects to make them easier to use and to be consistent between them all. + + +* **Bug Fix. NEATHER_QUARTZ is not a block** +But its type was set to a MaterialType of BLOCK. It is actually the result of breaking NEATHER_QUARTZ_ORE, of which, that is the proper block type to use. Use of neather_quartz would result in many exceptions in the console, one for each attempt. + + +* **New feature: Moved AutoManager to prison core and out of spigot module.** +This will allow access to these settings in all other modules instead of just the spigot module. +Rewrote the interface on how the the properties are saved and loaded to elimiante the dependancies upon spigot. + + +* **New Feature: Ladder specific placeholders for player ranks** +Add in placeholders for ladders. These are rank placeholders, but targeted for specific ladders. +Enhanced the `/prison placeholders search` to work with player based placeholders, and added support to specify the actual player too so it can work from the console. The placeholder search should now work with all placeholders and will be able to provide values for players if ran in game, or if the player's name is provided within the console. + + +* **Ranks names cannot contain formating** +Made it so new rank names cannot contain & formatting. It causes issues and that's what tags are for. +This change mitigates any preexisting ranks that may have been created with formatting, the /ranks list now escapes the & characters by replacing them with -. Then the /ranks info command is now able to use those escaped names. The results of this change is that new ranks cannot contain & formatting, but preexisting ranks should continue to work and could eliminate issues. +There was an issue with /ranks list and formatted names causing issues with on click events. + + +* **New Feature: Prestige! Significant work on Prestige added!** +Gabryca provided a lot of enhancements and features to the new Prestige system that he has been working on. It was merged with bleeding from it's own branch. It was pulled in because it has reached a point where it can start being used and undergo testing. + + +* **New features: AutoManager perms and lore enabled.** +Players can now use auto pickup, auto smelt, and auto block can be used if they either have the perms (all or nothing) or if the tools have the lore (0 to 100.00 percent chance). +The tool lore supports full enablement (no number following the lore) or a percent chance than can range from 0.001 through 100.000. Allows for tools to gain better chance. +The config setting Options.General.AreEnabledFeatures enables all mines and overrides player perms and lore. Set to false to allow perms and lore to work. +May need more testing, but looks functional so far. +Added new command /prison automanager to just display info on automanager and the perms that will enable it. The command only shows the help. + + +* **PlaceholderAPI Troubleshooting and Tips** +Update PlaceholderAPI install docs to reflect a possible way to get it to work with other plugins when the other third-party placeholders are not working. +At this time, it does not appear like this applies to Prison, but it coud be a tip that could be a solution for an edge case issue that someone may experince in the future. + + +* **Performance improvement in auto manager.** +This change to the auto manager has a significant performance improvement per block break by not having to reload all of the parameters each and every time a block breaks (it no longer loads from the file on each block break event which really reduced the overhead too). +This also sets up the auto manager for the next phase of enhancements. + + +* **New feature: The /mines tp now can be used by players** +If a player has the new permission: mines.tp.[mineName] they can use this command to tp to the mine. +The mine name must be all lower case. + + +* **New Feature: altPermissions on commands!** +Now if there is a permission that is internally checked within a command, it can be reported with the altPermissions so it can displayed if the user does the help keyword. +Examples: /rankup, /mines tp, and /ranks + +AltPermissions are alternative permissions that are not checked internally, or automatically. +It is up to the programmer to put hooks in to the code to check on these altPermissions. +This field of altPermissions is strictly for displaying helpful information to the end users +and it is only helpful if it is included. + +For example the command /rankup has an optional parameter ladderName. If a ladderName is +provided, then it checks to see if the player has the permission: ranks.rankup.[ladderName]. + +Because these permissions are not ever used to check for actual permissions, it is very +important to provide parameters such as [ladderName] to signify where the server owner, or +admin, must place the real ladder name within the permission. + +This is such a critically helpful feature because otherwise the only way you would know +that you need this permission is to look at the source code, of which many cannot do, and +those who can, may not know where to look. So this provides very important information that +was not available before. + + +* **New Feature: Command help keyword now includes permissions** +New Feature: Now includes the permissions in the help commands so users do not have to guess or hunt down what permissions are needed for these commands. + + +* **Fixes ladder commands: delrank and addrank** +Fixes the problem with removal and readding ranks from a ladder. +You can use /ranks ladder delrank to disconnect a rank from the ladder. Then use /ranks ladder addrank to put it back in to the ladder in a new position. + +These changes makes sure that the rankPrior and rankNext are set to null to remove dead or changed connections. It also sorts each ladder before hooking up the those links. + + +* **New Feature: Placeholder tools.** +You can now test all placeholders, including multiple placeholders within one test. +Also you can fully search for any placeholder, either player based or mine based placeholder. If you run the commands from console, you cannot use the player placeholders. +The commands: /prison placeholders test and /prison placeholders list. + + +* **Start the ground work for supporting another integration** +Initial setup for WorldGuard. Ran in to issues with lack of v6.x even existing in maven, which is needed for minecraft 1.8 through 1.12.2. WorldGuard v7.x is for minecraft versions 1.13.0 through 1.15.2. +Also both WG versions appear to use the same package and class names too, so how to include both in the build without conflict? Yeah, not sure. Will have to figure that out too. +Anyway, starting to provide the basic integrations hooks, which are disabled right now, but will be enabled as the details are worked out, and as Prison is adjusted to work better with it. + + +* **Failed Attempt to Enable 1.13 Flattened Materials** +Failed attempt to give 1.15.x the ability to have native block types. This code has been commented out with hopes that it could be used in the future. +The reason why this won't work, is not that the codes entered are incorrect, but it appears like the Material object that is actually being used when running on a Spigot 1.15.2 server is actually using code from Spigot 1.9.2 since none of the 1.13 materials are recognized at runtime. +So the "wrong" classes could be included in the build artifact, which in turns get's used during runtime. But should it? +So for now, its all disabled so it can be revisted later. + + +* **New Feature: Mine commands.** +Added commands to mines. This may be complete, but not fully tested, so disabling so it cannot be used. Will enable when it is proven to work well. +The concept with mine commands, is that after every reset, it will run a set of commands associated with the mine, of which could permit new and complex types of mines. + + +* **New Feature: IGNORE BlockType added.** +Created a new block type that will allow ignoring what is already there. +Ignore can be used instead of air, and as such it will not replace whatever is there. +It is possible that all blocks are 100% ignore so that way nothing in the mine changes during a reset. + + +* **New feature! Two new placeholders: formatted time.** +The placeholders for reset interval and time left now have formatted versions and aliases. +The format is 13d 21h 13m 1.23s. 1h 0m 13.13s. +It does not show leading digits if they are zero, such as days will be suppressed if zero. And days, hours, and minutes will all be suppressed if they are all zero. But if hour is non + + +* **Bug fix: Failure in logging upon plugin startup** +There was a failure on getting and calculating a paper 1.10.0 version due to a period to the left of the primary version. As such, the old code could not deal with it and was throwing a failure. To add to that failure, since it was during start up of the plugin, the resources were not yet setup for to allow the logging to work so that was causing another failure. +This fix allows the output of the log message to System.err so it will be at least recorded in the log files. + + +* **New Feature: Now Supports delayed world loading! Multiverse-core now properly supported!** +New Feature: Major modifications to support delayed world loading. This supports Multiverse-core, and other plugins, that may load worlds. +You cannot use softdepends or hard depends to ensure the world is loaded, but instead must use WorldLoadEvent events. +These changes enables the mines to load even when their world has not been loaded yet. It also disables any function or changes that could corrupt the mine's data by not having the world enabled and loaded. + + +* **Bug fix: Reset Radius Notification not working** +The message notification functions were not using the mine's radius value, so all radius notifications were based upon a distance of 150 blocks. +Extend within notifications to include players standing on top of the mines too. + + +* **New Feature: Start to properly deal with delayed world loading** +Plugins such as Multiverse-core may load most of their worlds after Prison has tried to load the mines. During the creation of the mines, the worlds must exist, otherwise the mines cannot be loaded under the older way prison was doing things. + +This change allows mines to be loaded, but will delay their hook up to the +world until the worlds are formally done loading. + +This is a work in progress and is not complete yet. + + +* **New Feature: New command /rankupMax will rank up the player until they are unable to afford the next rank.** + + +* **New feature: Provided a way to disable the resetting of the mine when reaching zero blocks.** + + +* **New feature: Can now disable the reset when a mine reaches zero blocks" +Provided a way to disable the resetting of the mine when reaching zero blocks. + + +* **Updates to AutoManager: Full Inventory** +Improve the full inventory handling. Basically always try to work with inventory items (auto pickup, auto smelt, and auto block) and if they cannot fit back in to inventory then just drop them. That allows them to fill up inventory slots that may not be full. +Have the dropExtra function use either the specified holographs or the action bar to show the inventory is full message. + + +* **Few fixes to mine commands, rank loading** +Fixed an issue with Mines commands. The one was a typo (had to be a stray space) and the other was using block search before using any other mine command (missed testing that before). Tweak formatting on mines info to make it easier to read. + + Fixed an edge case situation where the next rank was not available. Not really sure what caused it to be missing, but this prevents issues from happening within prison. + + +* **Improve how bypass resets works.** +Fine tune the bypass resets so it works better. Found some issues & fixed them. + + +* **Added 3d distances to some mine related commands** +Added the distance the player is from the mine to the /mines info command. +Created a 3d distance function to use for displaying distances to the mine. +For mine related messaging, its using 2d distances, ignoring the Y axis. + + +* **New Feature; Slime Block Fun!** +Simple feature to add some bounce to the Prison fun! +Different items in hand increases boost. After jumping on a slime block, the player gains immunity from fall damage, but they can still die if they land in lava, the void, etc... +Not really sure what the life span of this will be? +To enable, set the configuration property "slim-fun" to true in plugins/Prison/config.yml. + + +* **New Features: Command /mines list now has paging! Plus useful sorting.** +Major changes to the /mines list command by adding paging and better to read formatting. +Now you can specify a page and be able to read what's in the list. In game you can click to page to other pages. +This listing is sorted by Blocks Mined (since last server restart) then by Mine Name. This help to put the more active mines on page 1 and the inactive ones at the bottom of the list +There is an option for pages of ALL so you can get a full dump. If you want it. + + +* **Minor refactoring: Remaining Time before the Mine Resets** +Moving the calculations for the remainingTime in to the core mine classes so it can be accessed by other functions. This also moves the business logic out of the MineManager and puts it in the mine, where it should be. + + +* **Typo fix: Noticed a field was named "Rest" instead of "Reset".** +The fixing of the name does not alter the behavior of the code, but the wrong name could confuse someone. + + +* **New Feature: New Mine Placeholder! Mines Reset Count.** +Added a new mines placeholder: Mines Reset Count. Identifies how many times the mine was reset since the last server restart. + + + +* **New Feature: Zero Block Mine Reset Delay** +Added a new parameter to mines for a mine reset delay when the mine reaches zero blocks. + + +* **New Feature: Setup a packaged paging system for the commands** +New feature: Created a new class to encapsulate multiple pages for commands. Based upon /mines block search and hooked it up to /mines info. +Provisions for pre-pages that are shown before the listings, such as the first page of /mines info. Will expand to other commands later. + + +* **Bug Fix: If BlockType does not exist use STONE instead** +Bug fix: Found a problem that if you put a Block Type in a mine that does not exist on the server it fails to reset the mine and there really are no errors shown in game. +Default to STONE if the material does not exist. + + +* **New Feature: New Placeholder for mines. Blocks Mined!** +Added new placeholder blocks mined. This reports on the total blocks that have been mined within a mine since the server restart. + + +* **Improved Performance: AutoManager by extending from OnBlockBreakEventListener** +Extend the AutoManager so the same BlockBreakEvent logic for cacheing the player's last mine used to optimize performance. +By extending the class, and creating the doAction() function allows for simple reuse of the event listener. +It is also critical that the AutoManager is able to set a LOW event priority. + + +* **Improved performance: for the onBlockBreak event listener** +This is the code that monitors the number of blocks remaining in a mine. This logic will also be applied to the auto features since it needs to perform the same basic initial setup and checking to ensure the blocks are within a mine. + +Significant improvements to the OnBlockBreakEventListener to try to minimize overhead and to improve individual performance for all players. +The biggest hit, performance wise, will be when mining outside of the prison mines since it would have to check to ensure it's not within a mine, and will go through the whole list. It's just simple math, but performed for each mine that exists so it will add up. +Overall, the overhead is not much, but efforts were made to reduce it as best as possible, of which, I think is the best that can be done with the current environment. +Also renamed addBlockBreakCount function to incrementBlockBreakCount since that's what it is doing. + + +* **New Feature: Hooked up auto mine reset when blocks remaining reaches zero** +When a mine reaches zero blocks left, then it will auto reset. +This is hooked in to the onBlockBreak monitor and all it does is cancels the current mine's reset and then resubmits it to reset. +This could allow for the creation of a one block grinder. Although there may need more work, such as delays to slow down the reset since it is able to reset at blistering speeds. + + +* **New Feature: Enabled the blockBreakCount feature** +Using the new blockBreakCount feature on the placeholders to eliminate the use of the old refreshAirCount() function. +This should provide a lot of performance improvements and will allow for live updates without much overhead on the server. + + +* **Internal Mine Optimizations** +Refactored a lot of stuff with mines to eliminate wasteful operations. +Setup a collection organized by name for quicker access when there are larger number of mines within a prison. +Changed remove block to remove ONLY the first block so as to help admins remove duplicates instead of having to go back and readd them. Fixed issue with missing not on if blocktype is in mine. +Cleaned up the save mine function in relationship to the initial loading of mines to simplify the code and to eliminate possible problems. + + +* **New Feature: Starting to track actual block breaks in mines** +Hooked up an even monitor for BlockBreakEvents to track them within each mine. +This is a work in progress to eliminate the counting of air blocks, of which could be a strain on the server if something like holographic displays is hooked up to each mine, and runs a few times per second. +Upon server startup, had to count air blocks, but changed it to sync since on server load it was throwing too many exceptions due to entities within those chunks. +Hooked up a temp placeholder to test with, which will be removed shortly once moved in to production. + + +* **Async issue: Mine air counts** +Async failure: Well, found out the hard way there is even less that can be done with async threads. Even just checking the air counts that does not update the world can cause problems and could throw an exception. +The issue is that if a chunk has to be loaded and if there are any entities, then it will throw an exception and could possibly corrupt something. +For now i'm catching the exception and logging it. Will eliminate the async air counting in the next few days when I hook up live block counts of mined blocks. + + +* **Clear coordinates after usage** +Made changes to ensure that the coordinates are cleared after they are used. Also same for the last used mine name when that mine is deleted. + + +* **Enhancement: Better logging if major failure with rankup** +Enhanced the logging on rankup to better deal with logging even when there is major internal failures. Emphasis was to ensure transaction logging happens and is recorded no matter what. Added more details and fixed a few that were not working correctly. +Fixed a failure that was preventing rankup from happening. Had the wrong conditional testing that was preventing a rank from being assigned. + + +* **Enhancement: Placeholders optimized!** +The placeholders without the prison prefix have been eliminated since the prefix is now prefixed when it is missing prior to matching to a valid placeholder enum, and when generating PlaceholderKeys. This cuts the number of generated placeholders in half. This is significant since with the addition of the aliases there would be about 744 placeholders generated if the prison had 30 mines setup! Now a 30 mine prison would have about 372. + + +* **New Feature: Placeholder aliases!** +Added shorter versions of all placeholder commands. The aliases still must be prefixed with prison_ and if it's a mine related placeholder, then it must end with _minename. +Aliases are also displayed on the /prison version page in parenthesis following it's original placeholder name. + + +* **New Feature: Added a new placeholder! prison_rankup_cost_percent** +This indicates how close a player is to ranking up based upon their balance and expressed in percent closeness. +If their balance is zero or less, it will show zero percent. If they have more than enough money then it will show 100%. + + +* **New Feature: Added an auto rankup for all new players to the server!** +Added a rankup to the default rank on the default ladder for when a new player joins the server. +I REALLY thought this already existed!! oof!! +Modifed the rankupPlayerInternal function to supply the default ladder and default rank if needed. Also added more logging transactions to cover the new events. + + +* **Enhancement: AutoManager's BlockBreakEvent had to be set to a lower event priority** +Had to set to a EventPriorty.LOW so other plugins can work with the blocks. The other plugins were EZBlock & SellAll. This function was canceling the event after it auto picked it up, so the other plugins were not registering the blocks as being broken. The default EventPriority was originally set to NORMAL and that was not working with the other plugins. So setting it to LOW allowed them to inspect the block that was being broke, and do what they had to do, before prison's AutoManager performed the auto pickup events with it. + + +* **New Feature: Auto features added: Auto Pickup, Auto Smelt, and Auto Block!** +Gabryca added auto features to Prison. They include auto pickup, auto smelt, and auto block. + + +* **New Feature: Added detailed reporting on rankup, promote, demote, and set rank** +Added detailed logging on rankups to track all details. It may appear to be overly detailed, but if something goes wrong, it will allow tracking down exactly what may have went wrong. + + +* **New Feature: Added support for GemsEconomy and Custom Currency** +Added a direct integration for GemsEconomy so Prison can now use a custom currency with Rankups. +Added within prison the support for custom currencies. There is a special mechanism in place for dealing with custom currencies, since it requires special interfaces to specify the currency. Prison checks to make sure there is not only a registered economy that supports custom currencies, but it also checks to make sure the currency that is specified within the ranks is supported by an economy before allowing the player to add it. It also checks to make sure all custom currencies have supported economies upon Prison startup and reports errors if they are not found. When a player attempts a rankup, everything is once again checked to make sure the economies support the custom currency. Prison provides for admins to bypass the use of defined custom currencies through **/ranks promote**, **/ranks demote**, and **/ranks set rank**. + + +* **New Feature: List currencies that are used in Ranks** +On startup, gather all currencies that are defined within all of the Ranks, confirm there is a economy that supports it, and then print the list with the ranks module. If a currency is not found, then print an error message on the Prison start screen. The currencies will then be listed with each of the supporting economies when doing the /prison version command. + + +* **New Feature: Added temporal references to to the next lower and higher ranks** +To provide a much more simplified way of getting the next higher and lower ranks +when you already have a rank, I added temporal references internally to the ranks. +This means there is a zero cost associated when trying to get the next higher or lower +rank and its as simple as just another field within ranks. +These references, because they are temporal, are realigned when the ranks and ladders +are initially loaded, and whenever ladders change. + + +* **New Feature: Added base commands to /prison version listing** +Added the base commands to the /prison version, which is tied to the modules. +If a module is successfully loaded, then it will show the base commands that +it supports. + + +* **New Feature: GemsEconomy Direct integration** +Starting to hook up GemsEconomy. It's not ready and it may take a while to +put everything in place. The major thing that GemsEconomy provides is support +for non-standard currencies. + + +* **New Feature: AutoManager** +Gabryca is adding this new feature to the spigot interface. + + +* **Upgraded API: EssentialsX v2.1.7.1.0** +Update to the most recent version of EssentialsX v2.1.7.1.0 was v2.0.1-b354. +This is an internal library used only for compile purposes. + + +* **Upgraded API: SaneEconomy v0.15.0** +Update to a more recent version of SaneEconomy v0.15.0 was v0.13.1. +Note this is the last version where the signature of getBalance and setBalance is using doubles. Newer versions use BigDecimal so I've put try catches around them to capture potential errors so it can be reported. Did not upgrade to anything newer since the backend storage has changed which may be a major change for some servers. +This is an internal library used only for compile purposes. + + +* **New Feature: List all registered plugins** +To better support server owners when they have issues with Prison, the command +**/prison version** now lists all registered plugins along with their versions. +With this feature, to provide the information we need to help trouble shoot, +they only need to copy and paste to provide all the information we need. +"Please copy and paste the full **/prison version** command listing." + + +* **New Feature: prison_rankup_rank_tag added** +This provides the tag of the next available rank. + + +* **Started to Add Documentation** +To help ensure all users have access to Prison's documentation, and to be able to version all documentation, I've decided to go with github markdown documents. This will allow the documents to live within the project and they will be accessible online through github. Simple markdown hyperlinks will tie them together so the user can browse them by just clicking links to navigate. Markdown is very limited in what can be done with it, but accessibility and versioning is more important than bells and whistles. + + +* **Pulled in the Prison GUI menus** +Pulled the prison gui branch in to the bleeding branch. This was the result of the awesome work by Gabryca. +I added the mapping of /prison gui to redirect to /prisonmanager gui so it's integrated +with the standard prison commands. +Added the new permissions to the plugin.yml file. + + +* **New Feature: Mine Placeholders** +A lot of code was rewritten to support the mine related placeholders. Player placeholders +are simple since there are just a few placeholder keys. Mines are far more complex since +you have the "basic" internal placeholder keys (names), but when exposed outside of +prison, the mine names must be super imposed on each one. So if 6 mine placeholders +exist, and the server has 40 mines, then it would have to generate 240 placeholders. +Then it has to map all of those placeholders back to the internal placeholder key, so +it can identify which action to take. +Hooked the six new mine placeholders to their proper functions. +Created a mines chat handler. +Updated the player chat handler to use the new formats. Also updated MVdWPlaceholderAPI +and PlaceholderAPI to handle the new mines placeholders. + + +* **New Feature: Add in the target reset time for the mines** +This correctly sets the future targetResetTime when the next workflow job is submitted. This auto-adjusts the target time to compensate for delays in the system. +It also detects if there was a change in the reset time for the mine, and if so, then it will regenerate the jobWorkFlow to reflect those changes. + + +* **New Features: future targetResetTime, player counts within a mine, and count air blocks** +Start to hook up some mine related features such as future targetResetTime (the project time in the future when the mine will reset). This allows the creation of a count down timer until the reset happens. +Add a function to count the number of players within a mine. +Added a set of function to count the number of air blocks in a mine asynchronously. Set it up as a submittable task. The airblock count buffers and will run only every 30 seconds at most. If its a large mine, then it will delay slightly longer before refreshes to conserve computational resources. + + +* **Bug fix: Mine data was not fully loading prior to submitting workflow** +Found a timing issue where the mine's workflows were being submitted before all of +the mine related data was loaded from the file system. Over all it did not cause +too many issues, but it was defaulting back on the default values for mine resets +and was ignoring what was set for that mine. I implemented an inititialization +"channel" in the mine stacks... basically all layers of the mine's hierarchy will +kick off their constructors from the bottom (MineData) to the top (Mine). Once all +the layers are instantiated, then it kicks off executing all of the initialization +functions from the bottom (MineData) all the way back up to the top (Mine) again. +This allows the full loading of the saved mine data at the top layer, Mine, and then +allows a lower layer to utilize that loaded data, such as submitting the workflow. +This simplifies a lot of complexities pertaining to chicken-or-the-egg timings. + + +* **Compatibility Fix: Conflict with another chat plugin** +There was an onPlayerChat with the AsyncPlayerChatEvent issue +with the plugin InteractiveChat that was resulting in intermittent issues where +placeholders and on hover events were not always firing or working correctly. +Issues were resolved when the prison plugin was removed from the server that was +having this problem. +Reviewed the prison code and everything looked good, but what got things to work +correctly was setting the Spigot onPlayerChat event priority to EventPriority.LOW. + +* **New feature: /ranks set rank **. +You can now just set a rank on a given ladder, and not have to worry about multiple +promotes or demotes. +**Caution:** Use carefully, each rank that is configured must be independent of all the other +ranks. In other words, a given rank cannot expect the lower ranks to have set some +specific permission, but each rank's commands must ensure it works correctly without +hitting all the lower ranks. Same goes for when this command is used for demotions +and skipping many ranks in the process. + +* **New Features: Offline player support.** Added off line player support on some of the +commands. This now allows working with the players even if they are offline, which was not +possible before. + +* **New Features: /ranks promote and /ranks demote.** This is a way for an +admin/owner to demote or promote players directly, without incurring a cost to +the player. +This is good for testing purposes. But has a major concern. When setting up +the rank commands, the commands to remove elevated permissions must also be +included. Otherwise if a player is demoted, their perms will not be removed. + +* **Tag with v3.2.1-alpha.5 and add Core Documents shells** Started to +put together the document structures within the project. NO content yet, +but trying to hook everything up to the table of contents. More to follow! + +* **Removed Feature: Disabled the Sponge build**. The sponge project +was getting to the point that all the key functions that prison needs, has +no code and were returning nulls. Did not test the latest sponge build, +but there is no way some vital aspects would work, such as mine resets. +So commented the sponge references from gradle config files so it can be +easily reenabled if anyone wants to try to hook it back up in the future. + +* **Added command placeholders to display**. When the user is entering the +command **/ranks command add** it will now display the command placeholders +as {player} and {player_uid} so the admins/owners won't have to keep +asking what they are. They actually need to be displayed elsewhere too: TBD. + +* **Bug Fix: Glass block was not being removed** when fill mode was +enabled. Glass block will now be replaced as expected. + +* **New Feature: Mine Reset Notification Control** On a per mine +basis, notifications can now be turned off, set to a radius area from +the center of the mine, to player who are properly within the mine. +Added a command to support this new behavior: **/mines notifications**. +Each mine is independent, and there will be no global values. +As new mines are created/added they will be set to the default +radius distance which is currently 150 blocks from the center of +each mine. + +* **Bug Fix: Added PlaceholderAPI to the softdepends** Paper server +environment identified that it was missing. So I added it. Not sure +if that could have been cause issues with Multiverse loading issues? +Doubtful, but this is the first "thing" that has been found related +to hard depends or softdepends. + +* **New Feature: Command /mines resetTime** Added the command to allow +users to change the reset time for each mine. The global reset time is +only applied to new mines that are added. The new times apply once the +mines reset at the next mine reset. + +* **Work in progress: Mine resets** Committed the WIP just to get +it in git. Currently the code works, for that of which is being used. +The more complex compoennts dealing with async and paging is not yet +enabled so will have no impact yet. Also needed to commit so I can +add new features that overlap with this work (notifications). + +* **Removed dead links in README.md** A user made a reference to a few +dead links so I removed the ones that I could find. Also removed the +request for donations since that's currently a moot point. + +* **Took a quick look at tab completing" but ran in to issues with how +prison uses multi-word commands The space appears to force everything that +follows to be treated as an attribute/parameter to the root command. +Special handling of these conditions will need to be addressed later. +Tabled this attempt with hopes to return to it soon. + +* **Added new placeholder tag: prison_rank_tag** The existing placeholder +**prison_rank** returns the name of the current rank for the player. +The new placeholder **prison_rank_tag** returns that rank's tag value, +which could include formatting characters and would be suitable for +text chat prefixes. + +* **Bug Fix ?? : Cannot manually edit rank and ladder files.** +(to be addressed) +Manually editing the rank and ladder files, and maybe even the player +files, does not work, even when the server is shut down and restarted. +Could not reproduce, but a couple of users reported this as an issue and +I think I saw it once before too. Not really sure the cause but did not +see anything obvious in the code either. + +* **Bug Fix: Players on server prior to setting up prison have no ranks** +(to be addressed) +When prison is setup initially, if a player is already on the server, they +will not be assigned a player rank. This causes failures when the player +tries to use the /rankup command in that it reports "Error ! You don't have enough +money to rank up! Then next rank costs !" This happens when the rank +costs zero. + +* **Bug Fix: Found issue with the Vault Integration for Economy** +The vault economy integration was wrong. It was mixing use of player-centric and +bank-centric processing. As such balances may not have been working consistently, +and many of the vault's supported economys probably were failing to work at all. +This fix allows it work correctly and will eliminate possible failures and +intermittent issues.t + +* **Improve the reporting at startup and also for /prison version** +Added more details to provide the user with more information about +the prison environment and also the integrations. It's a work in +progress and some of the current formats will be changing and will be +refactored to be more useful and easier to read. + +* **Bug fix in third party tool: Rewrite of the SpiGet version reporting tool** +Bug fix. Third party tool. Spiget "claims" it deals with semVers when +that is nothing even close to being true. I wrote a proper version +parser to use for Prison so semVer actually works correctly which will +hopefully eliminate some situations where it fails. +I may fork the spiget project in the near future and share this +code with that project so others can benefit from proper semVer +handling, which may address some of their open issues too. + +* **Added new bStats parameter - Mines, ranks, and ladder counts** +Added a new custom parameter to bStats to record the number of mines, ranks, and +ladders that has defined at startup. So this data may now be added as custom +charts, but may not be able to ever see it online since I do not own the +bstats online account to configure it correctly on that end. But at least the +data will be there if it's even enabled. + +* **Added /ranks player command** +New command /ranks player show what rank a player currently has. The player must +be online. Future could add support for offline players, but quick attempt to +hook that results in some internal failures within the Prison plugin so deferring +on that feature. + +* **Change How Integrations Work** +All directly accessed integrations are now logged and recorded so their status can be +included with the /prison version command. Integrations that are indirectly used, +such as through other plugins like Vault, are never listed directly unless Vault +references them. + +I went though the placeholder integrations and fixed their APIs to use the newer +set of place holders. Fixed some bugs and expanded features. The list of integrations +now also includes the primary URL where they can find more information on the plugins, +and where to download them from. Also provides some additional information, such as +available place holders that can be used. + +* **Added Minecraft Version** +Added minecraft version to be stored within the plugin so it can be displayed +in /prison version and also be used +in the future with selecting block types that are appropriate for the server version +that is being ran. + +* **Bug fix: Block types not saving correctly if depending upon magic values** +Found an issue where loading blocks were by the BlockType name, but saving was by the +minecraft id, which does not always match. As a result, there was the chance a block +type would be lost, or it would revert back to the generic such as Birch Block +reverting back to Oak Block. + +* **Need to update gradle - Was at v4.10.3 - Upgraded to v5.6.4** +Currently this project is using Gradle v4.10.3 and it needs to be updated to v5.6.4 or +even v6.0.1 which is the current latest release. It was decided to only take the project +to v5.6.4 for now, and wait for the next release on v6.x, which may gain more stability? +But to do that it must be incrementally updated to each minor version and you cannot just +jump ahead or there will be failures. At each step you need to evaluate your build scripts and +then make needed adjustments before moving onward. + + * **Versions Upgraded To:**: **v5.0**, **v5.1.1**, **v5.2.1**, v5.3.1, v5.4.1, v5.5.1, **v5.6.4**, + * **Versions to be Upgraded To**: v6.0, v6.0.1 + * gradlew wrapper --gradle-version 5.0 :: Sets the new wrapper version + * gradlew --version :: Will actually install the new version + * gradlew build :: Will build project with the new version to ensure all is good. If build is good, then you can try to upgrade to the next version. + * Update to v5.0 was successful. Had to remove enableFeaturePreview('STABLE_PUBLISHING') + since it has been deprecated. It does nothing in v5.x, but will be removed in v6.0. + * Update to v5.1.1, v5.2.1, v5.3.1, v5.4.1, v5.5.1, v5.6.4 was successful. + + +* **Minor clean up to Gradle scripts** +The "compile" directive is actually very old and was deprecated back around version +v2.x. The replacements for that is "implementation" or "api" instead. The use of +api does not make sense since its use is to tag when internal functions are exposed +to the outside world as if it will be used as an externally facing API. That +really does not fit our use case, but what api also does is to force compiling all +source that is associated with something marked as api, including everyone's children. So performance will suffer due to that usage, since it shuts down incremental +building of resources. + +I also found that use of compileOnly may not be used correctly, but at this point +I'm just leaving this as a mention and then revisit in the future if time +permits, or issues appear to be related. Its a very old addition that provided +gradle with "some" maven like behaviors. It was only intended to be strictly used +for compile time dependencies, such as for annotations that are only needed for +compile-time checks, of which the plugins and resources we have marked as +compileOnly do not fit that use case. +[discuss.gradle.org: compileOnly](https://discuss.gradle.org/t/is-it-recommended-to-use-compileonly-over-implementation-if-another-module-use-implementation-already/26699/2) + + +* **Redesigned mine block generation works - Async and paged enabled** +Redesigned how prison blocks are generated to improve performance and to allow the +asynch generation of blocks, but more importantly, allows for paging of actual +block updates. The paging is a major feature change that will allow for minimal +impact on server lag, even with stupid-large mines. The idea is to chop up the +large number of blocks that need to be regenerated in to smaller chunks that can +be performed within one or two ticks, then defer the other updates to the future. +Thus freeing up the main bukkit/spigot execution thread to allow other tasks +to run to help prevent the server from lagging. + +* **Support for LuckPerms v5.0.x** +In addition to supporting older versions of LuckPerms, Prison now is able to +integrate LuckPerms v5.0.x or LuckPerms v.4.x or earlier. Take your pick. + +* **Minor changes to reduce the number of compiler warnings** +Minor changes, but better code overall. + + +* **Improved mine regeneration performance by 33.3%** +Figured out how to improve performance on mine regeneration by roughly about 33.3% overall. This +could help drastically improve performance and reduce lag since block updates must run +synchronously to prevent server corruption (limitation is due to the bukkit and spigot api). + +* **Mine stats and whereami** +Added a new command, **/mines stats**, to toggle the stats on the mine resets. Viewable with **/mines list** and **/mines info**. Stats are now within the MineManager so it can be accessed from within +the individual mines. +Added a new command, **/mines whereami**, to tell you what mine you are in, or up to three mines you are nearest to and the distance from their centers. + + +* **Major restructuring of how Mines work - Self-managing their own workflow** +They now are able to self-manage their own workflow for sending out notifications and for resetting automatically. +Mines is now layered, where each layer (abstract class of its ancestors) contributes a type of behavior and business logic that allows Mines to be more autonomous. +As a result, PrisonMines and MineManager are greatly simplified since they have less to manage. +Because Mines are now self-managing their own workflow, and they have taken on a more expanded role, some of the mine configurations are obsolete and removed. +Mines only notify players that are within a limited range of their center; they no longer blindly broadcast to all players within a given world or the whole server. +Mines extend from MineScheduler, which extend from MineReset, which extend from MineData. Each layer focuses on it's own business rules and reduces the clutter in the others, which results in tighter +code and less moving parts and less possible errors and bugs. + + +* **Concept of distance added to Bound objects** +Added the concept of distance between two Bound objects so as to support new +functionalities between mines and players. + + +* **Gradle now ready to upgrade to v5** +Resolved the last few issues that would have caused issues when upgrading to gradle +v5. Explored how gradle can use java fragments and variables. + ## tag v3.2.0 - 2019-12-03 @@ -63,7 +1041,7 @@ Test server versions used in testing: * **Minecraft 1.11** * **Minecraft 1.12.2** * **Minecraft 1.13.2** - * **Minecraft 1.14.2** + * **Minecraft 1.14.4** * **Major refactoring of the prison-mines package** diff --git a/docs/docs-commands/prison_docs_command_10_sellall.md b/docs/docs-commands/prison_docs_command_10_sellall.md new file mode 100644 index 000000000..91a56da7a --- /dev/null +++ b/docs/docs-commands/prison_docs_command_10_sellall.md @@ -0,0 +1,26 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Show a list of subcommands for the sellall feature. + +## SubCommands: + +- `/sellall sell` +- `/sellall gui` +- `/sellall add` +- `/sellall delete` +- `/sellall multiplier` +- `/sellall setdefault` + +## How to use the command + +Execute the command with the argument you want following the instruction, on the [main doc](../prison_docs_000_toc.md) page there's a whole guidebook for [this](../prison_docs_113_setting_up_sellall.md)! +Example: `/sellall` + +### Command Format + +`/sellall ` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_11_ranks.md b/docs/docs-commands/prison_docs_command_11_ranks.md new file mode 100644 index 000000000..fe26d7c61 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_11_ranks.md @@ -0,0 +1,23 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_12_ranks_command.md b/docs/docs-commands/prison_docs_command_12_ranks_command.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_12_ranks_command.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_13_ranks_create.md b/docs/docs-commands/prison_docs_command_13_ranks_create.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_13_ranks_create.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_14_ranks_delete.md b/docs/docs-commands/prison_docs_command_14_ranks_delete.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_14_ranks_delete.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_15_ranks_demote.md b/docs/docs-commands/prison_docs_command_15_ranks_demote.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_15_ranks_demote.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_16_ranks_info.md b/docs/docs-commands/prison_docs_command_16_ranks_info.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_16_ranks_info.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_17_ranks_ladder_info.md b/docs/docs-commands/prison_docs_command_17_ranks_ladder_info.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_17_ranks_ladder_info.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_18_ranks_list.md b/docs/docs-commands/prison_docs_command_18_ranks_list.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_18_ranks_list.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_19_ranks_players.md b/docs/docs-commands/prison_docs_command_19_ranks_players.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_19_ranks_players.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_1_prison.md b/docs/docs-commands/prison_docs_command_1_prison.md new file mode 100644 index 000000000..eb09501a5 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_1_prison.md @@ -0,0 +1,36 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +/Prison's the Main core command, by executing it you'll get a list of all the subcommands. + +**NOTE:** You can get some info about ALL subcommands or commands by adding the argument `help` at the end of it, for example `/prison help`. + +## Permission: + +- `prison.admin` + +## SubCommands: + +- [`/prison alerts`](prison_docs_command_2_prison_alerts.md) +- [`/prison autofeatures`](prison_docs_command_3_prison_autofeatures.md) +- [`/prison gui`](prison_docs_command_4_prison_gui.md) +- [`/prison modules`](prison_docs_command_5_prison_modules.md) +- [`/prison placeholders`](prison_docs_command_6_prison_placeholders.md) +- [`/prison reload`](prison_docs_command_7_prison_reload.md) +- [`/prison troubleshoot [name]`](prison_docs_command_8_troubleshoot.md) +- [`/prison version`](prison_docs_command_9_prison_version.md) + +## How to use the command + +Execute: +`/prison` +and all the subcommands will show up + +### Command Format + +`/prison [Arguments]` + +**END of the command INFO** + diff --git a/docs/docs-commands/prison_docs_command_20_ranks_player.md b/docs/docs-commands/prison_docs_command_20_ranks_player.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_20_ranks_player.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_21_ranks_promote.md b/docs/docs-commands/prison_docs_command_21_ranks_promote.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_21_ranks_promote.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_22_ranks_set.md b/docs/docs-commands/prison_docs_command_22_ranks_set.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_22_ranks_set.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_23_mines.md b/docs/docs-commands/prison_docs_command_23_mines.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_23_mines.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_24_mines_block.md b/docs/docs-commands/prison_docs_command_24_mines_block.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_24_mines_block.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_25_mines_command.md b/docs/docs-commands/prison_docs_command_25_mines_command.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_25_mines_command.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_26_mines_create.md b/docs/docs-commands/prison_docs_command_26_mines_create.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_26_mines_create.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_27_mines_delete.md b/docs/docs-commands/prison_docs_command_27_mines_delete.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_27_mines_delete.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_28_mines_info.md b/docs/docs-commands/prison_docs_command_28_mines_info.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_28_mines_info.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_29_mines_list.md b/docs/docs-commands/prison_docs_command_29_mines_list.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_29_mines_list.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_2_prison_alerts.md b/docs/docs-commands/prison_docs_command_2_prison_alerts.md new file mode 100644 index 000000000..c104725ef --- /dev/null +++ b/docs/docs-commands/prison_docs_command_2_prison_alerts.md @@ -0,0 +1,28 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Prison Alerts, you can clear a Prison Alert or All of them + +## Permission: + +- `prison.alerts` + +## SubCommands: + +- `/prison alerts clearall` clear all alerts +- `/prison alerts clear` clear an alert + +## How to use the command + +Execute `/prison alerts` followed by the `argument` you want, such as `clearall` or `clear` to use the command + +**NOTE:** +You can't use this command from the console! + +### Command Format + +`/prison alerts ` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_30_mines_reset.md b/docs/docs-commands/prison_docs_command_30_mines_reset.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_30_mines_reset.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_31_mines_set.md b/docs/docs-commands/prison_docs_command_31_mines_set.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_31_mines_set.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_32_mines_stats.md b/docs/docs-commands/prison_docs_command_32_mines_stats.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_32_mines_stats.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_33_mines_tp.md b/docs/docs-commands/prison_docs_command_33_mines_tp.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_33_mines_tp.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_34_mines_wand.md b/docs/docs-commands/prison_docs_command_34_mines_wand.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_34_mines_wand.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_35_mines_whereami.md b/docs/docs-commands/prison_docs_command_35_mines_whereami.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_35_mines_whereami.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_36_prisonmanager.md b/docs/docs-commands/prison_docs_command_36_prisonmanager.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_36_prisonmanager.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_37_prestiges.md b/docs/docs-commands/prison_docs_command_37_prestiges.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_37_prestiges.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_38_prestige.md b/docs/docs-commands/prison_docs_command_38_prestige.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_38_prestige.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_39_rankupmax.md b/docs/docs-commands/prison_docs_command_39_rankupmax.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_39_rankupmax.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_3_prison_autofeatures.md b/docs/docs-commands/prison_docs_command_3_prison_autofeatures.md new file mode 100644 index 000000000..b596503a0 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_3_prison_autofeatures.md @@ -0,0 +1,30 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Show a description of the AutoFeatures and some info about them. + +**NOTE:** You should enable in the **config.yml** the autofeatures first, then you can edit them from the `/prison gui` or the `autoFeaturesConfig.yml`. + +## Permissions: + +- `prison.admin` +- `prison.automanager` +- `prison.automanager.pickup` +- `prison.automanager.smelt` +- `prison.automanager.block` + +## SubCommands: + +- `none` + +## How to use the command + +Just execute the command himself `/prison autofeatures` + +### Command Format + +`/prison autofeatures` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_40_rankup.md b/docs/docs-commands/prison_docs_command_40_rankup.md new file mode 100644 index 000000000..1e798a48b --- /dev/null +++ b/docs/docs-commands/prison_docs_command_40_rankup.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Short description of the command will be here + +## SubCommands: + +- List +- Of +- Sub +- Commands +- With +- Short +- Description + +## How to use the command + +### Command Format + +`/prison` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_4_prison_gui.md b/docs/docs-commands/prison_docs_command_4_prison_gui.md new file mode 100644 index 000000000..b3c5e18e1 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_4_prison_gui.md @@ -0,0 +1,25 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +This command will open the `/prison gui` where an admin can manage a lot of things about prison on a GUI. + +## Permission: + +- `prison.gui` +- `prison.admin` + +## SubCommands: + +- `none` + +## How to use the command + +Just execute the command himself `/prison gui` + +### Command Format + +`/prison gui` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_5_prison_modules.md b/docs/docs-commands/prison_docs_command_5_prison_modules.md new file mode 100644 index 000000000..2af0ae379 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_5_prison_modules.md @@ -0,0 +1,25 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Prison Modules command, will show a "status" for modules, like enabled or disabled. + +## Permission: + +- `prison.modules` +- `prison.admin` + +## SubCommands: + +- `none` + +## How to use the command + +Just execute the command himself `/prison modules` + +### Command Format + +`/prison modules` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_6_prison_placeholders.md b/docs/docs-commands/prison_docs_command_6_prison_placeholders.md new file mode 100644 index 000000000..246dd184e --- /dev/null +++ b/docs/docs-commands/prison_docs_command_6_prison_placeholders.md @@ -0,0 +1,27 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Show a list of Prison Placeholders available at the moment you execute the command in the place you're. + +## Permission: + +- `prison.placeholder` + +## SubCommands: + +- `/prison placeholders list` Show a list of placeholders +- `/prison placeholders reload` Reload placeholders +- `/prison placeholders search [playerName] [pageNumber] [patterns]` Search placeholders for a player +- `/prison placeholders test [text]` Test a placeholder + +## How to use the command + +Execute `/prison placeholders` and add the `argument` you want at the end of it. + +### Command Format + +`/prison placeholders ` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_7_prison_reload.md b/docs/docs-commands/prison_docs_command_7_prison_reload.md new file mode 100644 index 000000000..a6971b3d0 --- /dev/null +++ b/docs/docs-commands/prison_docs_command_7_prison_reload.md @@ -0,0 +1,26 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Reload the Prison Plugin -> Show a list of reload commands for the plugin. + +## Permissions: + +- `prison.admin` +- `prison.reload` +- `prison.placeholder` + +## SubCommands: + +- `/prison reload placeholders` + +## How to use the command + +Execute `/prison reload` and add the argument you want at the end of it, for example `/prison reload placeholders` + +### Command Format + +`/prison reload ` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/docs-commands/prison_docs_command_9_prison_version.md b/docs/docs-commands/prison_docs_command_9_prison_version.md new file mode 100644 index 000000000..a3559038a --- /dev/null +++ b/docs/docs-commands/prison_docs_command_9_prison_version.md @@ -0,0 +1,24 @@ +### Prison Documentation - **WORK-IN-PROGRESS** +[Prison Documents - Table of Contents](../prison_docs_000_toc.md) + +## Description: + +Show many infos about prison and also some placeholders + +## Permission: + +- `prison admin` + +## SubCommands: + +- `none` + +## How to use the command + +Run the command himself to use this, example: `/prison version` + +### Command Format + +`/prison version` + +**END of the command INFO** \ No newline at end of file diff --git a/docs/images/prison_docs_013_Prison_Help_startup_1.png b/docs/images/prison_docs_013_Prison_Help_startup_1.png new file mode 100644 index 000000000..63cfeed78 Binary files /dev/null and b/docs/images/prison_docs_013_Prison_Help_startup_1.png differ diff --git a/docs/images/prison_docs_013_Prison_Help_startup_2.png b/docs/images/prison_docs_013_Prison_Help_startup_2.png new file mode 100644 index 000000000..cbf8da1a2 Binary files /dev/null and b/docs/images/prison_docs_013_Prison_Help_startup_2.png differ diff --git a/docs/images/prison_docs_013_Prison_Help_startup_3.png b/docs/images/prison_docs_013_Prison_Help_startup_3.png new file mode 100644 index 000000000..97a99f823 Binary files /dev/null and b/docs/images/prison_docs_013_Prison_Help_startup_3.png differ diff --git a/docs/images/prison_docs_013_Prison_Help_startup_4.png b/docs/images/prison_docs_013_Prison_Help_startup_4.png new file mode 100644 index 000000000..7f0c220da Binary files /dev/null and b/docs/images/prison_docs_013_Prison_Help_startup_4.png differ diff --git a/docs/images/prison_docs_013_Prison_Help_startup_5.png b/docs/images/prison_docs_013_Prison_Help_startup_5.png new file mode 100644 index 000000000..715ead270 Binary files /dev/null and b/docs/images/prison_docs_013_Prison_Help_startup_5.png differ diff --git a/docs/images/prison_docs_0xx_setting_up_EssentialsX_01.png b/docs/images/prison_docs_0xx_setting_up_EssentialsX_01.png new file mode 100644 index 000000000..9a9fce268 Binary files /dev/null and b/docs/images/prison_docs_0xx_setting_up_EssentialsX_01.png differ diff --git a/docs/images/prison_docs_0xx_setting_up_EssentialsX_02.png b/docs/images/prison_docs_0xx_setting_up_EssentialsX_02.png new file mode 100644 index 000000000..e75d3e57f Binary files /dev/null and b/docs/images/prison_docs_0xx_setting_up_EssentialsX_02.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_01.png b/docs/images/prison_docs_101_setting_up_mines_01.png new file mode 100644 index 000000000..7eb74f4c6 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_01.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_02.png b/docs/images/prison_docs_101_setting_up_mines_02.png new file mode 100644 index 000000000..93e9f1cdc Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_02.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_03.png b/docs/images/prison_docs_101_setting_up_mines_03.png new file mode 100644 index 000000000..4e2b0dc2c Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_03.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_04.png b/docs/images/prison_docs_101_setting_up_mines_04.png new file mode 100644 index 000000000..86d70cec4 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_04.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_05.png b/docs/images/prison_docs_101_setting_up_mines_05.png new file mode 100644 index 000000000..70419d4bf Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_05.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_06.png b/docs/images/prison_docs_101_setting_up_mines_06.png new file mode 100644 index 000000000..961de77c9 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_06.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_07.png b/docs/images/prison_docs_101_setting_up_mines_07.png new file mode 100644 index 000000000..8f688a23e Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_07.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_08.png b/docs/images/prison_docs_101_setting_up_mines_08.png new file mode 100644 index 000000000..8d3e2ccb8 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_08.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_09.png b/docs/images/prison_docs_101_setting_up_mines_09.png new file mode 100644 index 000000000..3b2d56c56 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_09.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_10.png b/docs/images/prison_docs_101_setting_up_mines_10.png new file mode 100644 index 000000000..5c4ec4904 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_10.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_11.png b/docs/images/prison_docs_101_setting_up_mines_11.png new file mode 100644 index 000000000..e430f705e Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_11.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_12.png b/docs/images/prison_docs_101_setting_up_mines_12.png new file mode 100644 index 000000000..58671edee Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_12.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_13.png b/docs/images/prison_docs_101_setting_up_mines_13.png new file mode 100644 index 000000000..1c10deeb7 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_13.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_14.png b/docs/images/prison_docs_101_setting_up_mines_14.png new file mode 100644 index 000000000..8cb9b241b Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_14.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_15.png b/docs/images/prison_docs_101_setting_up_mines_15.png new file mode 100644 index 000000000..0a7e8faf4 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_15.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_16.png b/docs/images/prison_docs_101_setting_up_mines_16.png new file mode 100644 index 000000000..52cb5d5a9 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_16.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_17.png b/docs/images/prison_docs_101_setting_up_mines_17.png new file mode 100644 index 000000000..c5b889d02 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_17.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_18.png b/docs/images/prison_docs_101_setting_up_mines_18.png new file mode 100644 index 000000000..c8f4ee890 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_18.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_19.png b/docs/images/prison_docs_101_setting_up_mines_19.png new file mode 100644 index 000000000..de04b333e Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_19.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_20.gif b/docs/images/prison_docs_101_setting_up_mines_20.gif new file mode 100644 index 000000000..3e05a82d7 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_20.gif differ diff --git a/docs/images/prison_docs_101_setting_up_mines_21.png b/docs/images/prison_docs_101_setting_up_mines_21.png new file mode 100644 index 000000000..4729ae501 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_21.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_22.png b/docs/images/prison_docs_101_setting_up_mines_22.png new file mode 100644 index 000000000..73c6816e5 Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_22.png differ diff --git a/docs/images/prison_docs_101_setting_up_mines_23.png b/docs/images/prison_docs_101_setting_up_mines_23.png new file mode 100644 index 000000000..7ca93507a Binary files /dev/null and b/docs/images/prison_docs_101_setting_up_mines_23.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_1.png b/docs/images/prison_docs_102_setting_up_ranks_1.png new file mode 100644 index 000000000..2a5f77a4f Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_1.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_2.png b/docs/images/prison_docs_102_setting_up_ranks_2.png new file mode 100644 index 000000000..c751bf44a Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_2.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_3.png b/docs/images/prison_docs_102_setting_up_ranks_3.png new file mode 100644 index 000000000..74e02bbc9 Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_3.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_4.png b/docs/images/prison_docs_102_setting_up_ranks_4.png new file mode 100644 index 000000000..a0cdf5328 Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_4.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_5.png b/docs/images/prison_docs_102_setting_up_ranks_5.png new file mode 100644 index 000000000..9c9279ff7 Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_5.png differ diff --git a/docs/images/prison_docs_102_setting_up_ranks_6.png b/docs/images/prison_docs_102_setting_up_ranks_6.png new file mode 100644 index 000000000..aa45ef819 Binary files /dev/null and b/docs/images/prison_docs_102_setting_up_ranks_6.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_01.png b/docs/images/prison_docs_107_setting_up_prestiges_01.png new file mode 100644 index 000000000..2612a45b4 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_01.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_02.png b/docs/images/prison_docs_107_setting_up_prestiges_02.png new file mode 100644 index 000000000..f851ae763 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_02.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_03.png b/docs/images/prison_docs_107_setting_up_prestiges_03.png new file mode 100644 index 000000000..4e01e5c1d Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_03.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_04.png b/docs/images/prison_docs_107_setting_up_prestiges_04.png new file mode 100644 index 000000000..70b1c9069 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_04.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_05.png b/docs/images/prison_docs_107_setting_up_prestiges_05.png new file mode 100644 index 000000000..ac4c7d254 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_05.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_06.png b/docs/images/prison_docs_107_setting_up_prestiges_06.png new file mode 100644 index 000000000..41b83ce6c Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_06.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_07.png b/docs/images/prison_docs_107_setting_up_prestiges_07.png new file mode 100644 index 000000000..7c2a05ed1 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_07.png differ diff --git a/docs/images/prison_docs_107_setting_up_prestiges_08.png b/docs/images/prison_docs_107_setting_up_prestiges_08.png new file mode 100644 index 000000000..d3fe7e146 Binary files /dev/null and b/docs/images/prison_docs_107_setting_up_prestiges_08.png differ diff --git a/docs/images/prison_docs_111_mine_commands_01.png b/docs/images/prison_docs_111_mine_commands_01.png new file mode 100644 index 000000000..7696f450d Binary files /dev/null and b/docs/images/prison_docs_111_mine_commands_01.png differ diff --git a/docs/images/prison_docs_111_mine_commands_02.png b/docs/images/prison_docs_111_mine_commands_02.png new file mode 100644 index 000000000..e59494af2 Binary files /dev/null and b/docs/images/prison_docs_111_mine_commands_02.png differ diff --git a/docs/images/prison_docs_111_mine_commands_03.png b/docs/images/prison_docs_111_mine_commands_03.png new file mode 100644 index 000000000..19efdf07c Binary files /dev/null and b/docs/images/prison_docs_111_mine_commands_03.png differ diff --git a/docs/images/prison_docs_111_mine_commands_04.png b/docs/images/prison_docs_111_mine_commands_04.png new file mode 100644 index 000000000..31597040e Binary files /dev/null and b/docs/images/prison_docs_111_mine_commands_04.png differ diff --git a/docs/images/prison_docs_111_mine_commands_05.png b/docs/images/prison_docs_111_mine_commands_05.png new file mode 100644 index 000000000..528e1d383 Binary files /dev/null and b/docs/images/prison_docs_111_mine_commands_05.png differ diff --git a/docs/images/prison_docs_310_guide_placeholders_1.png b/docs/images/prison_docs_310_guide_placeholders_1.png new file mode 100644 index 000000000..4dd6faf79 Binary files /dev/null and b/docs/images/prison_docs_310_guide_placeholders_1.png differ diff --git a/docs/images/prison_docs_310_guide_placeholders_2.png b/docs/images/prison_docs_310_guide_placeholders_2.png new file mode 100644 index 000000000..1d360d2a7 Binary files /dev/null and b/docs/images/prison_docs_310_guide_placeholders_2.png differ diff --git a/docs/images/prison_docs_310_guide_placeholders_3.png b/docs/images/prison_docs_310_guide_placeholders_3.png new file mode 100644 index 000000000..73155190a Binary files /dev/null and b/docs/images/prison_docs_310_guide_placeholders_3.png differ diff --git a/docs/images/prison_docs_310_guide_placeholders_4.png b/docs/images/prison_docs_310_guide_placeholders_4.png new file mode 100644 index 000000000..feee12437 Binary files /dev/null and b/docs/images/prison_docs_310_guide_placeholders_4.png differ diff --git a/docs/images/prison_docs_310_guide_placeholders_5.png b/docs/images/prison_docs_310_guide_placeholders_5.png new file mode 100644 index 000000000..4009314e8 Binary files /dev/null and b/docs/images/prison_docs_310_guide_placeholders_5.png differ diff --git a/docs/prison_docs_000_toc.md b/docs/prison_docs_000_toc.md new file mode 100644 index 000000000..0ff5444c0 --- /dev/null +++ b/docs/prison_docs_000_toc.md @@ -0,0 +1,248 @@ +# Prison Documentation + +
+ + +## Project Related + +* **[Prison README.md](../README.md)** +* **[Prison LICENSE.md](../LICENSE.md)** +* **[Prison Change logs](../changelog_v3.2.x.md)** Detailed changes to prison. +* **[Prison Known Issues](../knownissues_v3.2.x.md)** Known Issues and To Do's. +* **[Prison Discord Server](https://discord.gg/DCJ3j6r)** Get help here. + + +* **[Most Recent Version of Prison Documentation](https://github.com/PrisonTeam/Prison/blob/bleeding/docs/prison_docs_000_toc.md)** Docs change frequently, but GITHUG's master versions do not. These are the latest docs! + + +
+ + + +# Table of Contents for this Document + +* [Commands Shortcut Help](#commands-shortcut-help) + +* [Guides - Setting up the Server Basics](#guides-setting-up-the-server-basics) +* [Guides - Configuring Prison](#guides-configuring-prison) + +* [Guides - Working with other Plugins](#guides-working-with-other-plugins) +* [Guides - Configuring Other Plugins for Prison](#guides-configuring-other-plugins-for-prison) +* [Guides - FAQs](#guides-faqs) + +
+ + + +### Commands Shortcut help + +*Adding the `help` argument as the first parameter of any command will show additional help.* + + +**PRISON COMMANDS:** +(Work-In-Progress) + +- [/prison](docs-commands/prison_docs_command_1_prison.md) +- [/prison alerts](docs-commands/prison_docs_command_2_prison_alerts.md) +- [/prison autofeatures](docs-commands/prison_docs_command_3_prison_autofeatures.md) +- [/prison gui](docs-commands/prison_docs_command_4_prison_gui.md) +- [/prison modules](docs-commands/prison_docs_command_5_prison_modules.md) +- [/prison placeholders](docs-commands/prison_docs_command_6_prison_placeholders.md) +- [/prison reload](docs-commands/prison_docs_command_7_prison_reload.md) +- [/prison version](docs-commands/prison_docs_command_9_prison_version.md) + + +**RANKS COMMANDS: _Guidebook TO-DO_** (Work-In-Progress) + +- [/ranks \[ladder\] ](docs-commands/prison_docs_command_11_ranks.md) +- [/ranks command](docs-commands/prison_docs_command_12_ranks_command.md) +- [/ranks create \[rankName\] \[cost\] \[ladder\] \[tag\] ](docs-commands/prison_docs_command_13_ranks_create.md) +- [/ranks delete \[rankName\] ](docs-commands/prison_docs_command_14_ranks_delete.md) +- [/ranks demote \[playerName\] \[ladder\] \[chargePlayers\] ](docs-commands/prison_docs_command_15_ranks_demote.md) +- [/ranks info \[rankName\] ](docs-commands/prison_docs_command_16_ranks_info.md) +- [/ranks ladder](docs-commands/prison_docs_command_17_ranks_ladder_info.md) +- [/ranks list \[ladderName\] ](docs-commands/prison_docs_command_18_ranks_list.md) +- [/ranks players \[ladderName\] \[action\] ](docs-commands/prison_docs_command_19_ranks_players.md) +- [/ranks player \[player\] ](docs-commands/prison_docs_command_20_ranks_player.md) +- [/ranks promote \[playerName\] \[ladder\] \[chargePlayers\] ](docs-commands/prison_docs_command_21_ranks_promote.md) +- [/ranks set](docs-commands/prison_docs_command_22_ranks_set.md) + +**MINES COMMANDS: _Guidebook TO-DO_** (Work-In-Progress) + +- [/mines](docs-commands/prison_docs_command_23_mines.md) +- [/mines block](docs-commands/prison_docs_command_24_mines_block.md) +- [/mines command](docs-commands/prison_docs_command_25_mines_command.md) +- [/mines create \[mineName\] ](docs-commands/prison_docs_command_26_mines_create.md) +- [/mines delete \[mineName\] \[confirm\] ](docs-commands/prison_docs_command_27_mines_delete.md) +- [/mines info \[mineName\] \[page\] ](docs-commands/prison_docs_command_28_mines_info.md) +- [/mines list \[page\] ](docs-commands/prison_docs_command_29_mines_list.md) +- [/mines reset \[mineName\] ](docs-commands/prison_docs_command_30_mines_reset.md) +- [/mines set](docs-commands/prison_docs_command_31_mines_set.md) +- [/mines stats](docs-commands/prison_docs_command_32_mines_stats.md) +- [/mines tp \[mineName\] ](docs-commands/prison_docs_command_33_mines_tp.md) +- [/mines wand](docs-commands/prison_docs_command_34_mines_wand.md) +- [/mines whereami](docs-commands/prison_docs_command_35_mines_whereami.md) + +**MORE COMMANDS: _Guidebook TO-DO_** (Work-In-Progress) + +- [/sellall](docs-commands/prison_docs_command_10_sellall.md) +- [/prisonmanager](docs-commands/prison_docs_command_36_prisonmanager.md) +- [/prestiges](docs-commands/prison_docs_command_37_prestiges.md) +- [/prestige](docs-commands/prison_docs_command_38_prestige.md) +- [/rankupMax \[ladder\]](docs-commands/prison_docs_command_39_rankupmax.md) +- [/rankup \[ladder\]](docs-commands/prison_docs_command_40_rankup.md) + + + +
+ + + + + + +# Guides: Setting Up the Server Basics + + +* [Setting up a Spigot Server](prison_docs_010_setting_up_a_spigot_server.md) + Setting up Java. Setting up and running BuildTool. Creating a runnable Spigot Server. + + +* [Setting up Prison - Basics](prison_docs_012_setting_up_prison_basics.md) **Required** + Adding the plugin to your server. + + +* [Prison - Getting Help](prison_docs_013_Prison_Help.md) **Important** + Commands. Getting help for issues. Upgrading Prison. + + +* [Prison - Prison Configuration Files](prison_docs_014_Prison_Configs.md) **Important** + Customizing how Prison works by changing the Config files. + + + + +* **Setting up Vault** - (coming soon) + Including Vault is Strongly Suggested. + Hint: Download from spigotmc.org and copy jar to plugin folder. Zero setup. + + +* [Setting up EssentialsX](prison_docs_0xx_setting_up_EssentialsX.md) - + Including EssentialsX is Strongly Suggested - Configuring EssentialsX Chat placeholders - Recommend which modules to include. + + +* [Setting up LuckPerms](prison_docs_020_setting_up_luckperms.md) + Setting up LuckPerms. Warning about LuckPerms Versions. + + +* [Setting up PermissionsEX](prison_docs_022_setting_up_PermissionsEX.md) + Setting up PermissionsEX. Warning about being obsolete. + + +* [Setting up Ultra Permissions](prison_docs_024_setting_up_Ultra_Permissions.md) + Setting up Ultra Permissions. Compatible with Prison 3.2+ + + +* [Setting up WorldGuard & WorldEdit](prison_docs_026_setting_up_worldguard_worldedit.md) + Installing the plugins + + +* [Setting up PlaceholderAPI](prison_docs_0xx_setting_up_PlaceholderAPI.md) - Strongly Suggested if using placeholders. Troubleshooting. + +
+ + + +# Guides: Configuring Prison + +These guides cover the core fundamentals of Prison. They may reference other plugins, but these guides will not go in to depth on how to install or use the other plugins; those details will be a focused guide of their own. + + + +* [Setting up Mines](prison_docs_101_setting_up_mines.md) + Basics of creating. Searching for Blocks. Customizing. One-block mine example with HolographicDisplays. + + + +* [Setting up Ranks & Ladders](prison_docs_102_setting_up_ranks.md) + **-+= New! Work In Progress! =+-** Configuring and using Ranks and Ladders. + + + +* [Setting up Prestiges](prison_docs_107_setting_up_pestiges.md) + Configuring and using Prestiges. + + + +* [Mine Commands](prison_docs_111_mine_commands.md) + What they are. Setting up. Working with Mine Commands. + + + +* [Setting up SellAll](prison_docs_113_setting_up_sellall.md) + What's SellAll, how to use it and set up (Including sellall **multipliers**). + + + +
+ + + + + + +# Guides: Working with other Plugins + + +* [Guide: Prison Placeholders](prison_docs_310_guide_placeholders.md) How to use. Includes HolographicDisplays. + + +**Work In Progress** + + + +
+ + + + +# Guides: Configuring Other Plugins for Prison + +This section of guides will focus more on other plugins and how they can integrate with Prison. They may use a few aspects of the prison API or command interface, but these guides will spend the majority of their time covering the other technologies that can help give Prison your own character. + + +* [Configuring and Using WorldGuard with LuckPerms to Protect Mines](prison_docs_626_configuring_worldguard_regions.md) + This guide uses LuckPerms in the examples, but any other permission plugin will work just as well. + + +* [Setting up EssentialsX Warps for Players](prison_docs_630_configuring_warps.md) + + +* [Ideas on Setting up Donor Mines and Private Mines](prison_docs_628_configuring_private_mines.md) + + + +* **Work In Progress** + + + + + + +
+ + + +# Guides: FAQs + +This section of guides will focus on FAQs; short helps for common problems. + + +* [FAQ - Other Plugins](prison_docs_810_faq_other_plugins.md) + CMI Plugin. Prison fails to load. No modules. + + +* [FAQ - Miscellaneous Questions](prison_docs_880_faq_misc_01.md) + Is Paper Supported? Setting the Currency symbol. + +
diff --git a/docs/prison_docs_010_setting_up_a_spigot_server.md b/docs/prison_docs_010_setting_up_a_spigot_server.md new file mode 100644 index 000000000..12d6f9419 --- /dev/null +++ b/docs/prison_docs_010_setting_up_a_spigot_server.md @@ -0,0 +1,128 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up a Spigot Server + +The Prison documentation covers how to setup a Spigot server for use as either +a test environment, a production server. These instructions covers how to +use the spigotmc's buildtools to simplify not only the initial setup, +but also provides easy updates. + +Buildtools also allows easy setup of many test environments since all you +would need to do is to just change the version. + + + +
+ + +# Setting up Java +This is intended strictly as a high-level overview on how to setup Java. +If you need more assistance, please search for online documentation since +there are many good resources out there. + +* First install a copy of Java that is accessible from the command line. + - It’s strongly suggested to use only the latest version 1.8.0_x since that is the version spigot and most other plugins are developed under. + + +* You can download it from [Sun SE Development Kit 8]https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html) for product that you need. + + +* You can also use Open JDK if Sun's license does not fit your needs. [OpenJDK Install](https://openjdk.java.net/install/) + + +* Test the java install with `java -version` from a command line + +
+ + + +# Setting Up and Running BuildTools + +* Download the spigot `BuildTools.jar` file. Follow the directions from here: + [https://www.spigotmc.org/wiki/buildtools/](https://www.spigotmc.org/wiki/buildtools/) + + +* Once you have downloaded BuildTools.jar, you will run *one* of the following commands (only one) from the command line. Multiple versions are shown as an example of how by simply specifying different versions, the same BuildTool will generate the different environments. The Windows and linux command line usage are the same. See the provided URL in the first step above for the list of valid versions that are supported by the BuildTools.jar file. + + java -jar BuildTools.jar --rev 1.8.8 + java -jar BuildTools.jar --rev 1.12.2 + java -jar BuildTools.jar --rev 1.13.2 + java -jar BuildTools.jar --rev 1.14.4 + java -jar BuildTools.jar --rev 1.15.1 + + +* For example, with BuildTools.jar being in a root directory, create a subdirectory and then start a build within that directory. The benefit is that you can use the same BuildTools.jar to build multiple different versions of spigot. This example starts off with building a v1.14.4 instance and then builds a 1.8.8 instance. + + mkdir spigot-1.14.4 + cd spigot-1.14.4 + java -jar ../BuildTools.jar –rev 1.14.4 + cd .. + mkdir spigot-1.8.8 + cd spigot-1.8.8 + java -jar ../BuildTools.jar –rev 1.8.8 + + +* **Updating BuildTools:** Once in a while you will be prompted to update the BuildTools.jar file. To do update it, all you need to do is to just download it, and replace the older one you were using. + + +* **Updating the built servers:** Every once in a while, when you are starting a server, there may be a notification that the server software needs to be update. Just rerun the BuildTools for the same version within the original build directory. The build tools will update all of the changed resources and then generate the new server jars that you copy to the actual server (see the next step). + + +
+ + + +# Creating a Runnable Spigot Server + +* Create a runnable server directory by creating a new directory outside of the build directory. Then copy the newly generated jar file, such as spigot-1.14.4.jar, to the new server run time directory. + + +* Windows example, if you’re still in the build directory: + + cd ../.. + mkdir spigot-1.14.4_server + copy /B builds\spigot-1.14.4\spigot-1.14.4.jar spigot-1.14.4_server + + +* Linux example, if you’re still in the build directory: + + cd ../.. + mkdir spigot-1.14.4_server + cp builds/spigot-1.14.4/spigot-1.14.4.jar spigot-1.14.4_server + + + +* Run the server for the first time (see the next step). It will start to auto-generate the server environment and then will stop. You will need to manually modify eula.txt and set eula=true. Save. Close. Restart the server. It will startup successfully now. + + +* This is a simple example of what is needed for a windows cmd file. It sets the minimum memory to 2 GB and the max to 8 GB. A linux script would be similar, but without the pause. + + java -Xms2g -Xmx8g -jar spigot-1.14.4.jar + pause + +* Let the server fully start for the first time, then stop it by entering “stop” in the open server console. The server is now ready to be customized. + + +This should give you a functional server for which to run the Prison plugin. + + +
+ + + +# Customizing your Server + +You can now customize the server, such as by adding plugins, etc... + + +
+ + + +# Other Server Platforms + +The same general instructions apply to paper server, but the big difference is that you download the runnable paper jar file from https://papermc.io/downloads and then place them in to your server runtime directory, and then follow the steps under **Creating a Runnable Spigot Server** but use the resources for the other platform. Then run the same general startup scripts. + + diff --git a/docs/prison_docs_012_setting_up_prison_basics.md b/docs/prison_docs_012_setting_up_prison_basics.md new file mode 100644 index 000000000..b4d835409 --- /dev/null +++ b/docs/prison_docs_012_setting_up_prison_basics.md @@ -0,0 +1,162 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up Prison - The Basics + +This document provides a quick overview on how to install Prison and get it running. + +
+ + + +# Dependencies + +* **None - No hard dependencies** - There are no hard dependencies for Prison. + +There may be no hard dependencies that will prevent Prison from running, but there are some core plugins that will make it easier to use. This short list is just a suggestion, but alternatives do exist and may be outside of our ability to comment or assist in their usage. + + +* **Vault** - Optional, but STRONGLY Suggested - This is perhaps the most important plugin. This plugin provides a common way to access other plugins running on your server, but without having to write any code within Prison to support them. Vault provides the mapping of a plugin's unique APIs to a common Vault API. Vault helps support Economy, Permissions, and Placeholders. Because of Vault, Prison can work flawlessly with dozens of other plugins. Please refer to Vault's documentation for what it supports. + + +* **EssentialsX** - Optional, but STRONGLY Suggested - Provides many of the basic commands and behaviors that you would expect from a Spigot server such as chat, warps, and even some moderation commands. EssentialsX is not Essentials, since Essentials is an older abandoned project, and EssentialsX is a forked project that is still maintained. Unfortunately, internally it is identified as simply Essentials, but you can tell it's EssentialsX if the version is greater than 2.15.x. + + +* **EssentialsX Chat** - Optional - Enhanced Chat experience. + + +* **EssentialsX Spawn** - Optional - Enhanced support for Spawns. + + +* **A permissions plugin of your choice** - Required - Prison works with many different permission plugins through Vault. I strongly suggest LuckPerms since it is free and is under active development so bugs and performance issues will be addressed. + * Warning: LuckPerms v5.0.x crashes older versions of prison, such as V3.2.0 and earlier. + * Notice: Prison v3.2.1 (including the pre-release versions) supports all versions of LuckPerms. + * Strongly suggest using LuckPerms v5.0.x with latest pre-release version of Prison. + + +* **PlaceholderAPI** - Used through Vault, it is free and provides the core interfaces in to the usage of placeholders. Prison also has a special integration setup for PlaceholderAPI to register all of the Prison placeholders dynamically upon startup. You do not need to download anything from the cloud for prison to work with this plugin. + + +
+ + +# Download Prison + +Download Prison from [spigotmc.org's Prison History Page](https://www.spigotmc.org/resources/prison.1223/history). + + +Setting up Prison is simple: + +* Download Prison - Current Releases + - Go to the SpigotMC.org LuckPerm's resource page: + - [Prison Downloads](https://www.spigotmc.org/resources/prison.1223/history "Prison download can be found under the Version History tab") + - Click on the Version History tab + - Choose the version to download + +* **Download Prison's Pre-Release Version** + - Useful to access newer features and fixes + - You can usually find a build on the Discord Server: + - [Prison Discord Server](https://discord.gg/DCJ3j6r) + +* Copy to your server's plugin directory + +* Restart the server + +* Follow Prison's documentation on customization, but at this point it's ready for use. + + +
+ + + +# Important Prison Information + +Upon starting up, Prison will display a lot of information in the server's console. +This information is intended to help you configure and confirm that prison started +correctly with all of the related resources that it is using. It also provides you +with valuable information that is needed to help troubleshoot issues, if you should +happen to encounter any. + +Some of the important details that are listed: +* Versions of Prison, Spigot, and other plugins +* Which modules were successfully loaded +* The root commands that are available +* The integration status of all related plugins that are supported +* The list of active placeholders +* Startup error messages, if any. Examples would be if a rank is configured to use a +custom currency and that currency cannot be found in any economy. + + +
+ + + +# Getting Help + +If you should run in to any questions or issues, please first review all online documentation first. + + +If you cannot find what you need within the documentation, please visit our Discord server to get the quickest responses there. +[Prison Discord Server](https://discord.gg/DCJ3j6r) + + +In order to provide the best support, the prison startup screen provides most of the information that is needed to help trouble shoot your issues. Please take a screen print and provide on discord with a detailed explanation of the issue. + + + +If you do encounter an issue, and the startup information is requested, please include +everything from the first line to the last. Please take a screen print and provide on +discord with a detailed explanation of the issue. Include everything from: + +``` + [16:21:30 INFO]: [Prison] Enabling Prison v3.2.1-alpha.13 +``` + +through: + +``` + [16:21:31 INFO]: | Prison | Prison - Finished loading. +``` + + + +# Prison Commands + +On the startup screen, prison shows all of the base commands that are active. From these commands, they will provide you with sub-listings of all the other commands. + +* **/prison** +* **/mines** +* **/ranks** +* **/rankup** + + +These commands are intended to run in game, but most can be ran from the system console. Sometimes the system console is easier to displays longer listings, such as **/mines list**. Also the console is better with wider text, and with easier to read text since it's not trying to display over a mc world. + + +
+ + +# Getting Started + +There is a lot to do get your server up and running. But here are some ideas on what to get started on first. It may even be a good idea to create a couple of small mines in an area that you have not spent much time with your final builds. Plan on creating a couple of test mines and ranks, then deleting them. You can quickly get a good understanding of how prison can be setup by playing around with a lot of the setting within a few minutes. + + +Remember that the command **/prison version** will show all the available root level commands by the modules. Entering those commands will show all of the related sub commands. + + +* Create a couple of small mines. About 10x10x5 blocks in size would work well. Above ground is easy too. +* Play around with adding blocks, removing them, searching for blocks to add. +* Try to resize the mines, and even deleting them. + +* Go ahead and create a couple of ranks. +* Setup the permissions (see the WorldGuard and LuckPerms document for examples) +* Add some simple permissions to the rank commands +* Test the ranks and the /rankup commands. + +* To clean up, you can remove the test ranks (or keep them) +* Remove the mines. + + +
+ diff --git a/docs/prison_docs_013_Prison_Help.md b/docs/prison_docs_013_Prison_Help.md new file mode 100644 index 000000000..caef5c56a --- /dev/null +++ b/docs/prison_docs_013_Prison_Help.md @@ -0,0 +1,187 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Getting Help + +This document provides some important information on how to find help in setting up your prison server, and ultimately, how and where to ask for help. + +
+ +# Overview + +If you are having problem, please take a quick look at the following documents as found in the Table of Contents: + +* Setting up prison and various plugins - If special conditions for their configurations become apparent in order for prison to work, notes will be added there. +* Review topics that may address your issue +* At the end of the table of contents are some FAQs. Special situations may be added to them. +* Known Issues & A TO DO List - This is also a TO DO list, but at the bottom are some known issues with prison. + +
+ + +# General Information on Prison Commands + +All of the commands for Prison can be ran in-game, and most can be ran from the console. When running commands from the console, you do not have to prefix the commands with a **/**. But within these documents, all commands will be referenced with a prefixed **/** so it is clear that it's a command. Just don't include it when running within the console. + +Personal preference is to run the commands from the console since there are less restrictions on width or number of lines shown, and they are easier to see without the busy background of the game. + +Within most of these documents, the console will be used to screen print from for those reasons. + +The only commands that cannot be ran from the console are the commands that expect you to be an in-game player. Examples are **/mines whereami** and **/mines tp **. + +If you need to do some maintenance, or configurating of your Prison, the console could be an easier environment to use. + +But in-game, some commands have clickable actions. Such as page next or page prior. Or even command completion, such as with **/mines block search** and clicking on a search result provides you with a filled in copy of **/mines block add** where all you need to do is just fill in the percentage. + + +
+ + +# Prison Commands and Using "help" + +It may be helpful to know what commands are available, and what options exist for those commands. + +Upon startup, prison lists many details about the environment, and one set of those details, are the base commands that are available within Prison. See the area pertaining to the modules, since the commands generally are tied to modules. + + +Prison Commands + + +Many commands within prison are compound commands, such that they start with a base command, followed by one or more other commands. When Prison lists the available commands, if there are sub commands, that information is included in the command listing, including the sub command count. For example: + +* **/mines** +* **/mines set** +* **/mines set spawn** + +If for a given set of commands, such as **/mines set**, has sub commands, then Prison will present a listing of all sub commands and their parameters. + +Prison Mines Commands + +In this screen print, you can see the hierarchy of **/mines** and **/mines set**. The parameter listing is helpful, but it does not provide all the information that is available. + +Let's take a closer look at **/mines set notification** and how the **help** keyword can provide a lot more meaningful details. If you just enter that command, without any parameters, Prison reports an error message, depending upon which parameter failed. See the next screen print and the related *Error* message. It's not helpful in the least, especially if you are not certain what each parameter is supposed to be. + + +Prison Mines Commands + +But notice within the above screen print, the same command has been entered, but this time with the keyword **help** added as if it were the first parameter. For example, **/mines set notification help**. Prison recognizes that you are requesting help for that command, and then it displays all of the information it has for each parameter. Finally, that is truly help. + + +Prison automatically inserts the **help** keyword if you enter the base commands. So **/mines** is really injecting the **help** keyword for you so it will generate the list. It's like you've entered **/mines help**. + + + +
+ + + +# Where to ask for help: The Prison Discord Server + +The best place to ask for help, and to get answers quickly (within a few hours) would be without a doubt the Prison Discord Server. + +[Prison Discord Server](https://discord.gg/DCJ3j6r) + + +You can also submit a help ticket on the Prison github Issues tab, but the response may be slower and less detailed + + +
+ + + +# Asking for Help + +Before you actually ask for help, take a look at some of the documents presented here. You may find your answer, or at least become a little more familiar with Prison. There is a good chance that if your question is a common one, then you will just be referred to this documentation anyway. + +When you do ask for help, please realize if you can provide a clear description of the problems you are experiencing, plus the versions of Prison, Spigot, etc, then we can help you faster and more accurately. To help provide you with answers to these questions, see the next section of this document for information on what you can copy and paste to provide all those much needed details. + + + +
+ + +# Prison v3.2.1 and it's Pre-Release Versions + + +The Prison startup screen contains a lot of information that can be used help identify issues that are detected by Prison on start up, and can provide useful information about the general environment. See the screen prints below. + +If you are having problems and need to ask for help, please provide all of this information when requested. Screen prints are preferred. When asked what versions of prison and platform that you are running, copying the following three lines is what is needed to answer that question: + +``` +[18:40:20 INFO]: | Prison | Loading Prison version: 3.2.1-alpha.12 +[18:40:20 INFO]: | Prison | Running on platform: SpigotPlatform +[18:40:20 INFO]: | Prison | Minecraft version: git-Spigot-21fe707-e1ebe52 (MC: 1.8.8) +``` + +Prison Startup Screen +Prison Startup Screen + + +These screen prints may not contain the most recent enhancements to prison, since they are based upon an alpha release. Please see your server's console for details pertaining to your environment. + + +
+ + + +# Prison v3.1.0 and Earlier - General Information + +There is zero support available for these earlier versions of Prison. It is therefore highly recommended that you upgrade to the latest release of Prison. Once a version of prison is released, we cannot go back and apply any fixes; fixes will be applied to the next release. + + +My suggestions are based upon what I have seen within the code for Prison, and best guesses on how the older versions **may** have worked. These are guesses. Proceed carefully and make backups at each upgrade to ensure you can retry a step if something should go wrong. - Blue + + +To upgrade prison, the process should be rather simple. But there are general steps that you must follow to help ensure a smoother transition to the latest release. + + +It is highly suggested that you should first backup your whole server, especially the data within your plugins folder. Please be aware that upgrading Prison may require you to upgrade other pugins that you have, and those plugins may require other plugins to be upgraded. + + +DO NOT just install the latest plugin(s) you find on the internet! Most plugins have specific versions of Spigot/Bukkit/Minecraft that they will work with! Get them only from trusted servers such as spigotmc.org, bukkit.org, or etc... Those sites should have multiple versions and should identify what server engines they are compatible with. + + +If you want to also upgrade to a newer version of the server software that you are running, such as spigot, bukkit, paper, etc..., first upgrade the plugins for your current version of the server. So if you are running Spigot 1.8.8 and a plugin has a newer version available that works with 1.8.8, install that first and then start the server and make sure everything is working well. Usually if there is a major change in a plugin from one version to the next, the "last" version may have code to "convert" your data to prepare for the next higher version. If there is an important intermediate step that will convert your data, or require you to make major changes, there should be some notes in documents somewhere. Spending a few minutes reading the release notes on these websites could save you hours of work trying to recover from a messed up upgrade. Remember to make backups! + + +

Upgrading from Prison v3.2.0 to Prison v3.2.1

+ +If you are running Prison v3.2.0, then upgrading to v3.2.1, or it's pre-release editions, you don't have to take any special precautions other than backing up your server and the plugin's data folders. + +The internal files remain the same between these two versions. Version 3.2.1 has a lot of new additions to the internal file formats, but nothing will break if using v3.2.0 data with v3.2.1. Matter of fact, if for some reason, you want to down grade from v3.2.1 to v3.2.0 you can. Any new data elements from v3.2.1 will be lost if any of the data items are rewriting to the file system. If a mine is not modified, as an example, then it will not write the mine data back to the file system. + + +

Upgrading from Prison v3.1.1 to Prison v3.2.1 or Prison v3.2.0

+ +Upgrading from Prison v3.1.1 to either Prison v3.2.0 or Prison v3.2.1 requires no special procedures or processes. The internal file formats are pretty close to being the same that it should work with no problems. Just make sure you perform a backup of the server and the plugins data folders to provide that extra level of insurance and protection. + + +It is inadvisable to try to down grade to Prison v3.1.1 if running Prison v3.2.0 or later. It may work, but there could possibly be potential internal failures due to the addition of the new fields. If you try to do this, you assume all risks and no help will be provided. + + +

Upgrading from v3.0.0 to Newer Versions of Prison

+ +Honestly I cannot advise you on actual steps to take, since I do not know what file formats were used with v3.0.0. I suspect they are compatible 100% with v3.1.1. If this is true, you can just jump to Prison v3.2.1 with no problems. + +If there is an incompatibility, then its strongly suggested that you upgrade to v3.1.1 first and let it perform its own conversions, then shut down the server carefully. If it is not shutdown cleanly, then the data files will not be saved to the file system. Once you update to v3.1.1 then you can update to v3.2.1 without any issues. + + + + +

Upgrading from Earlier Versions Prior to v3.0.0

+ +For prison releases prior to Prison v3.0.0, the data structures they uses to store all the data on the file system was different. I do not know how it was different, I just know it was. I saw there were remains of a conversion utility in Prison v3.0.0 that I **think** was able to convert Prison v2.x data to Prison v3.x formats. If you are upgrading from Prison v2.x it is VERY IMPORTANT that you first upgrade to Prison v3.0.0!! Once up start up that server, use the command **/prison** and confirm there is a command **/prison convert**, and if there is, run that command to convert your old data to the new Prison v3.0.0 format. Make sure you take backups BEFORE and AFTER upgrading your data! Also do a "clean" shutdown on prison v3.0.0 to finalize the changes. + + +**Please NOTE:** It needs to be understood that changes made to mines, ranks, ladders, or player data, in versions of Prison prior to v3.2.0 did NOT save those changes until the server shutdown. So if the server should happen to crash, the changes could have been lost. So this is important to understand, because if you are upgrading from an older version of prison to Prison v3.0.0, a version prior to Prison v3.2.0, then you MUST ensure the server shuts down cleanly or the conversions and/or changes to the prison data may not have been written to the server file system. After you shut down the server, if performing an upgrade to v3.0.0, please review all the files under the plugins/Prison directory and make sure the last modify date reflects when you shut down the server. If the files have not been updated, try restarting the server, and maybe make a change to mine or rank to force a save when the server shuts down. I cannot help with this process other than these suggestions. Sorry. + + +Once you are running Prison v3.x.x then you can safely upgrade to Prison v3.2.0, or better yet, to Prison v3.2.1 (or the pre-release edition). + + + + +
+ + diff --git a/docs/prison_docs_014_Prison_Configs.md b/docs/prison_docs_014_Prison_Configs.md new file mode 100644 index 000000000..c27bc78e0 --- /dev/null +++ b/docs/prison_docs_014_Prison_Configs.md @@ -0,0 +1,27 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Prison Configuration Files + +This document provides some important information on the various Prison configuration files. + + +Some of the config files requires manual changes to the file itself, and others are fully managed within the server. All of these files are listed below along with how to change each one and what the various configs do. + + +
+ + +# Overview + + +Coming soon. This is a work in progress. + +
+ + + + +
+ diff --git a/docs/prison_docs_020_setting_up_luckperms.md b/docs/prison_docs_020_setting_up_luckperms.md new file mode 100644 index 000000000..e00ce9331 --- /dev/null +++ b/docs/prison_docs_020_setting_up_luckperms.md @@ -0,0 +1,55 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up LuckPerms + +This document provides a quick overview on how to install LuckPerms. + +
+ + + +# Dependencies + +* **Vault** - **Required** - Provides the permission integration with Prison + +
+ + + +# Setting up LuckPerms + +Setting up LuckPerms is simple: + +* Download LuckPerms + - Go to the SpigotMC.org LuckPerm's resource page: + - [LuckPerms Downloads](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-plugin.28140/history "LuckPerms download can be found under the Version History tab") + - Click on the Version History tab + - Choose the version to download + +* Copy to your server's plugin directory + +* Restart the server + +* Follow LuckPerm's documentation on customization, but at this point it's ready for use with the Prison plugin. + + +
+ + + + +# Warning about LuckPerms Versions + +Due to how LuckPerms registers with the Spigot Plugin Manager, it will cause plugins to fail if they were designed for the older LuckPerms version. Therefore, plugins can only be used with the version of LuckPerms that they were designed for and it's up to the server owner to be able to figure that out on their own. + +**Prison plugin v3.2.0** *only* works with LuckPerms v4.4.1 and older. Luckperms v4.4.1 is the suggested version and can be downloaded here: +[LuckPerms v4.4.1](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-plugin.28140/history "LuckPerms v4.4.1 can be found under the Version History tab") + +**Prison plugin v3.2.1** and newer works with *any* version of LuckPerms. It is suggested that LuckPerms v5.0.x is used, but you may also use LuckPerms v4.4.1. + + + +
+ diff --git a/docs/prison_docs_022_setting_up_PermissionsEX.md b/docs/prison_docs_022_setting_up_PermissionsEX.md new file mode 100644 index 000000000..f113ceaa5 --- /dev/null +++ b/docs/prison_docs_022_setting_up_PermissionsEX.md @@ -0,0 +1,46 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up PermissionsEX + +This document provides a quick overview on how to install PermissionsEX. + +
+ + + +# Dependencies + +* **Vault** - **Required** - Provides the permission integration with Prison + +
+ + + +# Setting up PermissionsEX + +Setting up PermissionsEX is simple: + +* Download PermissionsEX + - Search for a downloadable copy. + - The bukkit.org resource page has been disable and suggests using LuckPerms instead. + +* Copy to your server's plugin directory + +* Restart the server + +* Follow PermissionsEX's documentation on customization, but at this point it's ready for use with the Prison plugin. + + +
+ + + +# Obsolete Warning for PEX + +Since PEX has been abandoned and has no support, it is strongly suggested that you use LuckPerms or another permission manager instead. If you do decide to use PEX then you do so at your own risk. We reserve the right not to spend time addressing issues that may be resolve with a more modern permission manager. + + + +
diff --git a/docs/prison_docs_024_setting_up_Ultra_Permissions.md b/docs/prison_docs_024_setting_up_Ultra_Permissions.md new file mode 100644 index 000000000..698046691 --- /dev/null +++ b/docs/prison_docs_024_setting_up_Ultra_Permissions.md @@ -0,0 +1,52 @@ +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up Ultra Permissions + +This document provides a quick overview of how to install Ultra Permissions. Please keep in mind that this is a paid plugin, and is in no way associated with the Development team of Prisons. + +
+ + + +# Dependencies + +* **Vault** - **Required** - Provides the permission integration with Prison + +
+ + + +# Setting up Ultra Permissions + +To set-up Ultra Permissions, please follow the instructions listed below: + +* Download Ultra Permissions: + - Go to the SpigotMC.org Ultra Permissions resource page: + - [Ultra Permissions Download](https://www.spigotmc.org/resources/ultra-permissions.42678/) + - All versions appear to be compatible with Prison v3.2.x, so download the latest release. + + +* Copy to your server's plugin directory. + + +* Restart your server. Do not use **/reload** (see below). + + +* The plugin is ready to use as-is, but if you want to customize the settings, please follow the Ultra Permissions documentation that is available on their [wiki page](https://ultrapermissions.com/wiki/). + + +
+ + + +# Special Notices + +* Do not use **/reload** with this plugin since it may not correctly grab all the required permission nodes. + + +* You are required to add yourself as a super user by running the following command `upc addsuperadmin [player]` if you wish to use the in-game GUI or run any Ultra Permissions related commands. + + +
+ diff --git a/docs/prison_docs_026_setting_up_worldguard_worldedit.md b/docs/prison_docs_026_setting_up_worldguard_worldedit.md new file mode 100644 index 000000000..23c0694b2 --- /dev/null +++ b/docs/prison_docs_026_setting_up_worldguard_worldedit.md @@ -0,0 +1,45 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up WorldGuard and WorldEdit + +Although Prison does not directly utilize WorldGuard or WorldEdit, both of these plugins can be critical in setting up a functional Prison server. + +WorldGuard requires WorldEdit to work. +WorldGuard can be used to restrict players from entering and using mines that they should not have access to. + + +This short document just explains how to install these plugins. Other documents will go in to greater detail on how to setup and use WorldGuard regions to protect your mines and to limit players from going in to mines they do not have access to. + +Of course you don't need to use WorldGuard and WorldEdit, these are just presenting one possibility for your sever. Other options exist. + + +
+ + +# Installing the Plugins + + +* Download WorldEdit + - Go to the bukkit.org WorldEdit's resource page: + - [WorldEdit plugin Download](https://dev.bukkit.org/projects/worldedit/files "WorldEdit downloads can be found under the File tab") + - Click on the File tab + - Choose the version to download that matches the minecraft version of your server + +* Download WorldGuard + - Go to the bukkit.org WorldGuard's resource page: + - [WorldGuard Plugin Download](https://dev.bukkit.org/projects/worldguard/files "WorldGuard downloads can be found under the File tab") + - Click on the File tab + - Choose the version to download that matches the minecraft version of your server + +* Copy to your server's plugin directory + +* Restart the server + +* Follow WorldGuard documentation on customization, but at this point it's ready for use with the Prison plugin. + + + +
+ diff --git a/docs/prison_docs_0xx_setting_up_EssentialsX.md b/docs/prison_docs_0xx_setting_up_EssentialsX.md new file mode 100644 index 000000000..18f8849ce --- /dev/null +++ b/docs/prison_docs_0xx_setting_up_EssentialsX.md @@ -0,0 +1,108 @@ +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up EssentialsX + +This document provides a quick overview on how to install EssentialsX and add the Prison Rank Tag to your chat. + +
+ + + +# Optional Dependencies + +It is highly recommended to install [Vault](https://www.spigotmc.org/resources/vault.34315/). Vault will allow many different economies, permissions, and placeholders to works with Prison. If no other economy is detected, then Prison will default to using EssentialsX as a backup. + +
+ + + +# Setting up EssentialsX + +Setting up Essentials is easy, but you've to choose which plugins to add. + +* Download the EssentialsX's Zip, which contains various jars plugins. +* Download from the SpigotMC.org EssentialsX **Version History** tab for the EssentialsX Resource: + - [EssentialsX Download page](https://www.spigotmc.org/resources/essentialsx.9089/) +* Unzip the download. +* Copy the jar files of your choice to your server's `plugin/` directory. +* The zip file contains multiple jars of the same version. Always use the same versions with each other, but never mix one EssentialsX jar version with another version. +* Available Modules and what is recommended: + * EssentialsX - Main jar - **Required** + * EssentialsXAntiBuild - *Optional* - Don't use if using WorldGuard? + * EssentialsXChat - **Recommended** - Provides chat based placeholders (see below) + * EssentialsXGeoIP - *Optional* - Provide geo locations for players IP addresses + * EssentialsXProtect - *Optional* - Not sure, but shouldn't be used with WorldGuard? + * EssentialsXSpawn - *Optional* - Not sure, but provides special behaviors for spawn? Not sure. + * EssentialsXXMPP - *Don't Use* - Google XXMPP for spigot to confirm if you really have a need to use this. You probably never will. + +* Prison does not require you to change any settings in EssentialsX, but you may if you want to customize some things. +* List of possible items to modify: + * Chat prefixes (see below) + * Item prices if you will be using the shop functions through EssentialsX. If you do, I suggest you remove the entries for armor, weapons, tools, food, etc. Otherwise players may accidentally sell things they need. Also you can add new items to be sold. + * Customize kits + * etc... +* Restart your server + +
+ + + +## Add the Prison placeholder *Rank Tag* to Player's Chat Prefix + +* EssentialsX's plugin folder on the server +* Path: `plugins\Essentials\` +* Edit the `config.yml` file: + - Search for the section pertaining to chat by using the keyword: **EssentialsChat** + - Add the placeholders {prison_rank_tag} or {prison_rank} to the "format:" line. + * The placeholders should be entered in all lower case + * The curly braces is used by EssentialsX chat engine, not Prison. Other chat plugins may use percent symbols instead. + * {prison_rank} is the rank name without special formatting + * {prison_rank_tag} is the defined tag (with formatting) for that rank + - Example: + ``` + format: '<{prison_rank_tag}:{DISPLAYNAME}>{MESSAGE}' + ``` + +Reload EssentialsX settings, or restart the server. + +
+ + + +# Applying Settings and Reloading Essentials + +* Restart the server. Or use **/essentials reload**. + + +* Notice: **Don't use /reload to reload the whole server!** Using /reload or another tool such as plugman could corrupt plugins. Prison was not designed to do soft reloads and as such mines, ranks, ladders, and even some player settings and histories could be corrupted. + + +EssentialsX + + +
+ + + + +# Essentials Currency Commands + + +Eventually you may have to deal with currency, so here are the esstentals commands. + +* `/eco` - Show the EssentialsX economy commands +* `/eco give ` - Give a player money +* `/eco take ` - Take away money from a player +* `/eco set ` - Set a player's balance to an amount. Resets the balance to a given amount. +* `/bal ` - Shows the balance for a given player. + + +EssentialsX + + + +**End of the guide** + + +
diff --git a/docs/prison_docs_0xx_setting_up_PlaceholderAPI.md b/docs/prison_docs_0xx_setting_up_PlaceholderAPI.md new file mode 100644 index 000000000..40222f51d --- /dev/null +++ b/docs/prison_docs_0xx_setting_up_PlaceholderAPI.md @@ -0,0 +1,84 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up PlaceholderAPI + +This document provides a quick overview on how to install PlaceholderAPI. + +Additional documentation on placeholders: + +* [Guide: Prison Placeholders](prison_docs_310_guide_placeholders.md) + How to use placeholders. Includes HolographicDisplays. + +* [Setting up Mines](prison_docs_101_setting_up_mines.md) + This has a placeholder example using a One-block mine example with HolographicDisplays. + + +
+ + + +# Optional Dependencies + +* If you want to use placeholders with plugins without placeholderAPI support you should also install the right version of [Protocollib](https://www.spigotmc.org/resources/protocollib.1997/) for you server and the [PlaceholderAPI ChatInjector](https://www.spigotmc.org/resources/chatinjector.38327/) + + +
+ + + +# Setting up PlaceholderAPI + +Setting up PlaceholderAPI just need couple of steps: + +* Download PlaceholderAPI + - Go to the SpigotMC.org PlaceholderAPI download page + - [PlaceholderAPI Download page](https://www.spigotmc.org/resources/placeholderapi.6245/) + +* Copy to your server's plugin directory + +* Restart your server. Do not use **/reload** or you'll break Prison Placeholders. Prison registers all of the placeholders upon server startup so the registrations will be lost. + +
+ + + +# Placeholders Commands + +Use the command **/prison placeholders** for a listing the placeholder commands. These include: + +* **/prison placeholders list** +* **/prison placeholders search ** +* **/prison placeholders test ** + +* **/prison version** + +The listing of all the placeholders is available within the Prison startup information, and through `/prison version`. + +
+ + +# Troubleshooting Possible Compatibility Issues + +There are no known issues with Prison and PlaceholderAPI, but out of the interest to help get your servers up and running, here are a couple of tips that could help with your environment. + + +

Issue with PlaceholderAPI not working with Economy and Scoreboard

+ +There was an issue with an economy placeholder not working with the QuickBoard placeholder. + +The solution was to download from PlaceholderAPI, their modified version of Vault and Essentials. Followed by a server restart, or use `/papi reload`. + +``` +/papi ecloud download Vault +/papi ecloud download Essentials +/papi reload +``` + +As a note, I'm not sure how well Prison behaves with the use of `/papi reload`. It may be good to test with, but safer to just restart the server, once everything appears to be working and looks good. The point is that any plugin reload is good to test with, but if you're going to let your server run for months, its probably best to do a clean restart. + +With this example of installing the PlaceholderAPI and downloading their version of Vault and Essentials, the player reported that once the economy placeholder was working, another placeholder stopped working. Their solution was to reinstall all of the papi (PlaceholderAPI) plugins (not sure if that includes reinstalling papi's version of Vault and Essentials) and then restarted the server. The point here, is a clean start is probably helpful. + + +
diff --git a/docs/prison_docs_101_setting_up_mines.md b/docs/prison_docs_101_setting_up_mines.md new file mode 100644 index 000000000..acd380626 --- /dev/null +++ b/docs/prison_docs_101_setting_up_mines.md @@ -0,0 +1,476 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Setting Up Mines + +This document provides some highlights to how to setup mines. It is a work in progress so check back for more information. Initially it will cover the basics, but hopefully will expand to more advanced topics. + +
+ +# Overview + +This document should be able to provide the basic information to get your mines configured. There are many options available within Prison, and also through the use of other plugins, and these advanced topics are beyond the scope of this document. + + +Items to add to this document: +* Use of **/mines set area** to change the mine size without deleting it. +* Use of **/mines delete** how it works and how to recover a deleted mine. +* Use of **/mines list** +* Use of **/mines reset** provide a little information about how it works in relationship to the other settings and commands. There are some internal things that happen and this will help clarify how the other settings are impacted. + +
+ + +# Getting Started + +Please review the Prison's [Table of Contents](prison_docs_000_toc.md) for information on how to configure your server, and which plugins are required, or at least suggested. + +It is also strongly suggested that you review the documentation on [Configuring and Using WorldGuard with LuckPerms to Protect Mines](prison_docs_626_configuring_worldguard_regions.md) since that explains a lot on how to setup the basics with your world, which will not be covered here. There will be references to this document, but this will be the only link provided. + +These instructions assume you are OP'd since that is part of the above document pertaining to WorldGuard. If not, go ahead and OP yourself. + +To follow along with this documentation, you may want to crate your first mine as a test, with intentions of deleting it later. It may be easier to remove it, than to convert it over to a final product. The instructions here are informational, not focused on perfection. So after you figure out how to create your mines, you may want to provide more attention to the details to ensure they are perfect. But for now, the focus is on the commands. + +Please note that all command provide in this document will be written as if they were entered within the game. If you use some of them from the console, then do not include the slash prefix. I always include the slash prefix in the documentation so the commands stand out as being commands. + +It should also be strongly noted that you should never modify the save files for the mines, ranks, or ladders, since that could easily lead to corruption and undefined failures and problems. We cannot support misuse of the files. + +
+ + +# Create Your First Mine + +Being OP'd, find a good location for your mines. + +Also the following commands may help setup the environment. Optional of course. Having some block in your inventory can be useful for use as scaffolding or selecting. I personally like to use sealanterns because they are not a common block and they stand out well in low light conditions and are easier to see in screen prints. Giving yourself `/fly` and `/god` may not be necessary, but it could help if you need to find a good location, or if there are mobs in the area when you drop out of creative mode and back in to survival. + +``` +/op +/fly +/god +/gamemode creative +/time day +/give sealantern 64 + +``` + +

The wand

+ +Now give yourself a Prison wand. + +``` +/mines wand +``` + + +

Laying Out the New Mine

+ +For the sake of this document, and to keep things simple, the mines we are creating will be very small and will not be deep within the ground. + + +First mark out the dimensions of your mine. As you can see here, I've marked my first mine out with sealanterns to show you how big it will be. You really only need to mark opposing corners since that defines a cuboidal region. I've cleared away some of the dirt so you can see the lower blocks. This mine will be 5 x 5 x 5 in size. + +Laying out the new mine + + + +

Define the New Mine

+ +Then holding the Prison's wand (blaze rod), left-click in one corner, and then right-click in the opposite corner. In this example, I've left-clicked the lowest sealantern block, then right-clicked the highest sealantern block. You should see a message indicating that you have successfully selected a region. + +Selected the mine with the wand + + +Then create the mine with: +``` +/mines create test1 +``` + +It will default to 100% air. So then follow up creating the new mine with a reset to confirm everything is replaced with air. + + + +

Reset the New Mine to Test it Exists

+ +Go ahead and reset the mine. All of the sealanterns that were used to mark the mine are now gone, and so are the two layers of dirt. That confirms success with our first mine since the only default block that exists in the mines when created is air. + +``` +/mines reset test1 +``` + +The first reset with all air + + + +
+ + + +# Mine Commands Overview + +Now that we've created a mine, let's take a moment and review some of the available commands. There are a lot of options available, and many of those options have numerous features, so this will just be an overview so you have a better idea of what you could possibly do with your new mines. This listing is generated within the console so it's easier to see. This includes the main grouping of commands, which is displayed with `/mines`. Remember, when using commands within the console drop the leading slash. Then two sub-groups: `/mines block` and also `/mines set`. + + +For the following commands, we can use the console, which is also easier to see for this documentation. But these commands can just as easily be ran within game. + + + +The Prison Commands + + +Some of the highlights of these commands are as follows: +* `/mines create` : Create the mine based upon the wand selection. +* `/mines command` : **Now active!** **Take your mines to the next level!** See the document on [Mine Commands](prison_docs_111_mine_commands.md) for more information. Every time a mine resets, you can now control what commands run right before a reset, and what runs right after a reset. These commands are similar to the Rank Commands, since they can be any command that you can run from the console. The idea is that you can have unique mines that have not been possible before, such as randomly spawned forests or specific builds. +* `/mines delete` : Deletes a mine. You can always undelete a mine by going in to the server file system and rename the deleted mine, then restart the server. +* `/mines info` : Very useful in viewing all information related to the mine. +* `/mines list` : Displays all mines on your server. +* `/mines reset` : Resets the mine. Forces a regeneration of all the blocks, even if the mine is in skip reset mode. +* `/mines stats` : Toggles the display of stats that pertain to how long it takes to reset the mines. View /mines info or /mines list to see the stats. Use this command again to turn it off. +* `/mines tp` : tp to a mine's spawn point, or the center of the mine if no spawn has been set. If players are given the permissions, they too can use this command. Use `/mines tp help` for a list of the perms. +* `/mines whereami` : Shows you what mine you are in, or how far away you are from other mines in the area. If you have a lot of mines, it's easy to lose track of where you are, and this may help get your bearings. + + +* `/mines block add` : Add a new block type to a mine. It's easier to start off with block search and have it fill in the commands for you. +* `/mines block remove` : Remove a block type from a mine. +* `/mines block search` : Search for a block type based up on a search string. +* `/mines block set` : Edit the block's percentage within the mine. A percent of zero will remove. If the block does not already exist, it will be added. (Can replace add and remove). + + +* `/mines set area` : Redefine the area of the mine. Careful, this can wipe out builds if set incorrectly. +* `/mines set notification` : Can turn off the notifications on a per-mine basis. Or set the notification radius, or only notify players within the mine. This command cannot change the message. +* `/mines set resetTime` : Changes the time between resets, as expressed in seconds. Applies to each mine independently. +* `/mines set skipReset` : When enabled, can prevent a mine from resetting if it has no activity. Can set a threshold before the mine is reset, such as 80% will require 20% of the blocks be mined before being reset. Can also set a bypassLimit so that if the reset is skipped X number of times, it will force a reset anyway. +* `/mines set spawn` : Sets the mines spawn point. Whatever you are looking at is what the players will be looking at when they tp to that spot. +* `/mines set zeroBlockResetDelay` : If the mine runs out of blocks, when enabled, it will force a manual reset after the specified delay. The delay can be zero for instant reset. + +Adding the term `help` to the end of any of the above listed commands will display additional information that is available for each command, including the parameters and also all permissions that are associated with the commands. + +
+ + +# Customizing Your Mines + +Now that we have a mine created, let's go ahead and customize it. + +

Inspect our New Mine

+ +Let's first take a quick look at the new mine with this command. Let's view it now, before we start making changes. + +``` +/mines info test1 +``` + + +Info on test1 mine + + + + +

Select a Few Blocks

+ +The easiest way to select blocks is to search for them. Let's add a few such as cobble, glowstone, gold ore, and coal ore. Use the block search with cobble: + +``` +/mines block search cobble +``` + +Block search for cobble + +When using the console with block search limits what you can do with the block search. If you are in game, you can click on a block type that is displayed, and it will auto fill out the block add command for you, such that all you need to do is add the percentage at then end of the command. Also in game, you can page through the results. + +This is an example of in game use of this command. Notice by clicking on cobble it pre-fills all the parameters for the `/mines block add`, including the most recently used mine name and the selected block. This screen print shows that I added the value of `20` right after the percent sign. + +Block search for cobble + +Then let's take a look at the `/mines info test1 all` command, where all indicates all pages should be displayed in-line without paging. Notice how Prison automatically provides the balance of air so the total percent is equal to 100.0%. + + +Mine info with cobble + +Now if we perform a `/mines reset test1` then we can see how the cobble is randomly placed at a rate of 20%, and the remainder is air. + +Mine reset with cobble + + +Let's add the other blocks, each at 20% then take a look at the mine info and how it generates. Please notice that you can add a single block with as little as 0.01%. You can also add as many different blocks as you please too. There are no meaningful restrictions. + + +With the additions of the other ores, including 1/2 a percent of sealanterns: + +Mine test1 listing + + +Mine test1 listing + + +Example of a mine reset including a sealantern. Notice that there is still 19.5% air. + +Mine reset with all new blocks + + + + +
+ + + +# Next Steps - Skipping Resets, Notifications, and Zero Blocks + + +The mine is now usable as-is, but there are still a lot of options that we can add to enhance the mine's behavior to better suit our needs. For example, we can set a spawn point so we can control where players are teleported to when the mine resets, or if they tp to the mine. + +For our example, let's use the console to set the notification, resetTime, skipRest, and the zeroBlockResetDelay. We must be in game to set the spawn point since it uses where your player is standing and where you are looking. + + + +

Customizing the Mine

+ +Issuing all these commands through the console, such as the following. Note that the spawn point has been set in game, and the reset time has not be changed. + +Mine customizing within console + +Mine info showing all changes. Observe the spawn location now shows an actual location, but yet it does not show the pitch or yaw. The information also shows that the mine has reset 30 times since there server started, and that you must be standing within the mine to receive reset notifications. + +Mine info with all details + + + +

Notifications Explained

+ +In the example above, the notifications for that mine was set to only provide them to the players that are within the mine. The other options are disabled and radius from the center of the mine. + +``` +/mines set notification test1 within +``` + + + +

Skip Reset Explained

+ +To better explain what the skipReset does, is it uses the threshold percent as the trigger to allow the mine to auto reset. Once the **blocks remaining** value hits that value, or is less than that value, it will alloow the mine to rest at it's normal reset time. If the blocks remaining is higher than the threshold, then it skips the reset and increments the skip counter (not shown in this screen print since it is zero). Once the number of skips reaches, or exceeds, the Skip Limits value, then it will allow the mine to reset. + +This behavior allows unused, or barely used, mines to avoid from always being reset. This can reduce the server load and prevent lag on larger servers, or if there are many mines. + +This feature never will force a mine to reset early, nor will it ever make it reset. The only active behavior it can alter, is to skip the automatic reset behavior. + +If a mine is manually reset, then this feature will never prevent a manual reset from happening. A manual reset will also reset the counter for how many times the mine skipped the automatic resets. + + +

Zero Blocks Reset Delay Explained

+ +The feature zero blocks reset delay identifies what should happen when Prison detects that there are no more blocks within the mine. The only action actually is to force an early reset if this feature is enabled, but more specifically, it identifies how many seconds it should delay before forcing the reset. + +If this feature is enabled, and the delay value is set to Zero, then the mine is forced to reset with no delay. If there is a light load of jobs running on the server, then it will be instantly reset, otherwise there will be a delay for the job to wait its turn to run. + +If the reset delay is non-zero, the value is measured in seconds, with a valid value of 0.01 seconds. But keep in mind that in reality the seconds are converted to ticks and is scheduled within the Bukkit job queue. Therefore, since there are 1000 milliseonds within one second, and 20 ticks within one second, one tick is the smaller possible value, which equates to 50 milliseconds. This is equivalent to 0.05 seconds. When the seconds are converted to ticks, the values will be rounded down to a whole integer value. So if a value is provide such as 0.049 it will be submitted as 0 ticks, which will result in zero delay (if possible) before running. + +The bottom line is that this feature can force an earlier reset of the mine when it becomes totally empty of blocks. A delay may be needed, or desired, to reach your perfection for the mine. + + +
+ + +# Notifications - Config File & Settings + + +The global `resetMessages`, `resetTime` and `resetWarningTimes`, as found in the configuration file `plugins/Prison/module_conf/mines/config.json` needs a little explanation in what they do. + +The `resetMessages` is used only upon the server startup, and if it is disabled, then it will not use the `resetWarningTimes` and will skip all notifications pertaining to mine resets. The `resetWarningTimes` are global and cannot be customized per mine. If an individual mine's reset time is less than a warning time, then the reset warning will be skipped for that mine. + +The `resetTime` in this configuration file is only used during the creation of a mine and is used to set the initial value for the mine's reset time. If the configuration file's value is changed, it will never effect any preexisting mines, but only ones created after it has been changed. + +All mines use their internal reset time and can be customized on a per mine basis using the following command. The command's time value is expressed in seconds. + +``` +/mines set resetTime +``` + +There are a number of options available for mine notifications, and they are on a per mine basis to allow better control over how to configure everything. The command is as follows. The value for **mode** can be **disabled**, **within**, or **radius**. When radius mode is specified then the radius value must be supplied, and it is expressed as whole blocks (integer). + +``` +/prison set notification +``` + +The **disabled** option is pretty much self explanatory; messaging for that mine is disabled. + +The **within** the mine notification only provides notifications to the players that are within the boundaries of the mine, including players standing upon the top of the mine. One of the behaviors to this mode, is that all players within the mine will be notified that the mine will be reset, but once they are teleported out of the mine to the spawn point they cannot be notified that the mine has been reset (because they are no longer within the mine). But then again, the fact they were teleported to the spawn point should be a big clue the mine reset. + +The **radius** setting notifies players that are within the specified distance from the center of the mine, ignoring the Y axis. So if the distance is set to 30 blocks, and they are within that radius, but 200 blocks above the mine, they will be notified. Since the center point of the mine is calculated to be the, um, center, it is possible to make the radius smaller than some points of the mine and still be outside of the mine in other places. So this can provide some nice controls at far distances away from the mine, but could produce some odd behaviors (if you don't understand how it works) if the radius is much smaller in size. + + +Perhaps the last point pertaining to resets has to do with the actual timers. Once a mine is reset, either by automatic resets, zero block resets, or a manual reset, the timer will always reset and start over. This means if there is 30 seconds left on the timer and you do a manual reset, the automatic reset will not happen 30 seconds later. + + +
+ + +# Listing All Mines + + +As you start to build your mines, you may find it useful to list them all, or use the listing to search for a specific mine. + + +Here are some basic usages of the command. First let's review the help for the `/mines list` command to see what perms are needed and what parameters are available. If you are in game, clicking on `TP` will teleport you to the mine. Clicking on the name will show you the info on the mine, which is the same as using the `/mines info` command. Of course, clicking on these items only works in game, and not from the console. + + +``` +/mines list help + +/mines list + +/mines list 4 +``` + + +As we can see in the screen print of the help, the command takes optional parameters for the page number. It also has the option of `ALL` which will display the list of all mines, which could be helpful in the console. + + +Listing Mines + + + + +There is some useful information in the listing too. This listing shows how soon a mine will **Reset**, and what the reset frequency is. It will show you how many times it has been reset since the server started (**RCnt**). It even shows the percent blocks remaining too (**Rem**). If you've added any **mine commands**, those will indicated in a red **Cmds** entry; see the mine named **trees**. Also if a mine is disabled, it will be listed here too. + +Listing Mines + + +This listing also shows more details if you have `/mines stats` enabled. Remember that mine stats is a toggled setting. Run it once to enable it. Run it a second time to disable it. + + +With it enabled, it shows stats on mine resets in mine list, mine info, and when mines reset in general. The following screen print within the console shows how these may look, with the addition of the stats. + + +Listing Mines + + +. + + +
+ + +# Deleting Mines + +Mines can be deleted with the `/mines delete` command. + +It will prompt you for confirmation. If you are in game you can click on the link then it will fill out the command for you with the parameter `cancel` and you need to rename it to `confirm`. You have one minute to confirm a delete. + +When you successfully delete a mine, it will remove it from memory and from loading. The mine is only virtually deleted, since it will still exist in the server's directory. To undelete it, just rename the file back to a normal mine file name, and it will be loaded the next time the server starts. + + +
+ + +# The Mine Data Files + +The mine data files (and also the ranks and ladders) are stored on the server's file system as a way to store each mine's configurations. These files are intended for internal use only and should never be manually modified. When undeleting mines, you may have to rename the files so they are used the next time the server is started, but you should not change the contents. + +There are many commands that have interdependancies to multiple settings, and those settings are validated only through the use of the commands. Making changes directly to the files bypasses the validation of the commands and may lead to server corruption and failures. + +You are solely responsible for any problems you create by making changes directly to these files. We cannot help undo any problems, issues, failures, or undefined behaviors that may be caused with such tampering. + + +
+ + +# Advanced Topics + + + +### Changing the Size of a Mine + + +
+ + +# Example: A Single Block Mine Grinder + + +To provide a slightly different perspective as to what you can do with your Prison mines, let's build a one block mine and demonstrate the power of what we can down with a few of these settings. We're also going to tie this in to HolographicDisplays and use of the placeholders. + +So let's first setup a temporary placeholder for our new mine. I want it to be a one block mine, one block off the ground. So stack two blocks like this. Added a platform, because why not? + + +One block mine setup + + +One block mine setup + +Next, using the Prison wand, right-click and left-click on the top block. This will create a one block selection. Then create the mine, naming it **one**. + +``` +/mines create one +/mines block search ore +``` + +Then select the following ores and add them to the new mine, all at 10%. Add gold_ore, iron_ore, coal_ore, Lapis ore, redstone_ore, emerald_ore, diamond ore, etc... Then setup the reset time, turn off notifications, and set zero blocks reset delay to zero. + +One block settings + + +Now let's test our new mine. But first let's make an OP pick. Open the config file `/plugins/Essentials/config.yml` and search for `unsafe-enchantments` and set the value to `true` and then save and exit the file. You can either restart the server, or use `/essentials reload` to apply those changes. Then issue the commands in game, holding the pick after you get it. + +``` +/essentials reload +/give diamondpick 1 +/enchant efficiency 20 +/enchant durability 20 +/enchant fortune 5 +``` + +Remove the block under the one block mine, then reset it with `/mines reset one`. + +Install HolographicDisplays as described in our documentation (see table of contents... must include HolographicExtensions and ProtocolLib). Next we want to create two holographic displays for our one block mine, one above the block, and one below. Do the `/mines info one` to find the *Center* of the mine, and let's use that to modify the HolographicDisplay scripts below. In this example, our coordinates are `(-81, 70, -320)`. Add these to the file `/plugins/HolographicDisplays/database.yml`... + +``` +oneTop: + location: world, -80.5, 72.5, -319.5 + lines: + - 'Mine: one' + - 'Reset Interval: {slowest}%prison_mines_interval_one%' + - 'Reset Time Left: {fast}%prison_mines_timeleft_one% - {fast}%prison_mtlf_one%' + - 'Blocks Remaining: {slowest}%prison_mr_one% {slowest}%prison_mp_one%%' +oneBottom: + location: world, -80.5, 69.8, -319.5 + lines: + - 'Blocks Mined: {fast}%prison_mbm_one%' + - 'Reset Count: {fast}%prison_mines_reset_count_one%' + +``` + +Once you make these changes and save them, reload bolographic displays with `/hd reload`. You can then tweak the position of the HolographicDisplays so they are just centered on the mine, and keep reloading until you get it right. The X and Z axis should be x.5 and z.5, or "point 5", or half blocks to exactly center them. First get them centered on the mine, you can walk around the mine to confirm it looks centered. Then move each one up, or down, as needed. With the results looking like the following: + + +One block Holographs + + +Now to complete this mine, let's enable auto manager. We can do that by turning on Auto Manager for all of our mines, or we can just add a few ore entries to our OP pick. For this example, let's go with the lore. Enter these commands (assuming you have Essentials installed). Using `/prison autofeatures` command to review the correct names for the lore. Please note for this example I'm using 100% since no numbers are specified with these Lore names. + +It should be noted that Prison's Auto Features is currently in a state of development and may change with how these settings should be applied. Efforts will be made to keep these instructions up to date, but they may be missed. If you notice that this has happened, then please contact RoyalBlueRanger on the discord server and he will get this corrected. ### Auto Features are not needed to demonstrate this one block mine, but it does illustrate a few more capabilities that exist within Prison and how a few settings can provide a different experience. + +``` +/prison autofeatures +/i diamondpick 1 efficiency:20 durability:20 fortune:5 lore:Pickup|Smelt|Block +``` + +More information on the EssentialX's lore settings can be found here: [EssentialsX Item_Meta](http://ess.khhq.net/wiki/Item_Meta). + + +OP Pick with Lore + +Note, because we are using Lore on your pickaxe, we don't have to enable the auto features, such as with `AreEnabledFeatures`, but the individual elements should be all be set to `true`, or the `AllBlocks` should be set to `true`. The Lore on a tool can bypass the global settings. Also make sure you drop back in to survival mode to get the normal speed for your OP pick. + +``` +/gamemode survival +``` + + +OP Pick with Lore + + +And that's it for now. This is what I would consider a draft copy, and I will come back in a few days to refine this document (and remove this comment). + + +
diff --git a/docs/prison_docs_102_setting_up_ranks.md b/docs/prison_docs_102_setting_up_ranks.md new file mode 100644 index 000000000..573eec30c --- /dev/null +++ b/docs/prison_docs_102_setting_up_ranks.md @@ -0,0 +1,549 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Setting Up Ranks & Ladders + +This document provides information how to setup and use ranks and ladders. + +Information on how to use Prestiges is provided in a separate document: [Setting up Prestiges](prison_docs_107_setting_up_pestiges.md) + + +The use of Ranks, Ladders, and Prestiges are all optional. You can either use these Prison based items, or you can use some other plugin to provide these functionalities. The choice is yours. + + +
+ + +# Overview + + +Prison is more than just mines. Mines provides the physical content of your server. It's the ranks and the ladders that ties everything together. It's the Ranks that provides the hooks in to all of your server's other plugins through the use of Rank Commands. And it's the ladders that provides a logical progression from one rank to the next. + + +Prison Ranks can be very simple in that they don't have to do much. They don't even need to have any Rank Commands, but its through the Rank Commands that provides the power to customize your server in almost any way you can dream. + + +Ladders are a way to logically organize your ranks. The concept is a just like a ladder where you start at the bottom, and to progress up the ladder, one rank (rung) at a time. You cannot skip ranks, but you can also go back down the ladder too. Only admins can demote players, since that's not part of the normal Prison game play. + + +Prison also provides a number of Admin based commands to manage ranks and ladders. Obviously the standards on how to create and edit the Ranks and Ladders, but also Prison provides the tools to manage players within the various ranks and ladders too. + +
+ + +#Ranks + +There are many different ways you can setup your Prison server and ranks is just one small part of how you can customize everything. + + +One way to do ranks is to have a few prison ranks, with working to a "free" rank and beyond. Example would be ranks in the following order: D (default), C, B, A, Free, Citizen, Elite, Rules, Emperor. Where ranks D through A are restricted to the mines within the prison, but then Free and higher have access to the free worlds to gather resources, build bases, and other things such as even factions. A few examples provides screen prints with this design since it's generally fewer overall ranks. + + +Another type of prison game mode is more along the lines of an OP based Prison server where ranks run from A through Z with prestiges and even rebirths. Where the costs of ranking up becomes significantly higher with each rank, but yet the ores and blocks you can mine have much greater worth. The tools are generally very OP and beyond the normal safe enchantments. + + +This documentation uses setups that are fairly simple. The mine names also matches the rank names, but they don't have to. Matter of fact, the rank name and mine names can be almost anything to allow the most flexibility for your setup. + + +
+ + +#Adding Ranks + + +Adding Ranks is pretty simple. + + +The command to add a rank is as follows, along with the help generated for it: + +``` +/ranks create [rankName] [cost] [ladder] [tag] + +/ranks create help +``` + +The first rank you add will become the default rank on the ladder in which you add the rank. As you add more ranks, they will be added to the ladder in the order in which they were added. + + +If you need to change the order of a rank, or even move it to another ladder, you can use the command: `/ranks ladder addrank`. Ranks do not have to be associated with a ladder and with this command, you can actually remove the ladder from the rank, or move it around. See that command under ladders for more information. + + +The `cost` for the default rank, or the first rank on a ladder, should have a value of zero if you expect new players to gain this rank when they join the server. It doesn't have to have a zero cost, but it depends upon what your needs are. If the ladder is not the **default** ladder, then the odds are that the rank is not going to be zero. + + +The `ladder` field for this command is the ladder name to which the new rank will be assigned. If it is a rank that is based upon prison mining and advancement, then the **default** ladder should be used. The **default** ladder name is simply **default** and it does not have to be provided if there are no other parameters that follows. If you want to provide a `tag` name then just use a value of `default` for that parameter. + + +The `tag` parameter is the fancy display name for this given rank. You do not have to specify it on this create command, since you can always add it, or change it later with the `/ranks set tag` command. The tag generally contains additional formatting characters such as color codes and even brackets or other characters. It does not have to contain the rank's name. The tag is what is generally used in placeholders for its special formatting. + +For example a default prestige rank may simply be something like this with the emphasis on the plus sign: `&d[&6+&d]` + + + +Creating Ranks + + + + +
+ + +#Rank Information + +Information on a rank can be viewed by using the following command: + +``` +/ranks info [rankName] + +/ranks info help +``` + +This example of rank information includes a rank that has a custom currency. *See more information below on custom currencies.* If a rank does not have a custom currency, then it will not show the currency information. + + +Rank Info + + + +#Rank Listings + +To list all ranks for a given ladder use the following command: + +``` +/ranks list [ladderName] + +/ranks list help +``` + +The `ladderName` parameter is optional and will default to a value of **default**. To view ranks on another ladder, then you must use that ladder's name. But please notice, that at the bottom of the listing, it shows what other ladders are available to be used with this command. If you use this command in game, then you can click on those displayed commands to show those listings. + +Listing Ranks + + + + +
+ + + +#Deleting Ranks + +Ranks can be deleted, but when they are, they are simply just removed from Prison and no longer loaded. Deleted Ranks can be undeleted. + +To delete a rank, use the following command: +``` +/ranks delete [rankName] +``` + +Ranks are stored within the server's file system in the following path starting from the server's main directory: + +``` +/plugins/Prison/data_storage/ranksDb/ranks/ +``` + +Internally, all ranks are assigned a serial number which is used to identify the rank. The number starts with a 0 and is incremented as more ranks are added. Ranks that are deleted, may have new ranks reuse the same number. + + +This internal serial number is what Mojang would consider a magic number. In a future version of prison, these magic numbers will probably be eliminated. Their elimination will not impact preexisting prison configurations moving forward, but could impact the ability to down grade prison to an older release. + +When a rank is deleted, it is removed from memory, and the file that is saved in the above directory path is renamed and ignored the next time prison is started. For example, if a rank's file name is **rank_2.json** then when it is deleted it will be renamed **.deleted_rank_2.json_.del**. Such that it may look like this: **.deleted_rank_2.json_2020-08-16_13-49-27.del** + +To undelete a deleted rank, first shut down the server. Then go in to the server's directory above and rename the deleted file back to it's original name. If that name is taken by another rank, then you must rename it to have a magic number that is not being used by another rank, plus you must also change the internal value of the field "id" in that json file to match the new number that you assigned to it. + +For example, with the file name originally being **rank_2.json** and it was deleted and assigned the new name of **.deleted_rank_2.json_2020-08-16_13-49-27.del**. Plus if numbers 0 through 36 are taken, and you must rename it with the serial number of 37: **rank_37.json** then change the internal value of "id" to match: + +``` +{ + "cost": 3500000.0, + "name": "guardian_of_zool", + "currency": "zoolian_hearts", + "id": 37, + "tag": "&4[&b&ka&bGuardian of &eZool&b&ka&4]", + "commands": [] +} +``` + +The above shows how "id" has been updated to a value of `37`. It also shows that this rank uses a custom currency named **zoolian_hearts**. + + +
+ + +#Changing a Rank's Cost + +The Rank's cost is initially set when creating a rank, but it can be changed at any time. The command to change the cost is: + +``` +/ranks set cost [rankName] [cost] + +/ranks set cost help +``` + +The cost of the rank can be any value, up to the max value allowed by a Java double. The limitations of what can be stored in a double is also constrained by Java too. + +As an example, you can be able to use a really large value for a cost, but you may not be able to set the one's digit of that value. For example if you assign a cost of 214748364952382342, note that the last three digits are 342. But when you view the cost that is actually assigned to the rank, it will show the last three digits as 336. This is not an error, but a limitation with how floating numbers, such as doubles, work within computers in general. This is related to the fact the number is stored in more than one part, of which the mantissa and exponent are most significant for costs. If you're going to use really huge values for a cost, you must understand you cannot control some of the digits of a double. + + +Creating Ranks + + + + +If you are going to assign the rank a custom currency, then you can set the currency at any time, before or after changing the cost. + + +
+ + +#Changing a Rank's Currency + + +Prison supports custom currencies, instead of the default currency that is used with most plugins such as Vault or Essentials. + + +In order to use any default currency, you first must have a plugin that prison recognizes that supports multiple currencies and that must be a valid currency within that plugin prior to its use. + + +Currently the only plugin that Prison supports for custom currencies is GemsEconomy. GemsEconomy can be used in addition to the default currency as accessed through Vault. At this time, Vault does not support custom currencies, so Prison must be have an integration that supports it. + + +All custom currencies will be validated when they are assigned, changed, or loaded upon server startup. If a custom currency has been added to a Rank, but then removed from the server (removal of the plugin or deletion within the plugin) then Prison will prevent usage of that Rank by the players, even if they had sufficient amounts of that currency since there is no way to perform any of the transactions against it. Upon server startup, if Prison detects that a currency is no long supported, it will display a warning with related details. + + +When Prison is looking for support of a custom currency, it checks all registered economy plugins that have support for custom currencies, and then checks each of those economy plugins to see if any of them supports the custom currency. This actually allows a server environment to use many different economy plugins, even multiple custom currency plugins. + + +To add a currency to a Rank, first create the rank and give it a cost. You cannot assign a custom currency initially, but that's alright and that's expected. + + +Then associate a custom currency with the rank: + +``` +/ranks set currency [rankName] [currency] + +/ranks set currency help +``` + +The currency must exist in one of your economies. How to add it is beyond the scope of Prison's support and this documentation. + + +To currency can be changed to any other custom currency by using this command again. + + +The currency can also be removed, and set back to the default, by leaving the currency name blank. For example this removes a custom currency and returns it to a standard currency. + + +``` +/ranks set currency [rankName] +``` + + +The following is an example of a custom currency, named "puurr" that no long exists with in the server since the GemsEconomy plugin is not currently active. + + +Custom Currency Failure + + +Even though in the above example the custom currency "puurr" does not exist, players cannot rank up to the **Free** rank since Prison cannot confirm that it is a valid currency. But admins can use `/ranks promote`, `/ranks demote`, and even `/ranks set rank` to force a player to have those ranks. + + +
+ + + +#Changing a Rank's Tag + + +A Rank's tag is a formatted String value that is intended to be used with placeholders when special formatting is needed, or desired, for a given Rank. The tag value can be set when the Rank is initially created, or at any other time. If a tag is not defined, then any access of the tag through placeholders will only return the Rank's name. + + +To change a tag use the following command: + +``` +/ranks set tag [rankName] [tag] + +/ranks set tag help +``` + +All valid color and formatting codes that are based upon the `&` symbol can be used. + + +For example: &0 - &9, &a - &f, &k (magic), &l (bold), &m (strike through), &n (underline), &o (italic), and &r (reset). Google Image search "minecraft color codes" for graphic represtations for what those codes represent. Note that the magic value is controlled by the character that follows it. So if you have `&kaaaa` it will cycle through four magic values and they will all be in sync. But if you have `&kabca` then only the first and fourth digits will be sync'd and `b` and `c` will be viewed as different values. + + +
+ + +#Showing a Player's Ranks + +A player may only have zero or one active rank per ladder. But since a player can have active ranks in more than one ladder, this command will show all ranks within all ladders that are available for the given player. + +``` +/ranks player [playerName] + +/ranks player help +``` + +This command will show the Player's name, the given ladder and the rank name. If the player is not at the highest rank, then it will also show what the next rank's name is, along with the cost to achieve that rank. If the rank has a custom currency, that too will be displayed. + + +The following screen print shows the status of two different players. The **default** ladder is the main ladder for doing the prison ranking. The mobs ladder is a non-related ladder that could represent anything, such as side quests. The Donor ladder represents a ladder used for donors, of which it could have donor specific mines for the players to use. And this also shows that one player has prestiged once but the other player has not. + + +Player Ranks + + + +
+ + + +#Promote, Demote, and Set Rank Commands + + +Perhaps the most important thing to understand about these three commands is that it is strongly suggested that you should **never** use the `ranks set rank` command. This will be explained later, and is related to the rank commands that are associated with your ranks. + + +The following commands are intended for the use of admins only. They are to be used for setting up test accounts to see how their server is working, and to help address rank issues that players may be experiencing. + + +``` +/ranks promote [playerName] [ladder] [chargePlayers] +/ranks demote [playerName] [ladder] [chargePlayers] +/ranks set rank [playerName] [rankName] [ladder] + +/ranks promote help +/ranks demote help +/ranks set rank help +``` + + +**Warning: Risk of corruption of player ranks:** +Promote and demote changes the player's rank by just one rank. This is important because if you need to promote a player 3 ranks, and you run this command three times, then all rankup commands are properly ran for the player and when you promote then to the final rank, they will have the correct permissons. If you were to just use `/ranks set rank` and skip the other two ranks, then you risk the player missing important permissions and as such, may require more interventions to correct their ranks. The `/ranks set rank` command is there in case it needs to be used, but as owner/admin, you need to understand it may corrupt the players rank. + + +Demote is just as important as promote. Especially since it has been strongly suggested that when you create a rank command, that you include the commands to take away permissions that a player may have gotten in the next higher rank. If a given rankup command set includes all the commands required for that rank, and all the commands to remove the permissions from the next higher rank, then using `promote` and `demote` will never corrupt any player's rank and will lead to less problems. + + +For the commands's parameter **playerName** is obviously the player's name. + + +The parameter **ladder** is optional, and if not specified will default to the `default` ladder. If you need to use any other ladder you need to supply the ladder name. + +The parameter **rankName** is required and identifies what rank to set the player to. If there is more than one level of promotion or demotion, then there is a risk of corruption of the player's rank and missing permissions with promotions, or permissions the player should not have access to with demotions. + + +The parameter **chargePlayers** will require that the player has enough of the specific currency for promotions, and will subtract that amount from their bank balance. If they do not have enough funds, then they will not be ranked up, just as if they ran the command. If the player is being demoted and this parameter is used, then the player will be issued a refund for the prior rank. + + +The parameter **chargePlayers** when dealing with custom currency: If this parameter is enabled and the currency for the rank is not valid (removed from the economy, or the economy plugin was removed) then the promotion, or demotion, cannot be ran. If this parameter is **NOT** used and the custom currency does not exist on the server any longer, then the promote, or demote, commands will be ran ignoring the currency. The lack of this command can force a player to a rank where there is a custom currency error. + + + +
+ + +#Ladder Overview + + +By default, Prison automatically creates two default ladders so you do not have to create them, you just use them. These two ladders are called **default** and **prestiges**. The **default** ladder contains all of the ranks that are associated with the normal Prison play and ranking. The **prestiges** ladder is strictly for the prestige behavior within prison. + + + +
+ + + +#Create Ladder Command + +This command will create new ladders. Ladders do not need to have any ranks associated with them. + +``` +/ranks ladder create [ladderName] + +/ranks ladder create help +``` + +The parameter **ladderName** is the name of the new ladder. + +
+ + +#Add Rank to a Ladder + +This command will add a rank to the specified ladder. If the rank was on a different ladder, it will be removed and added to the specified ladder. The added rank will be added to the end of the ladder if **position** is not specified. If **position** is specified then this command can be used to move a rank to another position within the ladder. + +``` +/ranks ladder addrank [ladderName] [rankName] [position] + +/ranks ladder addrank help +``` + +The parameter **ladderName** and **rankName** are as expected and described above in the prior commands that have these same parameters. + + +The parameter **position** is optional. If not specified the specified rank will be added at the end of the ladder. If specified, it will insert the rank at the specified position, or if it does not exist, then at the end of the ladder. + + +If the rank already exists within the specified ladder, then this command will move the rank to the specified **position**. If **position** is not specified, then it will move the rank to the end of the ladder. + + +If the rank already exists in another ladder, it will be moved to the specified ladder to the specified position. + + +
+ + +#Ladder Delete Command + +This command will delete the specified ladder. The ranks that are within the ladder will be unassociated with the removed ladder and all of the ranks will then be ladderless. Any player that has that rank will be unable to be promoted or demoted until the ranks are added to another ladder, or the ranks are deleted. + +``` +/ranks ladder delete [ladderName] + +/ranks ladder delete help +``` + +*Note: Ladders are treated similar to ranks as far as how they are saved on the server's file system. The following information is identicial to the ranks.** + + + +Ladders are stored within the server's file system in the following path starting from the server's main directory: + +``` +/plugins/Prison/data_storage/ranksDb/ladders/ +``` + +Internally, all ladders are assigned a serial number which is used to identify the ladder. The number starts with a 0 and is incremented as more ranks are added. Ladders that are deleted, may have new ladders reuse the same number. + + +This internal serial number is what Mojang would consider a magic number. In a future version of prison, these magic numbers will probably be eliminated. Their elimination will not impact preexisting prison configurations moving forward, but could impact the ability to down grade prison to an older release. + + +When a ladder is deleted, it is removed from memory, and the file that is saved in the above directory path is renamed and ignored the next time prison is started. For example, if a ladder's file name is **ladder_3.json** then when it is deleted it will be renamed **.deleted_ladder_3.json_.del**. Such that it may look like this: **.deleted_ladder_3.json_2020-08-16_13-49-27.del** + +To undelete a deleted ladder, first shut down the server. Then go in to the server's directory above and rename the deleted file back to it's original name. If that name is taken by another ladder, then you must rename it to have a magic number that is not being used by another ladder, plus you must also change the internal value of the field "id" in that json file to match the new number that you assigned to it. + +For example, with the file name originally being **ladder_3.json** and it was deleted and assigned the new name of **.deleted_ladder_3.json_2020-08-16_13-49-27.del**. Plus if numbers 0 through 4 are taken, and you must rename it with the new serial number of 5: **ladder_5.json** then change the internal value of "id" to match: + +``` +{ + "ranks": [ + { "position": 0, "rankId": 7 }, + { "position": 1, "rankId": 8 }, + { "position": 2, "rankId": 11 }, + { "position": 3, "rankId": 13 }, + { "position": 4, "rankId": 6 }, + { "position": 5, "rankId": 15 } + ], + "maxPrestige": 0, + "name": "mobs", + "id": 5 +} +``` + +The above shows how "id" has been updated to a value of `5`. + + +As a warning, you must ensure that no other active ladders have the same name, and that none of the specified ranks that are contained in this deleted ladder, are being used within any other ladders. If either one of these fields are used in another ladder, then you can corrupt your server's list of ranks and ladders and it is unknown how the corruption will impact your server, or what data loss will be permanent and unrecoverable. In other words, the full extent of the corruption is unknown. + + +If you have any doubts on possible conflicts or corruption, then its safer to just create a new ladder and manually move all ranks back in to it.a + +Please note that the ladder's use of magic numbers can cause a great deal of problems when undeleting a ladder since it is not obvious which rank is which, or if it they are even used elsewhere without a lot of manual checking of each rank within the file system. Deleted ranks may be virtually deleted, but if any amount of time has passed such that ranks can be reassigned or deleted and new ones added, then it could be dangerous to undelete a ladder. Undeleting a ladder is best done before any other changes have happened on the server. Undeleting a ladder servers as a fail-safe way of undoing an accidental deletion that you may have second thoughts on a few minutes later. + + +# The Ladders and Ranks Data Files + +The ladders and ranks data files (and also the mines) are stored on the server's file system as a way to store each mine's configurations. These files are intended for internal use only and should never be manually modified. When undeleting ladders or ranks, you may have to rename the files so they are used the next time the server is started, but you should not change the contents with the exception for their IDs. + +Even changing the IDs is a highly risky operation, since if you use an id that is already in existence, then it could corrupt your server. Also the same goes for specifying the wrong ID, or a non existant id on a ladder. + +There are many commands that have interdependancies to multiple settings, and those settings are validated only through the use of the commands. Making changes directly to the files bypasses the validation of the commands and may lead to server corruption and failures. + +You are solely responsible for any problems you create by making changes directly to these files. We cannot help undo any problems, issues, failures, or undefined behaviors that may be caused with such tampering. + + +#Ladder Remove a Rank + +This command will remove a rank from a ladder. In removing the rank, it will not delete the rank, and the given rank will not be associated with any ladder. Any players that are active on that Rank will be removed fro that rank, such as they would be if the Rank was deleted. + +``` +/ranks ladder delrank [ladderName] [rankName] + +/ranks ladder delrank help +``` + + + +#Ladder List + +The ladder list command will list all active ladders on the server. + + +``` +/ranks ladder list + +/ranks ladder list help +``` + +No other information is provided for the ladders listed. For more information on a ladder use `/ranks list [ladderName]` or `/ranks ladder listranks [ladderName]` but this last one has less information provided. + + +#Ladder Ranks Listings + +This command provides a simple list of ranks associated with the given ladder. + +It is suggested that you use `/ranks list [ladder]` instead of this command since it provides a lot more information than this command. + +``` +/ranks ladder listranks [ladderName] + +/ranks ladder listranks help +``` + + + + + +
+ + + + +#Rank Commands + +The use of Rank Commands is what hooks everything together within Prison. It ties Ranks to mines and even hooks it up to other plugins and custom behaviors. + +A good example of how to use it with LuckPerms can be found in the documentation for [Configuring and Using WorldGuard with LuckPerms to Protect Mines](prison_docs_626_configuring_worldguard_regions.md). LuckPerms and WorldGuard is just one use case, but the information provided in that documentation can be applied to any other plugin or another permission plugin. + + +(more information to be added later, for for now, the WorldGuard and LuckPerms document covers a lot of the key features) + + +
+ + +#Example of using Ranks + +(coming soon an example on how to use ranks) + + +
+ + +
+ +#This is a work in progress! + +More coming soon! + + +Please visit our discord server to ask for help if you need it. + +Sorry this document is not yet available. + + +
+ diff --git a/docs/prison_docs_107_setting_up_pestiges.md b/docs/prison_docs_107_setting_up_pestiges.md new file mode 100644 index 000000000..c6e0060c3 --- /dev/null +++ b/docs/prison_docs_107_setting_up_pestiges.md @@ -0,0 +1,199 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Setting Up Prestiges + +This document provides information how to setup and use prestiges. + + +
+ +# Overview + +This document should be able to provide the basic information to get the built in prestiges configured and working. + + +
+ + +# Enabling Prestiges + +By default, prestiges are disabled and must be enabled before you can use them. Edit the following file: + +``` +plugins/Prison/config.yml +``` + +Then set the following item to `true` and then save the file and restart the server. + +``` +prestiges: true +``` + +Example of enabled prestige setting + + +
+ + +# The Prestige Ladder + + +The prestige ladder is created by default and is named `prestiges`, even if prestiges are not enabled. Ranks can be added to the presetige ladder, even if it is not enabled. + + +In order to use prestiges, you need to create new ranks for each prestige level. Add them as you would for the default ladder and the normal ranks, but you need to specify the prestige ladder name within the commands. + + +To show all the ladders that are available, use the following command. This can help confirm that the ladder exists (it should be created as default) and it can confirm the correct spelling too. + +``` +/ranks ladder list +``` + +List of ladders + + +
+ + +# Adding Prestige Ranks + +In order to add prestige ranks, you add them just like you add ranks, but you specify the prestige ladder within the command. + + +You can name them anything you want, but in our example below we are using p1, p2, etc, with the prestige tag values of `+` for the first prestige rank, then the following would be `+2`, `+3`, etc., wrapped in square brackets and colored. + + +Here is an example of adding the first two prestige ranks. These are the commands: + +``` +/ranks create p1 5000000 prestiges &d[&5+&d] +/ranks info p1 +/ranks create p2 8500000 prestiges &d[&5+2&d] +/ranks info p2 +``` + +This screen print shows the results of the above four commands. + + +Creating the first two prestige ranks + + + +
+ + + +# Setting up the Chat Placeholders or Prestige Ranks + +You can add placeholders to the chat prefixes. Please consult your chat documentation on how to do this, but here is an example for Esstentials-X Chat plugin. + + +Enabling the chat placeholder just requires editing one line within the `config.yml` file. Search for the keyword **EssentialsChat** in that file, then edit the `format:` tag. For example: + + format: '<{prison_rank_tag}:{DISPLAYNAME}>{MESSAGE}' + + +Once setup, restart the server. Or use **/essentials reload**. Do not force all the plugins to be reloaded with a tool such as plugman since Prison (and other plugins) may fail to re-load properly. + + +The placeholder `prison_rank_tag` will include all placeholders that are active for the player, including the prestige chat prefixes. The only issue is that you cannot control the order of how they will display. + + +It is possible to manually set the order by using the individual placeholders for each rank you want to show. This allows you to omit certain placeholders too. You can preview all of the placeholders with the placeholder search command. + + +``` +/prison placeholder search rank_tag + +/prison placeholder search RoyalCoffeeBeans rank_tag +/prison placeholder search RoyalCoffeeBeans 2 rank_tag +``` + +Please notice the first placeholder that is displayed in this screen print. The `prison_rank_tag` shows prestige as second, and default is third. It also shows a donor rank first. This order is not controllable and may vary from server to server. If you need another order, then you will have to manually set them up. + + +Reviewing all placeholder tags + + +In our example, to customize the chat prefixes, we are interested in the following prefixes: `prison_rank_tag_pretiges` and `prison_rank_tag_default`. + + +So to set them up in the EsstentialsX Chat prefix would be as follows: + +``` + format: '<{prison_rank_tag_prestiges}{prison_rank_tag_default}:{DISPLAYNAME}>{MESSAGE}' +``` + + +
+ + + +# Prestiging + +Prestiging is simple to do. If the player is at the highest default rank, then they can rankup the prestige ladder. Prestiging will reset the player's balance to zero. Currently there is no warning or confirmation so proceed carefully. + +``` +/prestige +``` + +The following screen print is from performing the following commands, where the player does a prestige, then sends a chat message to show the chat prefixes. + +``` +/ranks player +/prestige + +``` + +Note the following ranks on this server goes from E (the lowest) to A (second highest), then Free (highest). The first command `/ranks player` shows the player is at Free rank. + +Prestige + + + + +Prestige is only available through a gui interface. You have to hover over the blocks to see the lore, which provides additional information. + + +Prestige + + +Showing the Confirm Prestige button. + +Prestige + + +Showing the cancel button. + +Prestige + + +
+ + + +# The prestiges Command + +This command shows the prestige ranks. There are a few ways to get a listing of all of the prestige ranks. + +``` +/prestiges +/prisonmanager prestiges + +/ranks list prestiges +/ranks ladder listranks prestiges +``` + +This command is intended for players and can be enabled, or disabled, within the `plugins/Prison/GuiConfig.yml` file. The command `/prestiges` is less configurable, but not sure why. + + + +
+ + + + + diff --git a/docs/prison_docs_111_mine_commands.md b/docs/prison_docs_111_mine_commands.md new file mode 100644 index 000000000..6f11fbc15 --- /dev/null +++ b/docs/prison_docs_111_mine_commands.md @@ -0,0 +1,172 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Mine Commands + +This document provides information how to setup and use Mine Commands. + + +
+ +# Overview + +Mine Commands can allow you to take your mine to the next level. You have control to run a series of commands before, or after, then mine actually resets. You can even disable the replacement of individual blocks that is traditionally associated with a mine reset. + +The mine commands operate like rank commands, except that they run during a mine reset event. They are ran as console, so they have full perms within the server. They can also run any command that you have access to from the console, so it may be best to test all the commands from the console prior to adding them. + +This document will show a simple example, but your imagination can take it to the next level. + +My personal goals to use this new feature was to dynamically build a large forest. Then upon reset, it would erase all the trees and leaves, including any that grew outside of the mine, then randomly plant new trees. The results would be a new random forest that would be generated each time. Unfortunately, I have most of the details worked out on how it will work, but not 100% done yet, so I do not have this example to share at this time. :( + - Blue + + +
+ + +# Adding and Listing Mine Commands + +The family of mine commands are as follows: + +``` +/mines command +``` + +Mine Commands + + +Add a mine command with the following command. I'll add the same command twice just to illustrate that you can have a command run before, or after a mine reset. + +``` +/mines command add + +/mines command add trees before ping +/mines command add trees after ping + +/mines command list trees +``` + +Running these three commands looks like the following. + + +Adding Mine Commands + + +The `/mines info trees` command shows how many commands are associated with the mine. It shows that there are two commands at the bottom of the listing. + +Adding Mine Commands + + + +
+ + +# Mine Command Listing Information + + +One point that needs to be explained has to do with the actual listing of commands. The listings will be in the order that you add them, and that is the order in which they will be ran. But it does not matter if you mix together the **before** and **after**. The whole list is processed in order each time, but a filter only runs the correct command. + +It should also be understood that the **before: ** and **after: ** is actually part of the command and is stored that way. If a command does not have either of those prefixes, then it will never be ran. This is a warning if you are tempted to manually change the commands (could happen). + + +
+ + + +# Mine Resets + + +To test these commands, just perform an manual reset. + +``` +/mines reset trees +``` + + +Adding Mine Commands + + + + +
+ + + +# Deleting Mine Commands + +Deleting is simple, but it may not be quite as obvious. + +First it is suggested that you list all of the commands associated with your mine, then copy the command you want to delete. To copy the command, select everything between the single quotes. Then paste that in to the delete command. + +As a tip, if you are in game and you list the mine commands, clicking on the command in the listing will delete it. This could be dangerous, so because when listing commands in game. This example will showcase the command usage from within the console. + + +Using the these commands to illustrate removing a command, with listing all of the commands before and after removal. Then performing a mine reset to show that only one command is ran. + + +``` +/mines command list trees +/mines command remove trees before: ping +/mines command list trees + +/mines reset trees +``` + +Removal of one Mine Commands + + + +
+ + +
+ + + +# Parent Mine Control + +One feature of interest that was requested by a few people, was to have the ability to use Parent mines to control the child mines. + +A parent mine is a large mine, that has smaller mines nested inside it, or the children could be elsewhere outside of the parent mine. The options are numerous, but the idea is that all the mines reset at the same time as if they are connected. + +To accomplish this, let's use the following for our example. The mines will be based upon the name of **doggo**. Of course, you may have a better name in mind, so let your imagination rule! + +* **Parent Mine:** doggo +* **Child Mine 1:** doggo1 +* **Child Mine 2:** doggo2 + +Create the mines... of course do the selections first. The set the mine reset times, making sure that the children reset times are larger than the parent's. This will ensure that the children will actually never be reset automatically on their own. + +``` +/mines create doggo +/mines create doggo1 +/mines create doggo2 + +/mines set resettime doggo 900 +/mines set resettime doggo1 1000 +/mines set resettime doggo2 1000 +``` + +Then setup the commands on the parent to manually reset the children. + +``` +/mines command add doggo after mines reset doggo1 +/mines command add doggo after mines reset doggo2 +``` + +Now since we are assuming the children are nested within the parent mine, let's prevent the parent mine from destroying the child mine's blocks. We can do that by using the block type IGNORE at 100% within the parent mine. + +``` +/mines block add doggo IGNORE 100.0 +``` + +Then set the blocks in the child mines. + +And that's it. You can tweak some of the other settings on the mines, but this accomplishes what we set out to do: have a parent mine reset the children mine at the same time. Since the children's reset times are much longer than the parents, they will never reset on their own because when they are manually reset, it resets the timer, which will never reach zero. + + + +
+ + + diff --git a/docs/prison_docs_113_setting_up_sellall.md b/docs/prison_docs_113_setting_up_sellall.md new file mode 100644 index 000000000..fc96ac1b6 --- /dev/null +++ b/docs/prison_docs_113_setting_up_sellall.md @@ -0,0 +1,116 @@ +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - SellAll info + +This document provides info about how to use the SellAll feature and set it up + +
+ +# Overview + +SellAll's a feature with the goal of sell and clear the player inventories from ores or blocks for money. +Essentially, while mining you'll fill your inventory and once full you need to sell what you've mined +to get money and rankup or buy stuff, SellAll will do all the job and give you the money. + +There's also a Multiplier feature which will modify the sellall values of all blocks depending on your prestige but before +using it you should enable ```prestiges: true``` in your ```config.yml``` + +
+ +# Getting started + +First of all, if you want to use this feature, you'll need to enable it in your ```config.yml``` by editing ```sellall: false``` to ```sellall: true``` (by default's on false) and restart the server. +Also if you want give a look to the ```sellallconfig.yml``` and customize it. + +*THE CONFIG SHOULD LOOKS LIKE THIS:* +_It might even be slightly different but it doesn't matter_ + +``` +Options: + GUI_Enabled: 'true' + GUI_Permission_Enabled: 'true' + GUI_Permission: prison.admin + Sell_Permission_Enabled: 'false' + Sell_Permission: prison.admin + Add_Permission_Enabled: 'true' + Add_Permission: prison.admin + Delete_Permission_Enabled: 'true' + Delete_Permission: prison.admin + Player_GUI_Enabled: 'true' + Player_GUI_Permission_Enabled: 'false' + Player_GUI_Permission: prison.sellall.playergui + Multiplier_Enabled: 'true' + Multiplier_Default: '1' + Multiplier_Command_Permission_Enabled: 'true' + Multiplier_Command_Permission: prison.admin +``` + +### Then, you can do two things: +* If you're lazy or just don't want to do a ton of math like me, there's a default setup command which will add all the blocks +you should need for a Prison server with preset values, the command's ```/sellall setdefault```. +Then you could also edit these values if you don't like them with the command ```/sellall edit ```, for example ```/sellall edit COAL_ORE 75``` ; or +remove blocks with the command ```/sellall delete ```. + +* You can edit values or remove blocks even from the GUI ```/sellall gui```, there's a _similar GUI_ with the _same command_ for +players **without** special permissions with just the blocks and their values as an info. + +**OR** + +* Add all the blocks by yourself with your values with the command ```/sellall add ```, for example ```/sellall add COAL_ORE 75```. + + +
+ + +# ADVANCED FEATURES + +**Multipliers:** + +To use this feature, you'll need to enable prestiges in your ```config.yml``` by editing ```prestiges: false``` to ```prestiges: true```, add some Prestiges and enable +multipliers in your sellallconfig.yml by editing ```Multiplier_Enabled: false``` to ```Multiplier_Enabled: true```. + +The command ```/sellall multiplier``` will show all the sub-commands of this feature, right now there're only 2 commands: +``` +/sellall multiplier add +/sellall multiplier delete +``` + + +The first one will add a Multiplier to a Prestige and the second will remove it. + +How Multipliers work -> they'll just multiply the value of what you sold, for example: Your items worth ```$1000``` and you have a multiplier of ```1```, +so ```1000 * 1 = 1000``` is what you'll get (by default this's the value in the sellallconfig.yml), but if you've a Prestige with a multiplier of maybe ```1.5```, then +you'll get ```1000 * 1.5 = 1500```, this isn't even hard math but could make you some confusion. + +# LIST OF COMMANDS +``` +/sellall +Will show a list of commands to admin and sell items to those who aren't admins or players with special permissions. + +/sellall sell +Will sell the items of your inventory. + +/sellall add +Add an ITEM/BLOCK to the sellall config. + +/sellall delete +Delete an ITEM/BLOCK from the sellall config. + +/sellall edit +Edit an ITEM/BLOCK of the sellall config. + +/sellall GUI +Open an ADMIN GUI to admins where you can edit blocks values or remove them or open a PLAYERS GUI to player where they can see values of blocks. + +/sellall multiplier +Will show a list of multiplier sub-commands. + +/sellall multiplier add +Will setup a multiplier to that prestige. + +/sellall multiplier delete +Will remove a multiplier from the prestige. +``` + +And this's all, I hope this will help you all and also hope I didn't miss something, feel free to report your issues in our discord server or here on github! diff --git a/docs/prison_docs_310_guide_placeholders.md b/docs/prison_docs_310_guide_placeholders.md new file mode 100644 index 000000000..93290ab24 --- /dev/null +++ b/docs/prison_docs_310_guide_placeholders.md @@ -0,0 +1,279 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Placeholder Guide + +This document covers different aspects of placeholders within Prison. It explains how they work, how to use them, and different ways to use them. + +
+ + +# Overview + +Placeholders allow the sharing of data from one plugin with another plugin, and without either plugin knowing anything about each other. + +On the surface they appear to be simple, but there are a lot of moving parts below the surface, and with Prison Mines, there are even more things going on. + +Add in to the mix, that different plugins deal with placeholders in slightly different ways, and you can wind up with a challenge to get them to work under different circumstances. + +
+ + +# Requirements + +There is always more than one way to do things, and the same goes for having more than one way to use placeholders. This documentation, and our test servers, primarily rely on the use of PlaceholderAPI as the core component to making placeholders work. Depending upon other plugins that you may use, they may have other dependencies too. + +**PlaceholderAPI** - [Setting up PlaceholderAPI](prison_docs_0xx_setting_up_PlaceholderAPI.md) - Strongly Suggested if using placeholders. + +
+ + +# Placeholder Theory for Prison + +There are two major types of placeholders in prison: Player based and Mine based. + +The player based placeholders can only report on the player that is initiating the command, or request. These placeholders pertain to the player's attributes, such as rank or next rank. Internally, all of these requests must include the player's UUID, which is why you cannot just add them to a sign, since the sign does not know any player's UUID. + +There are actually two kinds of player based placeholders. Rank placeholders that returns all of the player's current attributes for all active ladders they are associated with. For example, if they are active on four ladders, then they will have four active ranks on four ladders with possible values. So it may look something like this: `[mod][Zeus][+2][B]`, of which the ladders could be mod, donor, prestige, and default. The thing to remember about player rank placeholders is that they may return more than one value, and you cannot control the order of the values (yet). + +The other player based placeholders are similar to the player rank placeholders, but are specific to a single ladder. This allows you to get single placeholder, and control the order of placeholders, at the expense of having to specify multiple placeholders. + +The Mine based placeholders provide details about mines, and the mine name becomes part of the placeholder name. Out of all of the possible mine based placeholders, each one is duplicated for each mine. So, in rough terms, if there are different mine placeholders and you have 40 mines, then prison will generate about 400 place holders: 40 mines x 10 placeholders each = 400 placeholders for prison. + +Prison has integrations for direct use of providing placeholder values to to the other plugins. Some of those other plugins request placeholder values using partial placeholder names. Therefore to improve performance and to prevent having to always reconstructing the full placeholder names, prison precomputes the fragments for all placeholders. Therefore, with our example of 40 mines and 10 placeholders, the actual internal number of placeholder combinations that prison will respond to is 800: 40 mines x 10 placeholders per mine x 2 for aliases = 800 placeholders for prison. + +Off hand this may sound bad, but Prison utilizes enumerations for identifying placeholders, so they may be objects, but they are lightweight and helps ensure placeholders align with the code at compile time. This not only provides better performance, and less memory consumption, but programming errors and typos are caught at compile time and not runtime, so they also provide for a more stable and reliable Prison environment. + +Internally, placeholders within Prison are case insensitive. But Prison uses lower cased placeholder names to register with any placeholder integration. Therefore, although prison may be case insensitive, the placeholder plugins may not recognize the placeholders unless you use them as all lower case names. + +Also, internally, prison only responds to the placeholder name without the escape characters. The escape characters are generally curly braces { }, or percents % %, but may be other characters. All placeholder plugins strip off the escape characters before passing anything to prison. So proper use of placeholders is dependent upon what is being required by your placeholder plugin and may require a mix of escape characters. + +
+ + +# Rank Command Placeholders + +The Rank Commands recognize only two placeholders, but they are not considered part of the standard placeholders. There are also only two placeholders that are recognized and both are case sensitive, and must also include curly braces too. + +* {player} +* {player_uid} + +This is mentioned here since these rank command placeholders are not part of all the other placeholders, so as such, it may be difficult to find information for these items. + + +
+ + +# Placeholder Commands + +*Since Prison v3.2.1-alpha.13* + +There are a few commands within prison that will allow you list placeholders, search for placeholders, and to test random text that includes placeholders. + +* **/prison placeholders** + + +* **/prison placeholders list** +* **/prison placeholders search** +* **/prison placeholders test** + + +* **/prison version** Provides the same placeholder listing as /prison placeholders list. + + + +

Prison Placeholder Command Listing

+ +Prison Placeholder Commands + +Example of placeholder command listings + + +

Prison Placeholder Listings

+ +Prison Placeholder Listing + +Example of the list of placeholders that is available through **/prison version** and **/prison placeholders list**. Please note that this list may evolve as new placeholders are added. Use these commands to get the current listing that is available on your server. + + +

Prison Placeholder Search with Two Search Patters

+ +Prison Placeholder Search + +This is an example of searching for placeholders using two search patterns: *temp5* and *format*. The term temp5 is the name of a mine and is an example of a the dynamic construction of placeholders, and that you can still perform a search with them. The search patters can be any String fragment found in either the placeholder, or it's alias. + +If more than one search pattern is provided, then all patters must hit on the same placeholder to be included in the results. They behave as a logical AND relationship. + + + +

Prison Placeholder Listings - All Placeholders

+ +Prison Placeholder Listing + +In this contrived example, since all placeholders begin with "prison", this search returns a listing of all placeholders. In this example, using the current Prison v3.2.1-alpha.13 release, it has generated 65 pages of results, at 6 placeholders per page which includes the alias. + + +

Prison Placeholder Listings

+ +Prison Placeholder Listing + + + +
+ + +# Chat Placeholders + +There are two major ways a placeholder can be resolved within Prison: through chat or through a placeholder integration. + +Chat based placeholders do not rely on other plugins for them to work. Instead they use the org.bukkit.event.player.AsyncPlayerChatEvent, of which Prison will respond and provide translations to prison related placeholders that it find with the chat message. + +Although no plugin is required for Prison to properly handle chat based placeholders, other plugins may be required to generate their use. Such as EssentialsX's Chat plugin. It provides a way to prefix chat messages with placeholders. + + + +
+ + + +# Enabling EssentialX's Chat Placeholders + +Set up the EssentialX's Chat plugin: [Setting up EssentialsX](prison_docs_0xx_setting_up_EssentialsX.md). + +Enabling the chat placeholder just requires editing one line within the `config.yml` file. Search for the keyword **EssentialsChat** in that file, then edit the `format:` tag. For example: + + format: '<{prison_rank_tag}:{DISPLAYNAME}>{MESSAGE}' + +Once setup, restart the server. Or use **/essentials reload**. Do not force all the plugins to be reloaded with a tool such as plugman since Prison (and other plugins) may fail to re-load properly. + + +This will include all placeholders that are active for the player, including the prestige chat prefixes. The only issue is that you cannot control the order of how they will display. + + +It is possible to manually set the order by using the individual placeholders for each rank you want to show. This allows you to omit certain placeholders too. Please see the next section of this document to learn how to set them up. + + +You can preview all of the placeholders with the placeholder search command. + + +``` +/prison placeholder search rank_tag + +/prison placeholder search RoyalCoffeeBeans rank_tag +/prison placeholder search RoyalCoffeeBeans 2 rank_tag +``` + + +Reviewing all placeholder tags + + +The placeholders are interested in are: `prison_rank_tag_pretiges` and `prison_rank_tag_default`. + + +So to set them up in the EsstentialsX Chat prefix would be as follows: + + + format: '<{prison_rank_tag_prestiges}{prison_rank_tag_default}:{DISPLAYNAME}>{MESSAGE}' + + + +

Ladder Specific Placeholders

+ +One of the newer features available for placeholders has to do with ladder specific placeholders. This is similar to the mines placeholders but for ladders specifically. These player ladder placeholders are just the same a the player rank, but only for one ladder. The player rank placeholders will return all ranks on all ladders for that placeholder. + +This is an example of specifying the rank tag for both the prestige and default ladder. This ensures the expected order of these two tags, but it will also exclude other tags from other ladders, such as donor ladders or mod ladders. + + format: '<{prison_rank_tag_prestiges}{prison_rt_default}:{DISPLAYNAME}>{MESSAGE}' + + +Use the placeholder tools under `/prison placeholders` for more information and to see what is available for use. + + + +For example, to preview all of the placeholders with the placeholder search command. + + +``` +/prison placeholder search rank_tag + +/prison placeholder search RoyalCoffeeBeans rank_tag +/prison placeholder search RoyalCoffeeBeans 2 rank_tag +``` + + +Reviewing all placeholder tags + + + + +
+ + + + +# Enabling HolographicDisplays Placeholders + +Follow directions on how to install and configure the HolographicDisplays plugin. + + +An important detail to realize, is that HolographicDisplays can only use placeholders that related to the server. It cannot access any of the prison placeholders on its own. It needs the help of other plugins to extend it's functionality. + +* [HolographicDisplays Download](https://dev.bukkit.org/projects/holographic-displays) +* [HolographicDisplays' Documentation](https://filoghost.me/docs/holographic-displays) + +* [HolographicExtension](https://www.spigotmc.org/resources/holographicextension.18461/) +* [PlaceholderAPI Setup Details](prison_docs_0xx_setting_up_PlaceholderAPI.md) +* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) + +The simple directions on how to get everything to work is to download these plugins, and place them all in to your server's plugin directory. There is no need to modify any config files to get them to work with prison. Prison will register all of the internal placeholders with PlaceholderAPI so it knows they are valid and where to find them (where to send them to be resolved). + +The simplest way to create a HolographicDisplay would be to stand where you want one. Then enter the following command: + +``` +/hd create test +``` + +Then on the server file system, open the file: + +``` +plugins\HolographicDisplays\database.yml +``` + +Then edit the file. Keep in mind it is a yml file and must be valid yml. + +This is an example of a holographic sign: + +``` +temp5: + location: world, -99.832, 74.500, 191.215 + lines: + - 'Welcome to Prison Mine: temp5' + - 'Reset Interval: {slowest}%prison_mines_interval_temp5% - {slowest}%prison_mines_interval_formatted_temp5%' + - 'Reset Time Left: {medium}%prison_mines_timeleft_temp5% - {fast}%prison_mtlf_temp5%' + - 'Reset Count: {medium}%prison_mines_reset_count_temp5%' + - 'Mine Size: {slowest}%prison_mines_size_temp5%' + - 'Blocks Remaining: {slowest}%prison_mr_temp5% {slowest}%prison_mp_temp5%%' + - 'Players in Mine: {slowest}%prison_mines_player_count_temp5%' +``` + +Notice that the prison placeholders are wrapped in the % % escape characters. The prefixed placeholders such as {slowest} and {fast} are for the plugin HolographicExtensions and they control how frequently the placeholders are refreshed. + +Once you update and save the database.yml file, you can have HolographicDisaplys reload from the files: + +``` +/hd reload +``` + +The above example will produce a holograph that is too tall and the bottom part will be underground. You can raise it up by manually increasing the **Y** value stored in *location:*. As you raise it up, and save the file, then you use **/hd reload** to refresh. It's also easier to make changes directly to the database.yml file. Repeat until it looks right. + + +One word of warning about editing the yml file, is that it must be valid yml, if not, then it could fail to load, or it may reset to a default file. If you're unsure about the file being proper yml, make sure you save a backup before trying to reload the HolographicDisplays settings. You can also run it through an online yml validator to fix any issues. To find an online yml validator, search for: "online yaml lint" or "online yaml validator". + + + + + +
+ + diff --git a/docs/prison_docs_315_guide_scoreboard-r.md b/docs/prison_docs_315_guide_scoreboard-r.md new file mode 100644 index 000000000..363a05368 --- /dev/null +++ b/docs/prison_docs_315_guide_scoreboard-r.md @@ -0,0 +1,12 @@ + + + + + + +https://www.spigotmc.org/resources/scoreboard.14754/ + + + +Animated Scoreboard" (only 1.16.2 scoreboard I could find) + diff --git a/docs/prison_docs_626_configuring_worldguard_regions.md b/docs/prison_docs_626_configuring_worldguard_regions.md new file mode 100644 index 000000000..22ad4b8ae --- /dev/null +++ b/docs/prison_docs_626_configuring_worldguard_regions.md @@ -0,0 +1,611 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Configuring and Using WorldGuard with LuckPerms to Protect Mines + +This document explains how to setup WorldGuard to protect your mines and how to prevent players from accessing it when they don't have the correct permissions. It also explains how to setup the permissions in the Prison's **/ranks command add + +# Please READ This + +This document is a work in progress. + +Sorry for the state of this document. We found a flaw in the original document in which the protections did not work as expected. The original documents were using region templates to help reduce the amount of typing you would have to do per mine and rank. But it turns out that once that region template was tied to a permission, then it opened up all mines to be mined by any player that has access to any mine. In other words, if someone gained access to A they could mine in Z if they could get there. Therefore I'm in the process of redesigning the permissions to work properly (which they now do). + +But updating this document will take a little more time because I'm needing to rewrite everything a few times. + +In the mean time, I've indicated some content that no longer applies with an `x` as the first character on the line. + + +
+ + +# Dependencies + +* Vault - Interfaces with the Permission Plugins +* [Install WorldGuard and WorldEdit](prison_docs_026_setting_up_worldguard_worldedit.md) +* Install a Permissions Plugin that is compatible with Vault + * This guide uses LuckPerms. See [Setting up LuckPerms](prison_docs_020_setting_up_luckperms.md) for more information. + +
+ + + +# Setting up WorldGuard and WorldEdit: + +Install both WorldGuard and WorldEdit as required for your version of the server and Minecraft. Follow the general directions in the link above. If you require additional help, there should be plenty of good resources if you search for them. + +
+ + + +# In Game versus In Console + +**Important to understand:** When you add commands to the **/ranks command add** then they will be executed as if they were being entered through the console. Any references to players within a rank command must be replaced with `{player}` or `{player_uid}`. + +Please note that for WorldEdit and WorldGuard there is a slightly different way of entering commands **in game** versus **in console**. This is very important to understand, because most of the commands may be entered in game, but when you add some of these commands to the **/ranks command add** then they will be executed as if they were being entered through the console. + +When you are in game, the world you are in will be used as a default value in any command that requires a world parameter. When you are entering commands from the console, you must specify the world parameter. Failure to specify the world will prevent the command from running. This will cause problems during the running of the **/rankup** commands. + +The WorldGuard documentation says the following about the `-w` flag. + +``` +-w can be specified to run this command for a different world or from console +``` + +There are a number of WorldEdit and WorldGuard commands that cannot be ran from the console. For example, WorldEdit's //pos1 and //pos2 are used to set two points that can be used to define a WorldGuard region. The problem is that you cannot speicify the world with those commands, so therefore they cannot be used in any rank commands. + + +Please note **from console**. + +**Please Note about other Plugins:** Although this example is using WorldGuard from the console, which requires the use of the **-w** flag, other plugins may also require the use of special considerations when being ran from the console. If the in-game command does not work, then review that plugin's documentation to see if there is any special requirements to run from console. + + +
+ + + +# Naming conventions to Consider + + +Please note that whatever you choose to use for the region names or the permissions is up to you. It may be easier to understand what is what, if it has some kind of context such as a region named **mine_** when compared with **mine_area_**. Same with permissions. If they begin with **prison.mines.** you will easily understand it role versus a **prison.tp.**, or a permission for notifications, or even enchantments. + +Putting thought in to the naming of resources, such as regions and permissions, can help make managing your server easier, especially if you have someone joining your staff well after your server has been released. + + +
+ + + +# Enable Yourself - Op'ing and Wanding + +To simplify things, make sure you are **op**. You should also know how to **deop** yourself too. You can **deop** yourself in game too, just prefix the command with a slash. + +From console: + + op + deop + + +Then **in game**, give yourself a WorldEdit wand: + + //wand + +
+ + + +# Protecting the World from Players + +**Purpose:** This prevents players from breaking any blocks in the world. It also prevents mobs from spawning. + + +As op, protect the whole world with a passthrough flag set to deny. This will prevent building, PVP, and everything else. Basically, any action that “passthrough” all over defined regions, will be denied. The command with the **-w world** parameter has been added to the following list too. Use that version from console, the other without **-w world** in game. And where the name **world** is the actual name of your world. + +In game commands: + + /rg flag __global__ passthrough deny + /region flag __global__ mob-spawning deny + /gamerule doMobSpawning false + +Console commands: + + /rg flag -w __global__ passthrough deny + /region flag -w __global__ mob-spawning deny + /gamerule doMobSpawning false + + +Note that the **/gamerule doMobSpawning false** may also help prevent mobs from spawning, and may help reduce the overhead of WorldGuard from having to deny the spawning events. Kind of double protection. + +
+ + + +# Various LuckPerm Commands for Templates and Mines + +The WorldGuard region templates are covered below, but first you need to setup the groups within LuckPerm or the permissions on the child groups will not be tied back to the parent groups within LuckPerms. + + +For prison, we will use the prefix of `prison.mines` so we know what these groups and permissions are related to the mines. + + +LuckPerms commands to create a group, and how to have a child group inherit permissions from a parent. The first command should be used twice, first for the parent, then the child group. The the next command shows how to assign a parent group to a child group. + + /lp creategroup + /lp group parent add + + + +We **must** create a LuckPerms group for every mine. Only mine `a` and `b` are shown here, but create one for each mine. + + /lp creategroup prison.mines.a + /lp creategroup prison.mines.b + + + +To check to see if these groups are setup properly, you can inspect them with the following commands. + + /lp listgroups + /lp group prison.mines.a info + + +To grant permission to the players, you need to use the following since we need to add the player to the group. + + /lp user parent set + /lp user parent set prison.mines.a + + +And to now hook this up to prison, you do same command, dropping the leading slash, but with adding a rank command prefix and use the {player} placeholder (more on this later) + + + /ranks command add a lp user {player} parent set prison.mines.a + + +
+ + + + +# Unprotecting a Mine for its members - Required for all Mines + +**Purpose:** This will actually give members the ability to perform mining related tasks within the mine. They need to be able to break the blocks within the mine, and to pickup items, XP, and allows item drops. + + +This defines a WorldGuard region, and needs to be applied to all mines. + + +Select the same area of the mine with the WorldEdit **wand**, then use the following commands to define a mine. It will define a region with the mine’s name, and set the parent to mine_template, with the only member ever being the permission group **prison.mines.**. Never add a player to a WorldGuard region since it will get messy. Always use permission based groups and then add the player to that group. + + +In this example I have included an owner of this mine which is group owner. And added the group admin as a member so the admins will have full access to this mine, even if they do not personally have the player's rank to access this mine. The actual members you add are up to you, but these are just two examples that you should consider. + + + /region define prison_mine_ + /region addmember prison_mine_ g:prison.mines. + + + /region setpriority prison_mine_ 10 + + /region flag prison_mine_ block-break allow + /region flag prison_mine_ item-pickup allow + /region flag prison_mine_ exp-drops allow + /region flag prison_mine_ item-drop allow + + /region addowner prison_mine_ g:owner + /region addmember prison_mine_ g:admin + + +Set the *priority* to a value of 10 to take higher precedence of other lower regions that may overlap. + + +The following region setting for access and deny may *appear* to be useful, but don't use them. Explanations follow. **Do not use the following:** + + ~~/region flag prison_mine_ entry -g nonmembers deny~~ + + ~~/region flag prison_mine_ x allow~~ + ~~/region flag prison_mine_ entry-deny-message You must rank-up to access this mine.~~ + +**NOTE:** The use of `~~` above are invalid and are added since markdown documentation *usually* uses them as strike though, but that does not work with github markdown. Nonetheless, i've kept them there just to add emphasis that it's wrong. + +**NOTE:** + +It’s a bad idea to deny access to the mines through these regions. Such as with **-g nonmembers deny** on the **prison_mine_** regions. If the players doesn't have access to the mines, and they try to enter from the top, WorldGuard will continually prevent them from entering, or more specifically it will prevent them from falling in to the mine. This will basically keep them floating in the air which will trigger a fly event within anti-hacking tools. It will be far more professional to protect the area that contains the mine, thus you can protect it over the whole y-axis too. Players can also get caught in a rapid loop where WorldGuard is trying to kick them out of the mine when restricting just the mine; could possibly cause a lot of lag, depending upon how many event’s are being triggered. + + +
+ + + + + +# Protecting a Mine's Area - Required for all Mine Areas + +**Purpose:** To keep out all non-members from a mining area. The mining area, as in this context, is the area that immediately surrounds a mine, and generally non-members should not have access to it. + + +In general, it may be tempting to restrict access to the mine itself so non-members cannot mine it. But there is a serious problem with just protecting the mine, and that’s when non-members walk on top of the mine. They will fall in to the mine, as expected, but WorldGuard will try to keep them out, so they will be bumped back above the mine, thus triggering a “fly” event, or a “hover” event. This action may trigger anti-hacking software to auto kick them, or auto ban the players, or the players could get stuck, and it may even cause a lot of lag on the server too. + +This also happens really fast, in a very repeated action, so it could lock up the player so they cannot jump back out before they get banned. I do not know if this could contribute to server lag, but a lot of processing appears to be happening so it is possible. + +The suggested action is to create a new region around the mine and protect that from entry from non-members. This region can then be extended from y=0 to y=255 with the WorldEdit command `//expand vert``. If anyone does get past it, they still won’t be able to mine. + + +The primary purpose is to keep non-members out of the region. It will also prevent non-members from TP'ing in to the area too. It will also supply the player with an error message to inform them they don't have the rn + + +Select the an area around the mine with the WorldEdit **wand**. Only select a rectangle area around the mine, ignoring the **y** axis. Then use the following commands to define a mine. It will define a region with the mine’s name, and set the parent to mine_template, with the only member ever being the permission **g:prison.mines.**: + + +The command **//expand vert** will take your selection and extend the **y** to cover the whole vertical range in your region. This is why you don't have to be concerned with the *y* axis when defining your mine area regions. + + +Just like **prison_mine_** WorldGuard region, we need to add the **g:prison.mines.**. + + //expand vert + /region define prison_mine_area_ + /region addmember prison_mine_area_ g:prison.mines. + + + /region setpriority prison_mine_area_ 10 + /region flag prison_mine_area_ entry -g nonmembers deny + /region flag prison_mine_area_ entry-deny-message You must rank-up to access this mine. + + /region addowner prison_mine_area_ g:owner + /region addmember prison_mine_area_ g:admin + + +You would need to repeat these settings for each mine. + + +Notice we are using the same permission permission for both the mine and the mine area: **g:prison.mines.**. This keeps it simple by reducing the number of permissions we have to give the players. + + +Of course, just like **prison_mine_** region, we also give `owner` and `admin` access too. + + + +
+ + + + + +# Granting Access to a Mine and Removal of the access + +**Purpose:** From either the console, or from within game, manually grant a player access to a mine. + +To add a player to the mine regions is as simple as giving the user the permission associated with the mine region. + +It's important to understand that you never add a player as a direct member of a mine region, since if you have thousands of players, it will make your configuration files messy, and could add to lag dealing with such large data files per region. + +Instead, you add the players to a permission group that has access to use the mine regions. One way to look at this is that a permission is like a key, and you're giving players a copy of the key access the mines. With a slight twist on that analogy, since we're dealing with groups, everyone within that group shares the same key for the mine regions. + + +The correct way to add a player to a mine region. Indirectly by giving them access to the "keys". Or in other words, since we hooked up the LuckPerms group `g:prison.mines.` then all we need to do is add them to the group and they will have access to the proper regions. + + +Template: + + /lp user parent set true + + /lp user parent set g:prison.mines. true + + +It is important to know how to remove access from a player so they can be demoted or removed from an area that they should no longer access. + +Template: + + /lp user parent unset + + /lp user parent unset g:prison.mines. + + + +The **wrong** way to add a player to a mine region. Incorrectly by adding them as a direct member. + + /region addmember prison_mine_ + +This will result is potentially hundreds, or thousands, of members being added directly to the mine's region. + + + +And also, the wrong way to add a player to a LuckPerms group. This won't work correctly. + + /lp user permission set g:prison.mines. true + /lp user permission unset g:prison.mines. + + + + +
+ + + +# Adding Rank Commands to run when /rankup is Performed + +**Purpose:** Adds the permission to access the mine area and to mine within a mine, when a player successfully runs /rankup. + + +Based upon the above documentation, and from within game, we would use the following to *manually* give a player a permission: + + /lp user parent set g:prison.mines. true + + +For example, if you have a player named *AHappyPrisoner* And you have a mine named "a" you would use the following command: + + /lp user AHappyPrisoner parent set g:prison.mines.a true + + +To run the **a** rank commands when the player uses **/rankup**, the following is the command for **/ranks command add **: + + /ranks command add a lp user {player} parent set g:prison.mines.a true + + +Notice how the manually entered command is used with the **/ranks command add **? Just drop the leading slash and it should be good. + + +If you want to be able to **demote** a player from rank "b" back down to rank "a" you would need add the following **/ranks command add** to the rank **a** which removes access to the **b** mine. + + /ranks command add a lp user {player} parent unset g:prison.mines.b + + +So to recap, for every rank, ideally you should add the new perms for that rank, and remove the perms for the next higher rank so as to enable the proper functioning of **/ranks demote**. + +
+ + + +# Alternatives + +There are many ways to accomplish the same goals and that's what makes Minecraft so versatile and interesting to play. The Prison Plugin does not want to impose a specific way to do most things, since it may not be the ideal way for your sever. + +One of the primary focuses for this document has been protecting the area around your mine to prevent players who should not access the mine, from enter that region. One alternative to needing to protect a mine, would be to limit the access to the mine so it does not have to be protected. One simple way of accomplishing that, is to have the mines in a void world, and then each mine would be a separate island. Then all that would need to be protected, or controlled, would be the warping to that location. + + +
+ + + + + +
+ + +# WG LP Commands - Overview + +These are entered in a step by step process, intended for you to follow. + +These can, and probably will, be translated in to a runnable script within prison that will set everything up for you. Well, once you take care of the basics. + +You should be in game when you run these commands, otherwise you may have to specify the world name with almost all LuckPerm commands. When these are converted to scrips, the world parameter will be added. + +Some code chunks will have **In Game:** which is intended to run from within minecraft. The **Console:** is intended to be ran from the console, where there is no player, so you have to provide the "world". Note that if your world is not named "world" then you will have to change that. If there are any code chunks that are marked as "script" then those provide an example of what kind of placeholders would have to be used. + + +
+ + + +## WG LP Command - Global for whole world + +Run once. + + +In game: + + /rg flag __global__ passthrough deny + /region flag __global__ mob-spawning deny + /gamerule doMobSpawning false + + +Console: + + /rg flag -w world __global__ passthrough deny + /region flag -w world __global__ mob-spawning deny + /gamerule doMobSpawning false + + + +
+ + + +## WG LP Command - Setting up LuckPerm Groups + +Run once. You must know what your mines and ranks will be. Mines are just a simple letter like A through Z. The ranks generally have the same name. + + +For the sake of this document we will assume they will range from A to Z, but we will only create permissions and regions for only one mine, a. Be certain to duplicate this for all of the mines that you have. There could be donor mines too, but for now let's ignore those. + +For each mine, there will be + + /lp creategroup prison.mines.a + + /lp creategroup prison.mines.b + + ... + + /lp creategroup prison.mines.z + + + +## LuckPerms Adding Permissions to the Groups + +Run once for each mine/rank. + +There will be other permissions that players will require in order to use your server. You can add some of these permissions to the LuckPerm groups so when a player becomes a member of that group, then they will inherit the permissions that are in that group. + +A good example of these permissions are of course rank based, such as access to the mine's warp, or even other permissions that everybody should have. + +In this example we will give the group `prison.mines.a` the standard prison rank related permissions, but also other permissions that everyone should have. + + + /lp group prison.mines.a permission set prison.tp.a + /lp group prison.mines.a permission set prison.gui + /lp group prison.mines.a permission set prison.user + + + /lp group prison.mines.a permission set warp + /lp group prison.mines.a permission set warp.list + /lp group prison.mines.a permission set warp.a + + +You can also add in a lot of EsentialX's permissions to fine tune what your players can do. A nice listing of permissions can be found here: https://essinfo.xeya.me/permissions.html + +Since rank A will always be a permission group all your players have, you can use this group as a container for those permissions. + +Then all other ranks would only need what is required of the new ranks. Such as: + + /lp group prison.mines.b permission set prison.tp.b + /lp group prison.mines.b permission set warp.b + + +
+ + + +## Creating the Mine's WorldGuard Region + + +You can either use the worldEdit wand to select what you want to set as a region, or you can use other WorldEdit features to set them. + + +These are a WorldEdit method that can be used in game if you know the x, y, z coordinate. This will not work with scripting because you cannot specify the world to apply it to. + + + //pos1 x, y, z + //pos2 x, y, z + + +Once you have a WorldEdit selection then you can create a WorldGuard region. + + /region define prison_mine_a + /region setpriority prison_mine_a 10 + + /region flag prison_mine_a block-break -g members allow + + /region flag prison_mine_a item-pickup -g members allow + /region flag prison_mine_a exp-drops -g members allow + /region flag prison_mine_a item-drop -g members allow + + /region addowner prison_mine_a g:owner + /region addmember prison_mine_a g:admin + + /region addmember prison_mine_a g:prison.mines.a + + +Repeat the same for mine b. + + + +
+ + +## Creating the Mine Area WorldGuard Region + +The mine area is an area that surrounds the mine to protect the area from players who should not have access. This area should be at least 5 blocks larger in the X and Z axis than the mine, so as to prevent non-member players from being able to attempt mining. + +You need to select the area like the prior region and then define it with the following commands. The following `//pos1` and `//pos2` is just an example of making the selection. The `//expand vert` is required (strongly suggested) to ensure the region extends from the lowest to the highest blocks. + + //pos1 x, y, z + //pos2 x, y, z + + + //expand vert + + /region define prison_mines_area_a + /region setpriority prison_mines_area_a 10 + /region flag prison_mines_area_a entry -g nonmembers deny + /region flag prison_mines_area_a entry-deny-message You must rank-up to access this mine. + + /region addowner prison_mines_area_a g:owner + /region addmember prison_mines_area_a g:admin + + /region addmember prison_mines_area_a g:prison.mines.a + + +Please notice that we have defined two WorldGuard regions: prison_mines_a and prison_mines_area_a. But for both of them, we've assigned the LuckPerms group g:prison.mines.a as members. This means, all we need to do is add the player to that LuckPerms group and they will have access to both the mine_area and also the mine. + + + +
+ + +# Adding the Prison Rank Commands + + +So finally for our example of setting up mines a and b, we now need to add the Rank Commands to active the permission for both. + +For rank a: + + /ranks command add a lp user {player} parent set g:prison.mines.a true + /ranks command add a lp user {player} parent unset g:prison.mines.b + + +For rank b: + + /ranks command add b lp user {player} parent set g:prison.mines.b true + /ranks command add b lp user {player} parent unset g:prison.mines.c + + +And that's it! Just repeat for all your other mines. + + + + + +# Other Commands That May Be Important: + + /region redefine mine_ + /region removeowner prison_mine_ + /region removemember prison_mine_ + + + +Set’s the WorldEdit selection to the dimensions of the given mine: + + /region select prison_mine_ + /region select prison_mine_area_ + + /region info prison_mine_ + /region info prison_mine_area_ + + /region list + /region list -w world + /region list -p + + +Some LuckPerm commands that may be useful. + + /lp group prison.mines. listmembers + /lp user group add + + /lp listgroups + /lp group prison.mines.a info + + /lp user parent set + + /lp group parent add + + + + +# WorldGuard And LuckPerms Info + +https://worldguard.enginehub.org/en/latest/regions/global-region/ + +https://worldguard.enginehub.org/en/latest/regions/flags/ + +https://worldedit.enginehub.org/en/latest/usage/regions/selections/ + +https://bukkit.org/threads/how-to-use-the-entry-group-flag-in-worldguard.124066/ + + +https://github.com/lucko/LuckPerms/wiki/Command-Usage + + +
+ + diff --git a/docs/prison_docs_628_configuring_private_mines.md b/docs/prison_docs_628_configuring_private_mines.md new file mode 100644 index 000000000..1440f41c2 --- /dev/null +++ b/docs/prison_docs_628_configuring_private_mines.md @@ -0,0 +1,133 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Setting up Private Mines + +This document explains how to setup private mines that are not accessible to +other players. This document provides an *idea*, but it's up to your to +figure out the details to make it all work. + + +This applies to both personal mines (one player) and also special groups such +as donors or clans or other in-game groups. + + +This process will be a very manual process with a lot of manual steps that +must be repeated over and over again. + +
+ + + +# Dependencies + +* Vault - Interfaces with the Permission Plugins +* [Install WorldGuard and WorldEdit](prison_docs_026_setting_up_worldguard_worldedit.md) Understanding this document is critical for getting things to work. +* Install a Permissions Plugin that is compatible with Vault +* This guide uses LuckPerms. See [Setting up LuckPerms](prison_docs_020_setting_up_luckperms.md) for more information. + +It is very important to understand how to use WorldGuard and LuckPerms with Prison. The +above document explains one way to do that, and it's something that will be expanded +upon in this document. + +
+ + +# Performance Considerations + +As more mines are added to your server, they will have an impact on performance overall. +Here are some possible concerns if you are adding hundreds of private mines. + +* The reset process of replacing blocks is time consuming and processing intensive. The more mines that exist, the more frequent resets will run. Having hundreds of mines will ensure more are resetting every minute, and you could have multiple that try to reset at the same time. +* Mine resets cannot be done asynchronously. Asynchronous replacement of blocks could cause corruption of the world. Even asynchronous reading of block types can corrupt the world IF there are entities in chunks that are loaded to check the blocks. Entities include mobs and even items thrown to the ground, or drops from mining. The point is that mine resets can be expensive. +* Prison "tries" to do as much as possible to minimize the load on the server when doing a mine reset. For example performing precalculations in an asynchornous thread before attempting to replace the blocks. Also it pages the block placements to allow other processes to run on the server which may increase the reset time, but it should minimize impact on TPS when many events are going on at the same time. +* Many mines will require more memory on your server. The amount will be small, but more mines will require more memory. Prison even with hundreds of mines, should not consume a ton of memory, but it will consume more than just a dozen mines. +* Having many mines will slow down the searching for the "right" mine. There are some processes that have to locate which mine is being "used". Unfortunately, the only way to do so, is to go through each mine one at a time looking for a "hit". If you have hundreds of mines and the "hit" is on the last one in the list, then the dealy may be slightly longer than if it "hits" on the first item. + + + +
+ + + +# Mine Names, Permission Names, and Region Names + +You need to figure out how you want to name everything. This includes the name of +the mines, the permissions, and regions. Their names are overall +arbitrary, but they need to make sense to you. There may be a large number that are +needed so it would make sense to use an incrementing number. + +For the purpose of this document, and to keep the mine names simple, +let's name the mines as follows. This will allow for up to 10,000 mines. +* mine0001 +* mine0002 +* mine0003 +* mine0004 + + +Examples of what will be used in this document: +* prison.mines.private.mine0001 +* prison.mines.private.mine0002 +* prison.mines.private.mine0003 +* prison.mines.private.mine0004 + + +Permissions are critical in that it allows you to associate a player with a mine. +When the mine is properly configured with WorldGuard, it will prevent other players +from being able to access it. + + +Region names should follow the same pattern as provided in the WorldGuard documentation. +* mine_mine0001 +* mine_mine0002 +* mine_mine0003 +* mine_mine0004 + + +In the WorldGuard documentation above, the region names used were based upon + +
+ + + +# Creating Mines Using Schematic Files + +If you will provide personal mines, that implies you will have to have a lot of +mines to sell. You can build them individually, but it may be worth figuring out how +to use WorldEdit to save a mine as a schematic File. Once saved as a schematic file, you can +then copy and paste it in your world many, many times. If you need more information on +how to do this, please google: **worldedit using schematics**. + +
+ + +# Hooking it up to BuyCraft or Enjin + +Whatever your store front you use, to provide the players with access and ownership to their +private mines, all you would have to do is provide the player with the permission that +was used to configure that mine. It would be as simple as that. Once the player has +the permission, everything should work fine. + +If you are naming the permission in a sequential order, see above, then the store +fronts may be able to "sell" a range of permissions. Otherwise you would have to +configure each one independently. + +
+ + +# Another Idea on how to Setup Private Mines + +There may be a way to setup a plot world to help with this. +A plot world plugin could generate the repeating patterns of the mines, +and take care of some of the management of them, but it would have to be a manual +process to hook them up to the prison plugin. Such an exercise is far beyond +the scope of this document, or the prison plugin being able to provide an automation +process to manage all of them for you. Good luck, you're on your own +in trying to figure this out. + + + +
+ + diff --git a/docs/prison_docs_630_configuring_warps.md b/docs/prison_docs_630_configuring_warps.md new file mode 100644 index 000000000..11dbefc41 --- /dev/null +++ b/docs/prison_docs_630_configuring_warps.md @@ -0,0 +1,127 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Configuring EssentialsX Warps for Mines + +*Note that this is a work in progress and does not include all steps yet.** + +This document explains how to setup the EssentialsX Warps so players can warp when to the mines that they should have access to. + +Please note that within Prison there is the **/mines tp ** command, but that is intended for use by admins only. There is no way to limit players to have access to specific +mines. Anyone who is giver access to that command has access to tp to any mine. + +
+ + + +# Dependencies + +* EssentialsX - for the */warps* and */warp* commands. + +
+ + + +# Configuring EssentialsX + +Download and install EssentialsX. + + +In the `plugins/Essentials/config.yml` is this: + +``` +// Set this true to enable permission per warp. +per-warp-permission: false +``` + +Set that to `true`. + + +Then restart the server. + +
+ + + +# Permissions for Warps + + +* **essentials.warp** - Allow access to the /warp command. +* **essentials.warp.list** - Specifies whether you can view warp list with /warp. +* **essentials.warps.[warpname]** - If you have per-warp-permission set to true in the config.yml then you can limit what warps players can use. This also controls what players would see with /warp. + +In the EssentialsX config file, set: + +``` +per-warp-permission: true +``` + +Then use the following permissions. The first two are required, but the last three are examples based upon the three warp names: a, b, and c. These three warp names happen to be the same as the mine's names, but they don't have to be. There is no direct connection between warp names and mine names. And there are no direct connections between warp permissions and mine permissions. +``` +essentials.warp +essentials.warp.list +essentials.warps.a +essentials.warps.b +essentials.warps.c +``` + + +# How to set the warps + + +First you need to set up each warp. Use the `/setwarp [mineName]` command to accomplish that. + + +If you want it to be exactly the same as your prison mine warps (ie... `/mines tp [mineName]`), first tp to that location and do not move or change where you are looking. Next issue the EssentialsX's command `/setwarp [mineName]` to set the warp location. + + +Then add them to the rank commands. Using LuckPerms from within game will look something like this: + +``` +/lp user [playerName] permission set essentials.warps.a true` + +``` + +And adding them to the ranks commands but instead of specifying the actual player name, use the `{player}` placeholder and Prison will substitute the player's name at runtime: + +``` +/ranks command add a lp user {player} permission set essentials.warps.a true +/ranks command add b lp user {player} permission set essentials.warps.b true +/ranks command add c lp user {player} permission set essentials.warps.c true +``` + +Note that with LuckPerms, the add user permission has an optional Boolean value of true at the end. If omitted, it defaults to true, but it is included here since most documentation includes it. + + +# Using PEX permissions with Warps + +If you're using PEX, the rank commands are similar, but using the pex notation. + +From within game the pex command will look something like this: + +``` +/pex user [playerName] add essentials.warps.a +``` + +And adding them to the ranks commands: + +``` +/ranks command add a pex user {player} add essentials.warps.a +/ranks command add b pex user {player} add essentials.warps.b +/ranks command add c pex user {player} add essentials.warps.c +``` + + + +# Links + +[http://ess.khhq.net/wiki/Command_Reference](http://ess.khhq.net/wiki/Command_Reference) + +[http://wiki.mc-ess.net/doc/](http://wiki.mc-ess.net/doc/) + +[https://luckperms.net/wiki/Permission-Commands](https://luckperms.net/wiki/Permission-Commands) + + +
+ diff --git a/docs/prison_docs_810_faq_other_plugins.md b/docs/prison_docs_810_faq_other_plugins.md new file mode 100644 index 000000000..dc2a5c6cb --- /dev/null +++ b/docs/prison_docs_810_faq_other_plugins.md @@ -0,0 +1,81 @@ + + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## FAQ - Other Plugins + +This document covers some common issues that pertain to other plugins, but may not actually be related to Prison. + +
+ +# CMI - Used for the Economy within Prison + +**Symptoms of Issues** + +Symptoms of issues with CMI is when it appears like Vault is using CMI has successfully loaded, but yet the Ranks module fails to load. + + +**Overview of CMI** + +CMI Is a premium plugin and costs about 15.00 Euros. It is a very extensive plugin that covers many aspects of what you could possibly do with a server. The developers claim it is a worthy replacement for EssentialsX. The primary aspects CMI with what Prison needs to use is their economy and permissions. +[CMI on spigotmc.org](https://www.spigotmc.org/resources/cmi-270-commands-insane-kits-portals-essentials-economy-mysql-sqlite-much-more.3742) + + +**How to Resolve the Issues** + +In order to use CMI with prison, it is required that you use Vault. Prison does not have any direct integrations to CMI. Since it is a premium plugin, the CMI integration for Vault is not publicly available in the original Vault plugin. Instead CMI offers their own modified version of Vault that has been recompiled with their hooks added, or an injector that will add it to the original Vault plugin. + + +On their spigotmc.org resource page, they identify there are two way to enable a working version of Vault: +1. Install and use their modified version of Vault (recommended) +2. Or use the CMIEInjector to modify an original Vault (not recommended) + + +**Common Problems** + +1. Make sure you are using the CMI modified version of Vault +2. The CMIEInjector has problems, or will cause problems. So don't use it. +3. Do not use both the CMI modified version of Vault and the CMIEInjector. They were never intended to be used together and they will cause problems with prison. + + +
+ + +# Prison Won't Load - No Modules Loaded - LuckPerms is the Cause + +**Symptoms of Issues** + +Symptoms of issues is that Prison fails to start up and load properly. The biggest symptom is that there are no active modules and most of the prison commands fail to work. + + +**Cause** + +This is caused by LuckPerms. More specifically, trying to use LuckPerms v5.0.x with Prison v3.2.0 or older. LuckPerms v5.0.x is not supported with older version of Prison. + + +**How to Resolve the Issues** + +There are two major ways to resolve this issue. + +1. Down grade to LuckPerms v4.4.1 for use with Prison v3.2.0 and older. + +2. Upgrade Prison to v3.2.1 (or the pre-release version of v3.2.1) or newer. Prison v3.2.1 supports both the newer LuckPerms v5.0.x and the older LuckPerms v4.4.1 and older. + +**Suggested action:** Upgrade Prison to v3.2.1 or newer and use the latest version of LuckPerms. + + +**Background** + +Basically the fault of this issues lies with LuckPerms in using the same registered +plugin name with the bukkit/Spigot Plugin Manager for v5.0.x. By using the same registered +plugin name and the same class name, but an altogether different package name, that broke +the standard way of using the plugin manager to get an instance of the plugin. This would +have caused any plugin to generate a Class Not Found exception. + +They shouldn't have used the same signatures for v5.0.x as they did with v4.x. +If they would have chosen a new signature, such as LuckPerms5, then there would have been no issues since the plugin manager would not falsely report that the old version of LuckPerms +is available and will not try to cast the new packages in to the old variables. + + +
diff --git a/docs/prison_docs_880_faq_misc_01.md b/docs/prison_docs_880_faq_misc_01.md new file mode 100644 index 000000000..fb729bf93 --- /dev/null +++ b/docs/prison_docs_880_faq_misc_01.md @@ -0,0 +1,107 @@ + + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## FAQ - Miscellaneous Questions + +This document covers some miscellaneous questions pertain to Prison in general. + +
+ +# Paper - Is it supported? + +**Quick Q & A:** Does it work? Yes it should work, but Paper is not supported. + +Although Paper servers are not officially supported, there is a strong interest in making sure Prison will work with Paper. Theoretically since Paper is a direct fork from Spigot, it should always be 100% compatible. So if issues are found with Paper, efforts will be made to fix those issues. + +The primary reason why paper servers are not directly supported has to do with the number of minecraft versions that are supported, and the number of test servers that requires to perform testing upon. By adding in paper as an officially supported platform, that would increase the complexities of testing. Therefore, buy not supporting paper directly, it simplifies the testing phase of development, but at the risk that there may be an ever slight chance of something not working correctly with paper. + +
+ + +# Configuring the Correct Currency Symbol for Prison + + +**Symptoms of Issues** + +The wrong currency symbol is used when showing monetary values. + + +**How to Resolve the Issues** + +Prison can use a standard national currency symbol. It cannot use a custom symbol +for a custom currency. +This FAQ explains how to change the national currency symbol. + + +The current prison plugin is using java internals to set the currency symbol. +As such, currencies within prison may show the wrong currency symbol. + +For example, in RankUpCommand it is using this reference: + + RankUtil.doubleToDollarString(result.rank.cost)); + +which is using this: + + public static String doubleToDollarString(double val) { + return NumberFormat.getCurrencyInstance().format(val); + } + + +If the currency that is shown is not what is configured on the server, +then you currently MUST change the java startup variables to set it +the language and location that you are needing to use. Otherwise +it will be pulling from the file system, which may not match your +in game settings. + + -Duser.language=en -Duser.country=US -Duser.variant=US + +In context the server startup may now look like this: + + java -Xms2g -Xmx8g -Duser.language=en -Duser.country=US -Duser.variant=US -jar spigot-1.13.2.jar + +In the future, Prison may need to switch this over to use either a language config in +a config file somewhere, or use what is defined, or set, within the +currency plugin, such as vault, or EssentialsX. + + +
+ + +# Information on Groupmanager and using it with Rank Commands + + +**Information provided by ChrisGames** + +**groupmanager** + +So what i was trying to do was do this command: + + manuaddp {player} essentials.warps.a + +manuaddp is for 1 specific player, then just the node for warping to a mine +when you give a rank or a specific player a permission you need to do + + /manselect [Worldname] + +To get it to work with the prison ranks you need to have it do manselect (world) before it does the other command. + +if the manuaddp command is higher in the config than the manselect (world) it will not work. + +. + +The commands, as ran from the console, for a player named *TestPlayer* in the world named *PrisonWorld*: + + manselect PrisonWorld + manuaddp TestPlayer essentials.warps.a + + +These are the same commands, but are in the format that is needed to be added to the Prison Rank commands for a rank named "a". Please note that the order in which these commands are entered are the way they will be ran upon rankup. + + ranks command add a manselect PrisonWorld + ranks command add a manuaddp {player} essentials.warps.a + + + +
diff --git a/gradle.properties b/gradle.properties index 636353e94..88361bce4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,9 @@ +## define the global gradle version: +## # This is actually the "correct" place to define the version for the project. +## # Used within build.gradle with ${project.version}. +## # Can be overridden on the command line: gradle -Pversion=3.2.1-alpha.3 +version=3.2.1-alpha.20 ## org.gradle.warning.mode=(all,none,summary) org.gradle.warning.mode=all diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a95168..5c2d1cf01 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae45383b6..0ebb3108e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d51..8e25e6c19 100644 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index f9553162f..9618d8d96 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/knownissues_v3.2.x.md b/knownissues_v3.2.x.md new file mode 100644 index 000000000..7b22aff0a --- /dev/null +++ b/knownissues_v3.2.x.md @@ -0,0 +1,702 @@ +[Prison Documents - Table of Contents](docs/prison_docs_000_toc.md) + +# Prison Known Issues and To Do's for v3.2.x + +This document is intended to keep track of known issues and also provide for +a short list of To Do's. This list is intended to help work through known +issues, and/or to serve as items that should be added, or fixed. + + +# To Do Items - During Beta v3.2.1 + + +* **Rename Mines** +Been a few requests to be able to rename mines. Since so much can go wrong with manually changing the files, this should be a reasonable new feature added before beta. + + +* **New Block Model - Implement in parallel** +Implement and have a fully functional new block handling mechanism that operate in complete parallel to the old method. This way admins can turn it on when they want to use it, otherwise their server will continue to use the old code. + + 1. Done: Create core classes for new block types. String based so 100% flexible. Names based upon XMaterial enums so easy mapping to XMaterial then to system version dependency. + + 2. Done: Available blocks based upon XMateral and spigot version they are running. Create the initial list to be used for block searches. + + 3. Done: Needs Review: Disable MineData.getBlocks() and use that to write all the main parallel code. Reenable. + + 4. Disable BlockType and write parallel code. Reenable. + + 5. Hookup block search. + + + +* **Rework commands within the spigot module so all user facing commands are routed through Prison's Command Interface** + Blue should work on this. + + + + + + * **Problem with rank removal from Ladders** +Create a new rank to the default ladder. Add a player to it. Then remove the rank from the ladder. + +The ladder no longer contains the rank. But the player is still associated with the rank, but yet ranks cannot contain players if they are not on a ladder. The commands expect a valid ladder name. Also there does not appear to be any checks and balances when ranks are moved from one ladder to the other since ranks have no idea what ladder they are in. + +This could cause major corruption if moving ranks between ladders, removing ranks from a ladder, and players being associated with those ranks. + +Create a "void" ladder and prevent ladders from being named: default, void, and prestige. When a rank is removed from a ladder, place it in to none and update all players that use that rank so the rank is still valid. Do not include void ladders in placeholders. + + + +* **Update config.yml when changes are detected** +Preserving the current settings, replace the out of date config.yml file with the latest that is stored within the jar. Updating the settings as it goes. + + +* **Ladder commands - global for all ranks in that ladder** +Add new placeholders for ladder commands to be able to have generic ladder commands that will apply and be ran for all ranks. May be able to eliminate the need for most rank commands. + + +* **Rank Commands - Edit and delete** +Add line numbers and enable the ability to edit and delete by line number. + + + + +GABRYCAToday at 3:46 PM +3.2.1 -> Nothing to fix + +3.3.0 before -> +Bug: Bug fixes which will be reported by the user, if nothing will be reported in a month, then some of the next planned features will be integrated within this: +New: +- Add Ranks, Mines, Prestiges and RankupCommands presets +- Add a walktrough GUI and Command to set up Prison presets on first start in the server +- , , , placeholders for RankupCommands + +3.3.0 -> +New: +- Add close GUI Button +- Next page button for ranks, ladders, rankupcommands and mines +- Eventually add some features which are missing in the GUIs +- Enhanced GUI listeners and management system (I'll need this for the next feature) +- Tracker and "cache" of the previous open GUI (will be deleted if there aren't Open GUIs... to sort this out) +- Go Back button to go to the previous GUI + + + + + +Enable zero block counts for parent mines. +if 100% block type of IGNORE, then after reset do an full mine air count so zero block reset works. :) + + + + +[Plugin Prison - To be able to manage at which layer such or such block appears] +- For example if I create a mine of 10 high, with iron_ore, gold_ore, emerald_ore,... I would like choose between which layers a particular bloc appears. + + "blocks": [ + "STONE-20.0-0", 0 when not configured = all layers + "IRON_ORE-25.0-0,5", 0,5 for all layers up to the layer 5 + "GOLD_ORE-25.0-4,7", 4,7 for layers between layer 4 and 7 + "EMERALD_ORE-25.0-8,0" 8,0 for all layers after layer 8 (in example : so up to layer 10) + ], + + + + +# To Do Items - Post v3.2.1 + + +

Major tasks

+ + + +* **Integration in to WordEdit & WorldGuard** +Largest problem with providing a solution is that prison supports 1.8 through 1.17!! +WE and WG has a strict policy that they only support the current version of spigot/mc. They do not support older versions, but their older versions that worked with older versions still exists to be used. +The major problem is that you cannot run scripts from console since WG and WE require to know what world the actions are in, and they get that from the active player. +There is an API that can be used, but they warn that there are changes to the API from version to version, so it may not be possible to provide the same API calls from 1.8 through 1.17. Will not know until we try to use them? + +Just had this idea... What if for these main commands, for configuration purposes, the player would have to be in game. When they run these commands we TP the admin to the mines' spawn point, then run them through the console on behalf of that player? That way WE and WG could get the world from the on line player. But this will not work for mine resets... :( + + + + + +``` + Note: May be spread out over multiple releases. + 1. New Block handling if not fully apart of v3.2.1. + 2. New Multi-language support. + 3. New Wizard Configuration support. + A. Basic Prison functions. + B. Basic Mine behaviors + C. Basic Rank and Ladder behaviors + D. Basic prestige behaviors + 4.Creation of new scripting language for automation tied to wizards + A. When creating a mine perform scripted actions to configure each one + B. When creating a rank perform scripted actions to configure each rank + C. Create the permission group within the permission plugin selected + D. Add perms for that permission group, including warp support (prison/warps) + E. Auto generate rank commands to add and remove group perms from each rank +``` + +. + + +

Higher Priority TO DO Items

+ + +* **Need to figure out how to better handle the Spigot commands that bypass the prison command interface** +They are inconsistent and non-standard to prison. They do not support the `help` keyword, nor do they show what perms they need. + + +* **Refactor GUI?** +Performance updates and validation to ensure computationally expensive processes are not running constantly. + + + +* **Possible Issue with delete Mine** +Delete a mine and try to add a new one right away with the same name. A user said it would not allow them, that the mine already exists. +* Tested on a few versions and could not reproduce. Keeping this entry here to retest in the near future. + + +

To consider - Lower priority

+ + + +* **When creating a new mine, register that mine with the placeholders** +Might be easier to just reregister all mines? Not sure if that will work? +Right now, if a mine is added, in order for it to show up in the placeholders, you would have to restart the server so all the placeholders are reregistered. + * * maybe just automatically run reset when vital elements change? * * + + + +* **Add WorldGuard Support** +This is a work in progress. The biggest problem is that although older versions exist, they only support the latest version, and they have removed the older documentation and maven repositories. So access is highly limited to functional resources for performing builds. The current documentation identifies that major functional changes exist even between minor versions. So one API for v6.1 (supports mc 1.7 through 1.8 only) may not work WG v6.2.2 (supports only mc 1.12), and may not even be close to support v7.0.2 (supports 1.14 through 1.15). + +Not knowing what "range" of WorldGuard behaviors are supported through the API, or if they are even the same, the initial range of supported integrations for WorldGuard will be very limited until it can be fully tested to see where those limits are. + +List of features that could be nice to have, ordered from easiest to most difficult to implement, with the possibility of never being able to do any of these: + * Using prison's mine boundaries, set a WorldEdit selection to those same dimensions so the user does not have to reselect the same area. + * Using prison's mine boundaries, define, or resize, a given WorldGuard region. Using a Prison defined naming schema. + * If a prison is deleted, then remove the Prison's WorldGuard region. + * Auto define the WorldGuard global templates and permissions, then auto define, update, and remove all mine related regions as the mines are added, changed, or removed. * Detect if there is an out of sync situation between prison and worldGuard. + + +* **Get started on new Multi-Language Support** +This is put on hold for the v3.2.2 release. + + + +* **Upon startup validate all Blocks that are defined in the mines** + +Upon loading prison, validate that all blocks that are defined within each mine are actually valid for that version of minecraft. This will be important in that it may help eliminate possible errors when the server owner upgrades the server, or other plugins. Also it will be very helpful when Prison's block handling is enhanced since it will be a tool used to verify and maybe even fix incorrect block types. + + + +* **Add to the Command annotations an option of *async* to run that command asynchronously** +Check to see if the commands are being ran sync or async. Add a parameter support so +commands that can be ran async. + + +* **Add prison Placeholders to papi's website for downloads** +Prison is already using papi (PlaceholderAPI). But see if we can add prison to the supported +plugis for papi's cloud. Should time this with the v3.2.1 release so there are more +placeholder to use. + +DeadlyKill: This what he needs ita +Papi +Hook Plugin +They have those expansions which hook other plug-ins + +https://github.com/help-chat/PlaceholderAPI/wiki/Placeholders + + + +* **Improve some of the display pages for ranks and ladders*** +Can add more information to the listings so they have more value. + + +* **Tab Completion*** +Hook up tab completion on the prison commands. + + +* **Better logging of major events*** +Need to log major events such as rankups, both to the server log, and also +to the community. Server logs for these events, especially when money is +involved, is important. + + + + +* **Possible new feature: Track how many blocks a player mines, including types*** +Stats could be interesting over time, and could also be used for in game +bonuses and rewards and incentives. + + +* **Redesign the save files to eliminate the magic numbers*** +Most of the save files within prison, for players, ranks, and ladders, are +using magic numbers which is highly prone to errors and is considered +very dangerous. Also prison would benefit from a redesign in file layout +to improve efficiencies in loading and saving, not to mention greatly reduce +the complexities within the actual prison code, which in turn will help +eliminate possible bugs too and give tighter code. + + + +* **Notification that inventory is full*** +In progress! + + +* **Built in selling system*** + +* **Custom Mine reset messages per mine*** + + +* **Enhancement: Multi-Language Support*** + +Offers for translation: + Italian : Gabryca + Greek : NerdTastic + German: DeadlyKill ?? Did not ask, but a possibility? + French: LeBonnetRouge + Portuguese: 1Pedro ? + + +* **Auto-Config of other Prison Related Plugins*** + +GABRYCA: [Idea] + +Not sure if it's possible, but an hook with worldguard on mine making, like a region where the mine region's made automatically with basic permissions'd be neat + +RoyalBlueRanger: + +Gabryca... yes. I think a "clean" integration in to WorldGuard regions and WorldEdit selection tools would be a great improvement. I've been wanting to do this from day one actually. But I'm not 100% sure how much "automation" can be done here. + +I guess if the first thing you do when you activate prison, is to "confirm" that you want prison that you want to "link" prison to WorldGuard and your Permission plugin (assuming supported ones exist), then auto generate all of the "step" that are outlined in the document that I create on github for WorldGuard. That would really help everyone setup their basic servers. + +A lot of work there.... but it would be VERY beneficial for sure. It could be the foundation of an automated setup where prison "proactively" works with other plugins to help build and configure the server. + +Areas of possibilities in "Auto-Configure Prison Environment": + * WorldGuard - Regions for mines + * WorldEdit - Selection tools + * LuckPerms - Perms + * EssentialsX - Warps tied to mine warps + * QuickSell - Place a [Sell All} sign at the mine warp + * Citizens - NPC generated to replace the QuckSell Sign + * CitizensCMD - Links the Citizen NPC to the QuickSell "tiered" pricing (requires Citizens) + +I think those few integrations could really provide a huge bootstrap to getting the basics of a prison server up and running. + + + +# Features recently added: + + + +* **Done: Prestige references** +Add the prestige command to the /prison version page; rework the commands layout. +Add the documentation for prestige. Gabryca provided the base docs. + + +* **Done: Prestige Fixes** +The existing prestige command is GUI only. That may be an issue with 1.8.x. When testing, it appears to work, but may need an non-gui way to rank up. + +The /prestige command does not show the cost. The cost must be shown, along with a warning that the player's balance will be set to zero. + +Since the player loses their balance, which may be far more than what the prestige may cost, there really must be a confirmation added confirming the cost, and the player's balance. It should also identify that the excess amount of money the player will lose. + +Must change the cancel button lore. It's grammatically incorrect with a double negative. Should only be: "Cancel Prestige". Likewise the confirmation button should just be "Confirm Prestige" since the colon is grammatically ambiguous. + + + +**Done! Parameterize Sort order for /mines list.** +Default sort order should probably be alphabetical. +Currently it is alphabetical with most active mines since restart at the top of the list, based upon blocks mined. + + + +* **Done!! Add GUI support for v1.8.x** + Might be able to add GUI support for 1.8.x with a few simple lines of code? + + + +* **Done: Autosell feature** +Be able to allow the players to toggle it on or off. Will not sell what is in inventory, but only the blocks that they are mining. Use /autosell . +What would be nice is if you could look up all permissions that start with prison.sellall.1.05 where the number at the end is the multiplier. So you setup the multiplier based upon permissions and no other configs. Then take all the multipliers and multiply them together. So if they have a 1.05 (donor), 1.03 (special limited time), 0.97 (prestige penalty... more difficult the higher you go) would have a total of 1.049. So basically different ranks, or even different tools could increase or decrease payout. + + + + +* **FIXED: BUG? Able to add more than 100% in one mine?** +User reported having three copies of everything which totaled more than 300%. +Used the /mines block add function? + + + +* **DONE: Add player names to the player file** + Have no idea who is who in the player files. + Make it an array of a new object, player name that has name and timestamp. + When the file is loaded and the player is online, check name, and if not recorded, then add it. + + + +* **DONE: Add permissions on notifications for mine resets ** +Only send a message to the player if they have the permission set, and follow the other notification settings too. +Add permission `mines.notification.[mineName]` and have each mine have a boolean field to indicate that it should check for permissions on the players prior to sending the message. + + +* **DONE: Broadcast all rankups** +Option to disable it in config settings. + + + +* **DONE: Placeholders - Reregister** +Add a new command `/prison placeholders reload` that will reregister all placeholders. I don't think there is a clean way to unload the registrations, but just going through registration may be good enough. Deleted, or obsolete, placeholders may still be registered, but at least the new ones will get registered too. + + +* **DONE: Placeholders - Add placeholder counts to startup** +Counts on how many placeholders are generate and registered. + + +* **DONE: Placeholders - Add new placeholders** +prison_rankup_cost_remaining +Such that `prison_mines_*_minename` will also be mapped to `prison_mines_*_player` and will only report the values when a player is in a mine. +I think that will be super cool, since you could put some current mine stats in your placeholder such as `prison_mines_timeleft_formatted_player` `prison_mines_remaining_player` and even `prison_mines_player_count_player`. + +NOTE this was added: the new ones are playermines and there are a total of 24 new ones. Will work great with scoreboards. + + + + + * **Not an issue: Prison Wand breaks blocks** + Should probably monitor block break events and cancel breakage with wand. + This was caused by a conflict with a scoreboard plugin that advertised as supporting 1.16.2, but broke a few different plugins. + + + +* **DONE! Complete the new Mines Reset Paging** + Holding up release v3.2.1. + + +* **DONE: Enhance AutoManager** +Make auto manager more like vanilla on the drops and support silking. Try to make more consistent between versions. +This is a lot closer to the vanilla. May need to revisit in the future to enhance even more. + + +* **DONE: Add permissions to the AutoManger*** +Add permission checking to AutoManager to allow a per-mine selection of which mines to enable it in or to tie it to some rank or donor rank. Could also put lore checking in place so tools could be enchanted to perform these functions too. Could have it so there is a percent chance related to the permission or lore. + + +* **DONE: Prestige and Rebirth** + + + +* **Already Exists: Update the Prison command handlers to support help context** +-= Note I was unaware if you use the command `help` as the only parameter it shows the help =- +This would show the parameter details for the commands. Right now the annotations that defines the command parameters has descriptions for the command and the parameters, but they are not displayed anywhere useful. + + + +* **DONE: Add support for player use of /mines tp** + +Could be done through other perms and then checking to see if they have access t + +that mine. Perm: mines.tp (all mines) or mines.tp.. + + + +* **DONE: Add a placeholder test command** +Create a command, like under **/prison** that can be used to test placeholders. +Have one where the user can enter any free text and then translate it. +Also have a page(s) that goes through all of them printing the place holder name and the current values. + + + +* **Improve the prestige laddering system*** +A plugin named EZprestige has been attempted to be used with prison. Not sure if successful? +-= Prison now has its own prestige system =- + + + +* **DONE: Remove world check before loading mine*** +Now supports deferred world loading, where the world loads after prison initializes. +This is a problem with Multiverse-core plugin since a softdepend loads way before a hard depend. As such, the worlds that were created with Multiverse-core have not yet been added to the bukkit list of worlds. + +If the world is checked after the server is running, they will be available. Put in a class variable that identifies if the world was verified, and if not, then check. + +Problem is that at startup time, we won't know if there is a problem with missing worlds. + + +* **DONE: Add /ranks remove currency [rankName] [currency]** +Done. Currently no way to remove a currency from a Rank to return it to normal currency. + + +* **DONE: Add onBlockBreak monitor to prison mines to count blocks mined** +Set EventPriority to monitor. Don't change anything. Just confirm block was changed to air and increment the count. +Do not have to precheck if the block was air before, since air cannot trigger a block break event. Just confirm it is air when monitoring to ensure it was removed. If this works, then this would allow the elimination of the air counts. + + +* **DONE: Delay Mine Resets based upon blocks remaining** + Done: This is the Skip Mine Reset processing. It will not do a reset until enough blocks are mined. + + +* **DONE: Skip Mine Resets - Based upon usage and percent remaining** + * If a mine is not being used, this can greatly reduce the processing needs within the server. If there are about 30 mines, and no players are online for hours, then it can greatly reduce the server loads and reduce the number of chunk updates. + * Placeholder added percent mined so data exists to use for this calculation. + * At reset time, if enabled, check to see how many blocks were mined. + * If more than threshold percentage, such as 10%, then reset the mine. + * Even if one block is mined, and it is below the threshold, may want to reset the mine after X number of *skipped* resets. + * Do not count as skipped if zero blocks are mined. + * All mines will reset after server reload, after the timer expires the first time, since in-mine block data and stats are not saved to the file system. + + * Fields to add to the Mine data: + * skipReset - true = enabled, false = disabled + * skipResetPercent - double - threshold to reset based upon blocks mined. Does not include original air-blocks. + * skipResetBypassThreshold - int - number of *skips* before a forced reset. + * skipResetBypassCount - transient - int - counts the number of times a reset is skipped. This is transient value and is never saved. + + + + + + +* **Not needed: Exclude specific Prison commands** +NOTE: The player asking for this was able to disable the ranks module and that was able to solve their problem. There are no other reasons to cause this much trouble and redesign of the command handling internally, so this is being removed from consideration. +Ability to exclude, or ignore, specific commands upon startup. +NOTE: this may not be needed. Disabling the Prison Ranks module solved the problem, which was trying to use EZRanksPro and prison's /rankup command was conflicting with that plugin's /rankup command + + + + +* **OBSOLETE: Sellall is now internal. Support QuickSell project for use with Prison *Only* *** + +*Goal:* Something to consider. See if it can work with 1.15.x. This would provide a solution for prison servers to use with the full range of our supported platform versions. Intentions of pushing changes back in to the main project and not maintaining a new project. + +QuickSell has be abandoned, but could be very useful for prison to provide a simplified integration of features. + +Quickly reviewed code and it looks fairly good and probably has very low maintenance. Base initial support could be updating dependencies within Maven. Goal to get QuickSell to work with all supported versions of Prison and all supported versions of spigot. + +Explicit support going forward would be directly related to Prison. If a support issue has to do with another 3rd party plugin, then support "could" be refused or unsupported 3rd party plugins could be removed. Primary focus would be for the support of Prison and to provide a QuickSell feature to users of the Prison plugin. + +https://www.spigotmc.org/resources/quicksell.6107/ + +https://github.com/TheBusyBiscuit/QuickSell +Currently 15 forks. Activity unknown. + + + + +* **Obsolete: New block handling system - put on hold** +-= Using XMaterial so this is covered, except for possibility of custom blocks =- + +Current system is based upon enumerations which are static and may not reflect the actual run time environment. Prison is compiled with 1.9.4, but yet the list may not include all blocks for all versions of bukkit/spigot/minecraft + + +If the new block handling system gets all blocks from org.bukkit.Material.values(), then it should reflect what's available on the server version that is running. If the server owner decides to upgrade, or down grade, their server version, then they will be responsible for "correcting" any block name that is no longer supported. This would be the negative for such a system + + +The benefits would be less to manage within prison; attitude of do what you want to do, instead of micro managing the list of blocks. Dynamic to support newest blocks available on minecraft/bukkit/spigot, or another platform. Ability to pickup custom blocks if they have been injected in to the Material enumeration + + +Currently there is a HUGE problem. Upon testing, I have determined that although a block exists within the server's org.bukkit.Material enum, Prison cannot select it. I do not know why. It could be related to the fact that prison is built with Gradle using spigot v1.9.4 and that imposes restrictions upon what enumerations can be accessed at runtime? That makes no sense since no artifacts of org.bukkit.Material should be carried over outside of the compile time instance. Until this issue can be addressed, there will be no work around or implmentation. + + + +* **Obsolete: Block Types for Specific Versions of Minecraft** +-= Using XMaterial going forward =- +Add in support for the loss of magic values, and also provide for newer block types too. Basically have a minecraft version selector that can tailor the list of available block types that can be used, based upon the minecraft version that is running + + + +* **DONE: Add parameter to charge player for Promote command** +On /ranks promote you can now charage a player for that rank. Also on /ranks demote you can issue a refund to the player too. See the actual commands for usage. + + +* **DONE: New Feature: Upon block break events, log block changes** +This will allow dynamic live tracking of mine stats, such as how many blocks +remain and relating percentages. The new async processing will enable this +to actually track individual blocks. + + +* **DONE: Document how to use essentials warps for each mine** +See documentation within github for these details. + + +* **DONE: Add placeholder aliases so they are not as long** +Done. + + +* **DONE: Eliminate the internal Placeholders** +Done. Performance improvement. + + +* **DONE: Integrate GUI in to bleeding** +Done. More GUI features will be added in the next release. + + +* **DONE: Setup GUI to use /prison gui** +Done. + + +* **DONE: Mine Placeholders** +Added a number of placeholders for mines. + + +* **DONE: Player Placeholder - prison_rankup_rank_tag** +When adding the new placeholder code, the prison_rankup_rank was set to return the +rank name and not tag. So added a prison_rankup_rank_tag so there would be access +to both to give the most flexibility. + +* **DONE: New Feature: List all registered plugins** +To better support server owners when they have issues with Prison, it would +be very helpful if **/prison version** would list all registered plugins in +a concise listing. In progress. Included now in bleeding. +All integrations are now listed with their version. +The spigot command **/pl** (plugin list) shows all active plugins. + +* **DONE: New feature: Track how many blocks are mined from the mines** +Stats on how many blocks are mined from each mine. May be bad to track, +but could open the door to interesting stats. +**DONE:** this is a whole mine sweep to count air blocks and is being performed +for the benefit of placeholders. This could be improved with block break +event tracking. + +* **DONE: New Feature: Add Placeholders for Mine related items** +Examples would be place holders for all mines, and their stats such as +size, dimensions, percent remaining, reset duration, time left until reset, +players currently within the mine, ect... + +* **DONE: Offline player support** +Was not possible to get offline users through the prison API. + +* **DONE: New Feature: Admin reset of Player Ranks** +Bypass the costs for the players. The admins can now use +/ranks promote, /ranks demote, and /ranks set rank. + +* **DONE: Eliminate support for Sponge** +It's not being used, so eliminate it and allow prison to possibly eliminate the +extra layers of indirection it currently has to improve performance and to +possibly reduce the possibilities for errors. + + +* **DONE: List currencies that are used in Ranks** + +On startup, gather all currencies that are defined within the Ranks, confirm there is a economy that supports it, and then print the list with the ranks module. + + + +## Known Issues - v3.2.1-alpha.9 - 2020-04-26 + + +* **Multiverse-core prevents worlds from loading** +Multiverse-core when it is a softdepend prevents prison from being able to access all of the worlds that it will normally load. When it is a softdepend Prison loads prior to multiverse-core so bukkit has no knowledge of those worlds. + +To fix this problem, you must manually add Multiverse-core as a hard dependency. + +**NOTICE** the line that starts with **depend:** since that line does not exist in the normal config.yml file. + +``` +website: https://mc-prison.tech +softdepend: [Essentials, Vault, LuckPerms, Multiverse-Core, Multiworld, MVdWPlaceholderAPI, PlaceholderAPI, GemsEconomy, WorldEdit, WorldGuard, ProtocolLib, PerWorldInventory, Multiverse-SignPortals, Multiverse-NetherPortals ] +depend: [Multiverse-Core] +commands: +``` + + +* **Prison v3.2.0 (and older) has limited Placeholder Support** + +Prison v3.2.0 only has one chat placeholder and it is {PRISON_RANK}, which must be in all uppercase. + +The only Prison integrations supported with this version are PlaceholderAPI and MVdWPlaceholder. + +The placeholders supported by PlaceholderAPI are prison_rank, prison_rankup_cost, and prison_rankup_rank. Supported case is unknown, so use lowercase. + +The actual placeholders for MVdWPlaceholder is actually unknown, but may be the same as listed for PlaceholderAPI, but I cannot confirm it. + + +* **Prison v3.2.1 Placeholder Support** + +The best way to find the available placeholders is to use the command **/prison version**. Keep in mind that any placeholder that ends with **minename** will be expanded for each mine, substituting the mine name for the suffix **minename**. For example, if there are 30 mines, then Prison will register 30 placeholders for each listed placeholder under **/prison version**. + +The placeholders will be registered as shown, in lowercase. They actually are case insensitive, but since they are registered in lowercase, the various placeholder APIs may only recognize lowercase entries. Also Prison's placeholders are registered without any brackets. Most placeholder tools use {} and a few others use %%; refer to the placeholder's documentation on correct usage. + +Prison's Ranks has a chat handler that now supports all placeholders. + +Support for PlaceholderAPI is through the prefix of "prison", of which it will route all placeholders with prefixes of "prison" through prison. When PlaceholderAPI makes calls to Prison, it strips the prefix. Therefore, Prison's placeholders will respond to the full placeholder, or the the placeholder minus the prefix. The list of all existing placeholders within Prison are not pre-registered. + +The MVdWPlaceholder api requires all placeholders be registered. + + +* **Some block types may not work for 1.15.x** +Since prison is not currently using the correct block names for 1.15.x, some +block types may not work. Prison is still using magic numbers for the +block types and those no longer work for 1.15.x. Symptom would be that +you set a block type such as birch block, but with the loss of the magic +number, it will revert back to just an oak block. ETA may be with +release v3.2.2? + + +* **Unable to change language on all Aspects of Prison** +Currently the number of phrases that can be changed to support other +languages is very limiting and requires a decent amount of manipulation +of extracting files from the Prison Jar. Future releases will allow just +about all uses of English to be replaced by external language files. +This will include error messages for players and mods (console errors), +command descriptions, and even replacement of the English commands as +aliases. The implementation of this will help ensure "errors" are caught +at compile time, and not runtime to help improve stability of the game. +Also the way the language files are structured at runtime will make it +easier to edit them. + + + +* **Information: Setting the correct currency for Prison** + + Note: Moved to FAQ docs. + + + +* **Known issue with LuckPerms v5.0.x Causing Prison Load Failures** + + Note: Moved to FAQ docs. + + + +* [ ] **Reports that other plugins may cause issues with Prison** +It's been mentioned that a plugin or two, named something like +"nohunger or nofalldmg", may have been causing issues with Prison. +Not sure if its the loading or running, but it behaved as if the +mines and ranks module was not loaded since those commands were not +functional. Only /prison was working. This appears as the same general +effect of LuckPerms v5.x failures, where they caused a failure that +prevented prison from performing a normal load. +I have not looked in to these plugins, but I would suggest that +WorldGuard should be used instead of these plugins to eliminate possible +conflicts. + + + +* [x] **REMOVED: No support for sponge - appears like it never had it** +Note: Support for sponge was commented out in two gradle files. The source code +remains, but it will not impact the builds anymore. + +There is a sponge module, but there is so little code that has been written, +that it does not appear to be hooked up. There is no way it could have ever worked +correctly since so many core components needed for the functionality of prison +are dependent upon Spigot internals, of which those same function calls under +Sponge's API are just empty or returning null values. + +For example, getScheduler() and dispatchCommand() both are empty, but they +are currently heavily used in both the mine reset process and also for +ranking up. + +It would be a really major effort to hook up the missing parts. I don't even +think anyone is trying to run it under sponge. Maybe best in the long run to +eliminate the sponge module and just focus on making prison better overall. +I think if I do get around to disabling it, it will just be commented out of the +gradle build such that the source will still be there, but it will be excluded +from the build. Otherwise as new features are added, and existing ones under go +major changes, then the Sponge components will have to be revisited and would be +wasting resources (and time) for no reasonable purpose. + + diff --git a/prison-core/build.gradle b/prison-core/build.gradle index c7c6433d7..240e78d0f 100644 --- a/prison-core/build.gradle +++ b/prison-core/build.gradle @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,14 @@ apply plugin: 'eclipse' apply plugin: 'idea' +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.apache.commons:commons-lang3:3.9' +} + processResources { from(sourceSets.main.resources.srcDirs) { include 'items.csv' @@ -27,7 +35,7 @@ processResources { shadowJar { dependencies { - include(dependency('org.apache.commons:commons-lang3:3.9')) + include(dependency('org.apache.commons:commons-lang3:3.10')) include(dependency('com.google.code.gson:gson:2.8.6')) } classifier 'API' diff --git a/prison-core/out/production/resources/lang/core/cs_CZ.properties b/prison-core/out/production/resources/lang/core/cs_CZ.properties new file mode 100644 index 000000000..924971ba6 --- /dev/null +++ b/prison-core/out/production/resources/lang/core/cs_CZ.properties @@ -0,0 +1,34 @@ +# +# Prison is a Minecraft plugin for the prison game mode. +# Copyright (C) 2017 The Prison Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +includeError=[%1] ma neplatnou hodnotu. +excludeError=[%1] ma neplatnou hodnotu. +cantAsConsole=Tento pozadavek nezle provest v konzoli. +missingArgument=Argument [%1] neexistuje nebo nemá vychozi hodnotu. +missingFlagArgument=Flag -%1 nema pozadovane parametry. +undefinedFlagArgument=Pro argument [%1] neexistuje flag -%2. +internalErrorOccurred=Pri pokusu o provedeni tohoto prikazu doslo k chybe. +noPermission=K provedeni tohoto prikazu nemas potrebne opravneni. +blockParseError=Parametr [%1] neni validni blok. +numberParseError=Parametr [%1] neni cislo. +numberTooLow=Parametr [%1] se musi rovnat nebo byt vetsi nez %2. +numberTooHigh=Parametr [%1] se musi rovnat nebo byt mensi nez %2. +numberRangeError=Parametr [%1] se musi rovnat nebo byt vetsi nez %2 a mensi nebo roven %3. +tooFewCharacters=Parametr [%1] se musi rovnat nebo byt vetsi nez %2 pismen. +tooManyCharacters=Parametr [%1] se musi rovnat nebo byt mensi nez %2 pismen. +playerNotOnline=Hrac %1 neni online. +worldNotFound=Svet %1 nebyl nalezen. diff --git a/prison-core/out/production/resources/lang/mines/cs_CZ.properties b/prison-core/out/production/resources/lang/mines/cs_CZ.properties new file mode 100644 index 000000000..59297f05e --- /dev/null +++ b/prison-core/out/production/resources/lang/mines/cs_CZ.properties @@ -0,0 +1,29 @@ +reset_warning=&7Vsechny doly se obnovi za &3%1&7. +reset_message=&7Vsechny doly byly obnoveny. +not_allowed=&7Nemuzes tezit v tomto dole. +autosmelt_enable=&bAutosmelt &7byl &azapnut&7. +autosmelt_disable=&bAutosmelt &7byl &cvypnut&7. +autoblock_enable=&bAutoblock &7byl &azapnut&7. +autoblock_disable=&bAutoblock &7byl &cvypnut&7. +autopickup_enable=&bAutopickup &7byl &azapnut&7. +autopickup_disable=&bAutopickup &7byl &cvypnut&7. +teleported=&7Byl jsi teleportovan do dolu &3%1&7. +mine_reset=&7Dul byl obnoven. +mine_reset_fail=&7Nelze obnovit dul. &8Pro vice info zkontrolujte konzoli. +mine_created=&7Dul uspesne vytvoren. +mine_deleted=&7Dul uspesne smazan. +select_bounds=&7Nejprve musis vybrat hranice dolu. &8Napis /mines wand pro vyber hranice. +world_diff=&7Nelze vytvorit dul pres dva ruzne svety. +mine_exists=&7Dul s timto jmenem jiz existuje. +mine_does_not_exist=&7Dul s timto jmenem neexistuje. +spawn_set=&7Spawnpoint dolu byl nastaven. +spawnpoint_same_world=&cSpawnpoint &7musi byt ve stejnem &csvete &7jako dul. +not_a_block=&c%1 &7neni blok. +block_already_added=&7Tento blok byl jiz pridan do dolu. +mine_full=&cDul bude prilis plny. &7Zkus snizit procento tohoto bloku nebo jinych bloku v dole abys vytvoril misto. +block_added=&7Blok &3%1 &7byl pridan do dolu &3%2&7. +block_set=&7Block &3%1 &7byl zmenen v dolu &3%2&7. +block_not_removed=&7Tento blok se v dole nenachazi. +block_deleted=&7Blok &3%1 &7byl odstranen z dolu &3%2&7. +mine_redefined=&7Uspesne &3zmeneny hranice &7dolu. +missing_world=&7Svet ve kterem byl dul vytvoren nelze najit. diff --git a/prison-core/src/main/java/tech/mcprison/prison/Prison.java b/prison-core/src/main/java/tech/mcprison/prison/Prison.java index 6bc452294..50b699c24 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/Prison.java +++ b/prison-core/src/main/java/tech/mcprison/prison/Prison.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,11 @@ package tech.mcprison.prison; +import java.io.File; +import java.util.Optional; + import com.google.common.eventbus.EventBus; + import tech.mcprison.prison.alerts.Alerts; import tech.mcprison.prison.commands.CommandHandler; import tech.mcprison.prison.error.Error; @@ -37,9 +41,6 @@ import tech.mcprison.prison.util.EventExceptionHandler; import tech.mcprison.prison.util.ItemManager; -import java.io.File; -import java.util.Optional; - /** * Entry point for implementations.

An instance of Prison can be retrieved using the static * {@link Prison#get()} method, however in order to use the core libraries, you must call @@ -48,19 +49,34 @@ * @author Faizaan A. Datoo * @since API 1.0 */ -public class Prison implements PluginEntity { - +public class Prison + implements PluginEntity { + + /** + *

This is not exactly a Prison module, but it's name is used within + * the LocaleManager to correct the generation of an incorrect directory + * path for the locale language files for this "core" module. Basically + * this provides consistency in the location of all language files that + * the admins can easily edit and know where to find them. + *

+ * + */ + public static final String PSEDUO_MODLE_NAME = "core"; + public static final int SPIGOTMC_ORG_PROJECT_ID = 1223; //72740; // Singleton public static final int API_LEVEL = 3; private static Prison instance = null; + + private String minecraftVersion; // Fields private Platform platform; private File dataFolder; private ModuleManager moduleManager; + private PrisonCommand prisonCommands; private CommandHandler commandHandler; private SelectionManager selectionManager; private EventBus eventBus; @@ -92,50 +108,69 @@ public static Prison get() { *

* Note that modules should not call this method. This is solely for the implementations. */ - public boolean init(Platform platform) { + public boolean init(Platform platform, String minecraftVersion) { long startTime = System.currentTimeMillis(); this.platform = platform; + this.minecraftVersion = minecraftVersion; + sendBanner(); - Output.get().logInfo("Enable start..."); + Output.get().logInfo("Enabling and starting..."); // Initialize various parts of the API. The magic happens here :) if (!initDataFolder()) { + Output.get().logInfo("&cFailure: &eInitializing the Prison Data Folders!" ); + Output.get().logInfo("&e&k!=&d Prison Plugin Terminated! &e&k=!&7" ); return false; } initManagers(); if (!initMetaDatabase()) { + Output.get().logInfo("&cFailure: &eInitializing the Prison Database!" ); + Output.get().logInfo("&e&k!=&d Prison Plugin Terminated! &e&k=!&7" ); return false; } Alerts.getInstance(); // init alerts - this.commandHandler.registerCommands(new PrisonCommand()); + this.prisonCommands = new PrisonCommand(); + this.commandHandler.registerCommands(prisonCommands); + long stopTime = System.currentTimeMillis(); + Output.get() .logInfo("Enabled &3Prison v%s in %d milliseconds.", getPlatform().getPluginVersion(), - (System.currentTimeMillis() - startTime)); + (stopTime - startTime)); registerInbuiltTroubleshooters(); if (getPlatform().shouldShowAlerts()) scheduleAlertNagger(); - + + + // Disabled for now. The integrations cannot properly support this yet. +// List integrations = Prison.get().getIntegrationManager().toStrings(); +// for ( String intgration : integrations ) { +// Output.get().logInfo( intgration ); +// } + + return true; } // Initialization steps private void sendBanner() { - PrisonAPI.log(""); - PrisonAPI.log("&6 _____ _ "); - PrisonAPI.log("&6| __ \\ (_) "); - PrisonAPI.log("&6| |__) | __ _ ___ ___ _ __ "); - PrisonAPI.log("&6| ___/ '__| / __|/ _ \\| '_ \\"); - PrisonAPI.log("&6| | | | | \\__ \\ (_) | | | |"); - PrisonAPI.log("&6|_| |_| |_|___/\\___/|_| |_|"); - PrisonAPI.log(""); - PrisonAPI.log("Loading version %s on platform %s...", PrisonAPI.getPluginVersion(), - platform.getClass().getSimpleName()); + Output.get().logInfo(""); + Output.get().logInfo("&6 _____ _ "); + Output.get().logInfo("&6| __ \\ (_) "); + Output.get().logInfo("&6| |__) | __ _ ___ ___ _ __ "); + Output.get().logInfo("&6| ___/ '__| / __|/ _ \\| '_ \\"); + Output.get().logInfo("&6| | | | | \\__ \\ (_) | | | |"); + Output.get().logInfo("&6|_| |_| |_|___/\\___/|_| |_|"); + Output.get().logInfo(""); + Output.get().logInfo("&7Loading Prison version: &3%s", PrisonAPI.getPluginVersion()); + Output.get().logInfo("&7Running on platform: &3%s", platform.getClass().getSimpleName()); + Output.get().logInfo("&7Minecraft version: &3%s", getMinecraftVersion()); + Output.get().logInfo(""); } private boolean initDataFolder() { @@ -206,7 +241,12 @@ public void deinit() { // Getters - @Override + public String getMinecraftVersion() + { + return minecraftVersion; + } + + @Override public String getName() { return "PrisonCore"; } @@ -269,8 +309,13 @@ public EventBus getEventBus() { public ModuleManager getModuleManager() { return moduleManager; } + - /** + public PrisonCommand getPrisonCommands() { + return prisonCommands; + } + + /** * Returns the command handler, where command methods can be registered using the {@link * CommandHandler#registerCommands(Object)} method. * diff --git a/prison-core/src/main/java/tech/mcprison/prison/PrisonAPI.java b/prison-core/src/main/java/tech/mcprison/prison/PrisonAPI.java index 4e11157e6..d04a3b099 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/PrisonAPI.java +++ b/prison-core/src/main/java/tech/mcprison/prison/PrisonAPI.java @@ -1,6 +1,14 @@ package tech.mcprison.prison; +import java.io.File; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + import com.google.common.eventbus.EventBus; + import tech.mcprison.prison.commands.CommandHandler; import tech.mcprison.prison.commands.PluginCommand; import tech.mcprison.prison.gui.GUI; @@ -17,9 +25,6 @@ import tech.mcprison.prison.util.ItemManager; import tech.mcprison.prison.util.Location; -import java.io.File; -import java.util.*; - /** * A static way to access {@link Platform} and {@link Prison} methods. * Much easier and recommended over using the aforementioned classes. @@ -83,6 +88,10 @@ public static List getCommands() { public static void dispatchCommand(String cmd) { Prison.get().getPlatform().dispatchCommand(cmd); } + + public void dispatchCommand(tech.mcprison.prison.internal.CommandSender sender, String cmd) { + Prison.get().getPlatform().dispatchCommand( sender, cmd); + } public static Scheduler getScheduler() { return Prison.get().getPlatform().getScheduler(); diff --git a/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java b/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java index 0a3d0d4b4..24d31fd45 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,13 +18,27 @@ package tech.mcprison.prison; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.TreeMap; +import java.util.UUID; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; +import tech.mcprison.prison.commands.CommandPagedData; +import tech.mcprison.prison.commands.Wildcard; +import tech.mcprison.prison.integration.IntegrationManager; import tech.mcprison.prison.integration.IntegrationType; import tech.mcprison.prison.internal.CommandSender; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleStatus; import tech.mcprison.prison.output.BulletedListComponent; import tech.mcprison.prison.output.ChatDisplay; +import tech.mcprison.prison.output.DisplayComponent; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.troubleshoot.TroubleshootResult; import tech.mcprison.prison.troubleshoot.Troubleshooter; @@ -38,35 +52,280 @@ */ public class PrisonCommand { - @Command(identifier = "prison version", description = "Displays version information.", onlyPlayers = false) + private List registeredPlugins = new ArrayList<>(); + + private TreeMap registeredPluginData = new TreeMap<>(); + + + @Command(identifier = "prison version", description = "Displays version information.", + onlyPlayers = false ) public void versionCommand(CommandSender sender) { + ChatDisplay display = displayVersion(); + + display.send(sender); + } + + /** + *

This class contains the data that is used to log the plugins, commands, and their aliases, + * that may be setup on the server. This is setting the ground work to store the command + * data that can be used to trouble shoot complex problems, such as conflicts, that are + * occuring with the prison plugin. + *

+ * + *

This is just the data, and does not interact, or modify, any of the other commands. + *

+ * + */ + public class RegisteredPluginsData { + private String pluginName; + private String pluginVersion; + private List registeredCommands; + + private boolean registered = false; + private int aliasCount = 0; + + public RegisteredPluginsData( String pluginName, String pluginVersion, boolean registered ) { + super(); + + this.pluginName = pluginName; + this.pluginVersion = pluginVersion; + this.registered = registered; + + this.registeredCommands = new ArrayList<>(); + } + + public void addCommand( String commandName, List commandAliases ) { + RegisteredPluginCommandData command = + new RegisteredPluginCommandData( commandName, commandAliases ); + + getRegisteredCommands().add( command ); + + setAliasCount( getAliasCount() + commandAliases.size() ); + } + + + public Object formatted() + { + String message = String.format( "&7%s &c%s&3(&a%s &7c:&a%s &7a:&a%s &3)", + getPluginName(), + (isRegistered() ? "" : "*"), + getPluginVersion(), + Integer.toString(getRegisteredCommands().size()), + Integer.toString(getAliasCount())); + return message; + } + + + public String getdetails() { + StringBuilder sbCmd = new StringBuilder(); + StringBuilder sbAlias = new StringBuilder(); + for ( RegisteredPluginCommandData cmd : getRegisteredCommands() ) + { + if ( sbCmd.length() > 0 ) { + sbCmd.append( " " ); + } + sbCmd.append( cmd.getCommand() ); + + for ( String alias : cmd.getAliases() ) + { + if ( sbAlias.length() > 0 ) { + sbAlias.append( " " ); + } + sbAlias.append( alias ); + } + + } + + return "Plugin: " + getPluginName() + " cmd: " + sbCmd.toString() + + (sbAlias.length() == 0 ? "" : + " alias: " + sbAlias.toString()); + } + + public String getPluginName() { + return pluginName; + } + public void setPluginName( String pluginName ) { + this.pluginName = pluginName; + } + + public String getPluginVersion() { + return pluginVersion; + } + public void setPluginVersion( String pluginVersion ) { + this.pluginVersion = pluginVersion; + } + + public boolean isRegistered() { + return registered; + } + public void setRegistered( boolean registered ) { + this.registered = registered; + } + + public List getRegisteredCommands() { + return registeredCommands; + } + public void setRegisteredCommands( List registeredCommands ) { + this.registeredCommands = registeredCommands; + } + + public int getAliasCount() { + return aliasCount; + } + public void setAliasCount( int aliasCount ) { + this.aliasCount = aliasCount; + } + + + } + + public class RegisteredPluginCommandData { + private String command; + private List aliases; + + public RegisteredPluginCommandData( String command, List aliases ) { + super(); + + this.command = command; + this.aliases = aliases; + } + + public String getCommand() { + return command; + } + public void setCommand( String command ) { + this.command = command; + } + + public List getAliases() { + return aliases; + } + public void setAliases( List aliases ) { + this.aliases = aliases; + } + } + + public ChatDisplay displayVersion() { + ChatDisplay display = new ChatDisplay("/prison version"); - display - .text("&7Version: &3%s &8(API level %d)", Prison.get().getPlatform().getPluginVersion(), - Prison.API_LEVEL); + display.text("&7Prison Version: &3%s", Prison.get().getPlatform().getPluginVersion()); - display.text("&7Platform: &3%s", Prison.get().getPlatform().getClass().getName()); + display.text("&7Running on Platform: &3%s", Prison.get().getPlatform().getClass().getName()); + display.text("&7Minecraft Version: &3%s", Prison.get().getMinecraftVersion()); + + display.text(""); + + display.text("&7Commands: &2/prison"); + + for ( Module module : Prison.get().getModuleManager().getModules() ) { + + display.text( "&7Module: &3%s&3 : %s %s", module.getName(), + module.getStatus().getStatusText(), + (module.getStatus().getStatus() == ModuleStatus.Status.FAILED ? + "&d[" + module.getStatus().getMessage() + "&d]" : "") + ); + display.text( " &7Base Commands: %s", module.getBaseCommands() ); + } + + List disabledModules = Prison.get().getModuleManager().getDisabledModules(); + if ( disabledModules.size() > 0 ) { + display.text( "&7Disabled Module%s:", (disabledModules.size() > 1 ? "s" : "")); + for ( String disabledModule : Prison.get().getModuleManager().getDisabledModules() ) { + display.text( "&a &cDisabled Module: &7%s&a. Related commands and placeholders are non-functional. ", + disabledModule ); + } + } + + + display.text(""); display.text("&7Integrations:"); + IntegrationManager im = Prison.get().getIntegrationManager(); String permissions = - Prison.get().getIntegrationManager().hasForType(IntegrationType.PERMISSION) ? - "&a" + Prison.get().getIntegrationManager().getForType(IntegrationType.PERMISSION) - .get().getProviderName() : - "&cNone"; + (im.hasForType(IntegrationType.PERMISSION) ? + "&a" + im.getForType(IntegrationType.PERMISSION).get().getDisplayName() : + "&cNone"); display.text(Text.tab("&7Permissions: " + permissions)); - String economy = Prison.get().getIntegrationManager().hasForType(IntegrationType.ECONOMY) ? - "&a" + Prison.get().getIntegrationManager().getForType(IntegrationType.ECONOMY).get() - .getProviderName() : - "&cNone"; + String economy = + (im.hasForType(IntegrationType.ECONOMY) ? + "&a" + im.getForType(IntegrationType.ECONOMY).get().getDisplayName() : + "&cNone"); display.text(Text.tab("&7Economy: " + economy)); + + + List integrationRows = Prison.get().getIntegrationManager().getIntegrationComponents(); + for ( DisplayComponent component : integrationRows ) + { + display.addComponent( component ); + } + + Prison.get().getPlatform().identifyRegisteredPlugins(); + + // NOTE: This list of plugins is good enough and the detailed does not have all the info. + // Display all loaded plugins: + if ( getRegisteredPlugins().size() > 0 ) { + display.text( "&7Registered Plugins: " ); + StringBuilder sb = new StringBuilder(); + for ( String plugin : getRegisteredPlugins() ) { + if ( sb.length() == 0) { + sb.append( " " ); + sb.append( plugin ); + } else { + sb.append( ", " ); + sb.append( plugin ); + display.text( sb.toString() ); + sb.setLength( 0 ); + } + } + if ( sb.length() > 0 ) { + display.text( sb.toString()); + } + } + + // This version of plugins does not have all the registered commands: +// // The new plugin listings: +// if ( getRegisteredPluginData().size() > 0 ) { +// display.text( "&7Registered Plugins Detailed: " ); +// StringBuilder sb = new StringBuilder(); +// Set keys = getRegisteredPluginData().keySet(); +// +// for ( String key : keys ) { +// RegisteredPluginsData plugin = getRegisteredPluginData().get(key); +// +// if ( sb.length() == 0) { +// sb.append( " " ); +// sb.append( plugin.formatted() ); +// } else { +// sb.append( ", " ); +// sb.append( plugin.formatted() ); +// display.text( sb.toString() ); +// sb.setLength( 0 ); +// } +// } +// if ( sb.length() > 0 ) { +// display.text( sb.toString()); +// } +// } + + +// RegisteredPluginsData plugin = getRegisteredPluginData().get( "Prison" ); +// String pluginDetails = plugin.getdetails(); +// +// display.text( pluginDetails ); + - display.send(sender); + + Prison.get().getPlatform().getWorldLoadErrors( display ); + + + return display; } - @Command(identifier = "prison modules", description = "Lists the modules that hook into Prison to give it functionality.", onlyPlayers = false, permissions = "prison.modules") + @Command(identifier = "prison modules", onlyPlayers = false, permissions = "prison.modules", + description = "Lists the modules that hook into Prison to give it functionality.") public void modulesCommand(CommandSender sender) { ChatDisplay display = new ChatDisplay("/prison modules"); display.emptyLine(); @@ -83,7 +342,8 @@ public void modulesCommand(CommandSender sender) { display.send(sender); } - @Command(identifier = "prison troubleshoot", description = "Runs a troubleshooter.", onlyPlayers = false, permissions = "prison.troubleshoot") +// @Command(identifier = "prison troubleshoot", description = "Runs a troubleshooter.", +// onlyPlayers = false, permissions = "prison.troubleshoot") public void troubleshootCommand(CommandSender sender, @Arg(name = "name", def = "list", description = "The name of the troubleshooter.") String name) { // They just want to list stuff @@ -107,7 +367,8 @@ public void troubleshootCommand(CommandSender sender, } - @Command(identifier = "prison troubleshoot list", description = "Lists the troubleshooters.", onlyPlayers = false, permissions = "prison.troubleshoot") +// @Command(identifier = "prison troubleshoot list", description = "Lists the troubleshooters.", +// onlyPlayers = false, permissions = "prison.troubleshoot") public void troubleshootListCommand(CommandSender sender) { ChatDisplay display = new ChatDisplay("Troubleshooters"); display.text("&8Type /prison troubleshoot to run a troubleshooter."); @@ -123,9 +384,330 @@ public void troubleshootListCommand(CommandSender sender) { display.send(sender); } - @Command(identifier = "prison convert", description = "Convert your Prison 2 data to Prison 3 data.", onlyPlayers = false, permissions = "prison.convert") - public void convertCommand(CommandSender sender) { - sender.sendMessage(Prison.get().getPlatform().runConverter()); + + @Command(identifier = "prison placeholders test", + description = "Converts any Prison placeholders in the test string to their values", + onlyPlayers = false, permissions = "prison.placeholder") + public void placeholdersTestCommand(CommandSender sender, + @Wildcard(join=true) + @Arg(name = "text", description = "Placeholder text to test" ) String text ) { + + ChatDisplay display = new ChatDisplay("Placeholder Test"); + + BulletedListComponent.BulletedListBuilder builder = + new BulletedListComponent.BulletedListBuilder(); + + Player player = getPlayer( sender ); + UUID playerUuid = (player == null ? null : player.getUUID()); + String translated = Prison.get().getPlatform().getPlaceholders() + .placeholderTranslateText( playerUuid, sender.getName(), text ); + + builder.add( String.format( "&a Include one or more Prison placeholders with other text...")); + builder.add( String.format( "&a Use { } to escape the placeholders.")); + builder.add( String.format( "&7 Original: %s", text)); + builder.add( String.format( "&7 Translated: %s", translated)); + + display.addComponent(builder.build()); + display.send(sender); + } + + private Player getPlayer( CommandSender sender ) { + Optional player = Prison.get().getPlatform().getPlayer( sender.getName() ); + return player.isPresent() ? player.get() : null; + } + + + /** + *

Gets a player by name. If the player is not online, then try to get them from + * the offline player list. If not one is found, then return a null. + *

+ * + * @param sender + * @param playerName is optional, if not supplied, then sender will be used + * @return Player if found, or null. + */ + private Player getPlayer( CommandSender sender, String playerName ) { + Player result = null; + + playerName = playerName != null ? playerName : sender != null ? sender.getName() : null; + + if ( playerName != null ) { + Optional opt = Prison.get().getPlatform().getPlayer( playerName ); + if ( !opt.isPresent() ) { + opt = Prison.get().getPlatform().getOfflinePlayer( playerName ); + } + if ( opt.isPresent() ) { + result = opt.get(); + } + } + return result; + } + + @Command(identifier = "prison placeholders search", + description = "Search all placeholders that match all patterns", + onlyPlayers = false, permissions = "prison.placeholder") + public void placeholdersSearchCommand(CommandSender sender, + @Arg(name = "playerName", description = "Player name to use with player rank placeholders (optional)", + def = "." ) String playerName, + @Arg(name = "pageNumber", description = "page number of results to display", def = "." ) String pageNumber, + @Wildcard(join=true) + @Arg(name = "patterns", description = "Patterns of placeholders to search for" ) String patterns ) { + + + + // blank defaults do not work when there are more than one at a time. So had to + // default to periods. So convert periods to blanks initially: + playerName = (playerName.equals( "." ) ? "" : playerName ); + pageNumber = (pageNumber.equals( "." ) ? "" : pageNumber ); + patterns = (patterns.equals( "." ) ? "" : patterns ); + + Player player = getPlayer( null, playerName ); + if ( player == null ) { + // No player found, or none specified. Need to shift parameters over by one: + if ( pageNumber != null && pageNumber.trim().length() > 0 ) { + + // playerName should be moved to the pageNumber, after pageNumber is moved to patterns: + patterns = (pageNumber.trim() + " " + patterns).trim(); + } + pageNumber = playerName; + } + + + int page = 1; + + /** + * Please note: Page is optional and defaults to a value of 1. But when it is not + * provided, it "grabs" the first pattern. So basically, if pageNumber proves not + * to be a number, then we must prefix whatever is in patterns with that value. + */ + if ( pageNumber != null ) { + + try { + page = Integer.parseInt( pageNumber ); + } + catch ( NumberFormatException e ) { + // If exception, add pageNumber to the beginning patterns. + // So no page number was specified, it was part of the patterns + patterns = (pageNumber.trim() + " " + patterns).trim(); + } + + } + + + // Cannot allow pages less than 1: + if ( page < 1 ) { + page = 1; + } + + ChatDisplay display = new ChatDisplay("Placeholders Search"); + + + if ( patterns == null || patterns.trim().length() == 0 ) { + sender.sendMessage( "&7Pattern required. Placeholder results must match all pattern terms." ); + return; + } + + BulletedListComponent.BulletedListBuilder builder = + new BulletedListComponent.BulletedListBuilder(); + + if ( player == null ) { + // playerName was not provided, or was invalid. So use sender. + player = getPlayer( sender ); + } + UUID playerUuid = (player == null ? null : player.getUUID()); + + List placeholders = Prison.get().getPlatform().getPlaceholders() + .placeholderSearch( playerUuid, (player == null ? null : player.getName()), + patterns.trim().split( " " ) ); + + builder.add( String.format( "&a Include one or more patterns to search for placeholders. If more")); + builder.add( String.format( "&a than one is provided, the returned placeholder will hit on all.")); + builder.add( String.format( "&a Player based placeholders will return nulls for values if ran from console,")); + builder.add( String.format( "&a unless player name is specified. Can view placeholders for any player.")); + + if ( player != null ) { + builder.add( String.format( "&a Player: &7%s &aPlayerUuid: &7%s", player.getName(), + (playerUuid == null ? "null" : playerUuid.toString()))); + + } + + + DecimalFormat dFmt = new DecimalFormat("#,##0"); + builder.add( String.format( "&7 Results: &c%s &7Original patterns: &3%s", + dFmt.format(placeholders.size()), patterns )); + + + CommandPagedData cmdPageData = new CommandPagedData( + "/prison placeholders search", placeholders.size(), + 0, Integer.toString( page ), 12 ); + // Need to provide more "parts" to the command that follows the page number: + cmdPageData.setPageCommandSuffix( patterns ); + + int count = 0; + for ( String placeholder : placeholders ) { + if ( cmdPageData == null || + count++ >= cmdPageData.getPageStart() && + count <= cmdPageData.getPageEnd() ) { + + builder.add( String.format( placeholder )); + } + } + + display.addComponent(builder.build()); + + cmdPageData.generatePagedCommandFooter( display ); + + display.send(sender); + } + + + @Command(identifier = "prison placeholders list", + description = "List all placeholders templates", + onlyPlayers = false, permissions = "prison.placeholder") + public void placeholdersListCommand(CommandSender sender + ) { + + ChatDisplay display = new ChatDisplay("Placeholders List"); + + display.text( "&a Placeholders are case insensitive, but are registered in all lowercase."); + display.text( "&a Chat based placeholders use { }, but others may use other escape codes like %% %%."); + display.text( "&a Mine based placeholders uses the mine name to replace 'minename'."); + + for ( String disabledModule : Prison.get().getModuleManager().getDisabledModules() ) { + display.text( "&a &cDisabled Module: &7%s&a. Related placeholders maybe listed but are non-functional. ", + disabledModule ); + } + + List placeholders = new ArrayList<>(); + Prison.get().getIntegrationManager().getPlaceholderTemplateList( placeholders ); + + + for ( DisplayComponent placeholder : placeholders ) { + display.addComponent( placeholder ); + } + + display.send(sender); + } + + + @Command(identifier = "prison reload placeholders", + description = "Placeholder reload: Regenerates all placeholders and reregisters them.", + onlyPlayers = false, permissions = "prison.placeholder") + public void placeholdersReloadCommandAlias(CommandSender sender ) { + placeholdersReloadCommand( sender ); + } + + @Command(identifier = "prison placeholders reload", + description = "Placeholder reload: Regenerates all placeholders and reregisters them.", + onlyPlayers = false, permissions = "prison.placeholder") + public void placeholdersReloadCommand(CommandSender sender ) { + + Prison.get().getPlatform().getPlaceholders().reloadPlaceholders(); + + String message = "Placeholder reload was attempted. " + + "No guarentees that it worked 100%. Restart server if any doubts."; + + sender.sendMessage( message ); } + + + /** + *

This command does not do anything, except to provide a command placeholder to + * make owners aware that there is auto features enabled within prison. + * Running this command will show the permissions needed to use these auto features. + *

+ * + *

Cannot use the @Command altPermissions parameter since the permissions can be + * dynamically altered to fit the needs of the owner's server. Using the command + * altPermissions will also require a server restart to reflect any online changes, + * not to mention a recompile since the end users cannot make these changes. + *

+ * + * @param sender + */ + @Command(identifier = "prison autofeatures", + description = "Autofeatures for prison: pickup, smelt, and block", + onlyPlayers = false ) +// , altPermissions = { "prison.autofeatures.pickup", "prison.autofeatures.smelt" , +// "prison.autofeatures.block" }) + public void autoFeaturesInformation(CommandSender sender) { + + ChatDisplay display = new ChatDisplay("Auto Features Information"); + + display.text( "&a Prison auto features provide the following options:"); + display.text( "&7 Auto pickup - &aUpon block break, items are placed directly in to player inventory."); + display.text( "&7 Auto smelt - &aItems that can be smelted will be smelted automatically."); + display.text( "&7 Auto block - &aConverts ores to blocks."); + display.text( "&7 Tool lore starts with: Pickup, Smelt, or Block. Only one per line." ); + display.text( "&7 Tool lore 100 percent with just name. Can have value 0.001 to 100.0 percent." ); + display.text( "&7 Tool lore examples: Pickup, Pickup 7.13, Smelt 55, Block 75.123" ); + + display.text( "&a To configure modify plugin/Prison/autoFeaturesConfig.yml"); + display.text( "&a Or use &7/prison gui"); + + List afs = AutoFeatures.permissions.getChildren(); + StringBuilder sb = new StringBuilder(); + for ( AutoFeatures af : afs ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + sb.append( af.getMessage() ); + } + display.text( "&3Permissions:" ); + display.text( "&b %s", sb.toString() ); + + display.send(sender); + + // altPermissions are now a part of this command. +// // After displaying the help information above, rerun the same command for the player +// // with the help keyword to show the permissions. +// String formatted = "prison autofeatures help"; +// Prison.get().getPlatform().dispatchCommand(sender, formatted); + + } + + + +// This functionality should not be available in v3.2.1! If someone is still running Prison 2.x.x +// then they must first upgrade to +// prison v3.0.0 and perform the upgrade, at the most recent, then v3.1.1. +// @Command(identifier = "prison convert", description = "Convert your Prison 2 data to Prison 3 data.", +// onlyPlayers = false, permissions = "prison.convert") +// public void convertCommand(CommandSender sender) { +// sender.sendMessage(Prison.get().getPlatform().runConverter()); +// } + + public List getRegisteredPlugins() { + return registeredPlugins; + } + + public TreeMap getRegisteredPluginData() { + return registeredPluginData; + } + + public RegisteredPluginsData addRegisteredPlugin( String pluginName, String pluginVersion ) { + RegisteredPluginsData rpd = new RegisteredPluginsData( pluginName, pluginVersion, true ); + getRegisteredPluginData().put( pluginName, rpd); + return rpd; + } + + public RegisteredPluginsData addUnregisteredPlugin( String pluginName, String pluginVersion ) { + RegisteredPluginsData rpd = new RegisteredPluginsData( pluginName, pluginVersion, false ); + if ( !getRegisteredPluginData().containsKey( pluginName ) ) { + getRegisteredPluginData().put( pluginName, rpd); + } + return rpd; + } + + public void addPluginDetails( String pluginName, String pluginVersion, + String command, List commandAliases ) { + + // just in case this plugin was not registered before: + addUnregisteredPlugin( pluginName, pluginVersion ); + + RegisteredPluginsData plugin = getRegisteredPluginData().get( pluginName ); + + plugin.addCommand( command, commandAliases ); + } } diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/AutoFeaturesFileConfig.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/AutoFeaturesFileConfig.java new file mode 100644 index 000000000..6fb90d89a --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/AutoFeaturesFileConfig.java @@ -0,0 +1,503 @@ +package tech.mcprison.prison.autofeatures; + +import java.io.File; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.file.YamlFileIO; +import tech.mcprison.prison.output.Output; + +public class AutoFeaturesFileConfig { + + public static final String FILE_NAME__AUTO_FEATURES_CONFIG_YML = "/autoFeaturesConfig.yml"; + private File configFile; +// private FileConfiguration config; + + private Map config; + + public enum AutoFeatures { + + + messages, + + inventoryIsFull(messages, "&cWARNING! Your inventory's full!"), + inventoryIsFullDroppingItems(messages, "&cWARNING! Your inventory's full and you're dropping items!"), + inventoryIsFullLosingItems(messages, "&cWARNING! Your inventory's full and you're losing items!"), + + + options, + + general(options), + + isAutoManagerEnabled(general, false), + + isCalculateDurabilityEnabled(general, false), + isCalculateFortuneEnabled(general, true), + isCalculateSilkEnabled(general, true), + + dropItemsIfInventoryIsFull(general, true), + playSoundIfInventoryIsFull(general, true), + hologramIfInventoryIsFull(general, false), + + + permissions(options), + permissionAutoPickup(permissions, "prison.automanager.pickup"), + permissionAutoSmelt(permissions, "prison.automanager.smelt"), + permissionAutoBlock(permissions, "prison.automanager.block"), + + + lore(options), + isLoreEnabled(lore, true), + lorePickupValue(lore, "&dPickup&7"), + loreSmeltValue(lore, "&dSmelt&7"), + loreBlockValue(lore, "&dBlock&7"), + + loreTrackBlockBreakCount(lore, false), + loreBlockBreakCountName(lore, "&dPrison Blocks Mined:&7 "), + loreBlockExplosionCountName(lore, "&dPrison Blocks Exploded:&7 "), + + loreDurabiltyResistance(lore, false), + loreDurabiltyResistanceName(lore, "&dDurability Resistance&7"), + + autoPickup(options), + autoPickupEnabled(autoPickup, true), + autoPickupAllBlocks(autoPickup, true), + + autoPickupCobbleStone(autoPickup, true), + autoPickupStone(autoPickup, true), + autoPickupGoldOre(autoPickup, true), + autoPickupIronOre(autoPickup, true), + autoPickupCoalOre(autoPickup, true), + autoPickupDiamondOre(autoPickup, true), + autoPickupRedStoneOre(autoPickup, true), + autoPickupEmeraldOre(autoPickup, true), + autoPickupQuartzOre(autoPickup, true), + autoPickupLapisOre(autoPickup, true), + autoPickupSnowBall(autoPickup, true), + autoPickupGlowstoneDust(autoPickup, true), + + + autoSmelt(options), + autoSmeltEnabled(autoSmelt, true), + autoSmeltAllBlocks(autoSmelt, true), + + + autoSmeltGoldOre(autoSmelt, true), + autoSmeltIronOre(autoSmelt, true), + + + autoBlock(options), + autoBlockEnabled(autoBlock, true), + autoBlockAllBlocks(autoBlock, true), + + + autoBlockGoldBlock(autoBlock, true), + autoBlockIronBlock(autoBlock, true), + autoBlockCoalBlock(autoBlock, true), + autoBlockDiamondBlock(autoBlock, true), + autoBlockRedstoneBlock(autoBlock, true), + autoBlockEmeraldBlock(autoBlock, true), + autoBlockQuartzBlock(autoBlock, true), + autoBlockPrismarineBlock(autoBlock, true), + autoBlockLapisBlock(autoBlock, true), + autoBlockSnowBlock(autoBlock, true), + autoBlockGlowstone(autoBlock, true), + + ; + + + private final AutoFeatures parent; + private final boolean isSection; + private final boolean isBoolean; + private final boolean isMessage; + private final boolean isInteger; + private final boolean isLong; + private final boolean isDouble; + + private final String path; + private final String message; + private final Boolean value; + private final Integer intValue; + private final Long longValue; + private final Double doubleValue; + + private AutoFeatures() { + this.parent = null; + this.isSection = true; + this.isBoolean = false; + this.isMessage = false; + this.isInteger = false; + this.isLong = false; + this.isDouble = false; + this.path = null; + this.message = null; + this.value = null; + this.intValue = null; + this.longValue = null; + this.doubleValue = null; + } + private AutoFeatures(AutoFeatures section) { + this.parent = section; + this.isSection = true; + this.isBoolean = false; + this.isMessage = false; + this.isInteger = false; + this.isLong = false; + this.isDouble = false; + this.path = section.getKey(); + this.message = null; + this.value = null; + this.intValue = null; + this.longValue = null; + this.doubleValue = null; + } + private AutoFeatures(AutoFeatures section, String message) { + this.parent = section; + this.isSection = false; + this.isBoolean = false; + this.isMessage = true; + this.isInteger = false; + this.isLong = false; + this.isDouble = false; + this.path = section.getKey(); + this.message = message; + this.value = null; + this.intValue = null; + this.longValue = null; + this.doubleValue = null; + } + private AutoFeatures(AutoFeatures section, Boolean value) { + this.parent = section; + this.isSection = false; + this.isBoolean = true; + this.isMessage = false; + this.isInteger = false; + this.isLong = false; + this.isDouble = false; + this.path = section.getKey(); + this.message = null; + this.value = value == null ? Boolean.FALSE : value; + this.intValue = null; + this.longValue = null; + this.doubleValue = null; + } + + public AutoFeatures getParent() { + return parent; + } + public boolean isSection() { + return isSection; + } + public boolean isBoolean() { + return isBoolean; + } + public boolean isMessage() { + return isMessage; + } + public boolean isInteger() { + return isInteger; + } + public boolean isLong() { + return isLong; + } + public boolean isDouble() { + return isDouble; + } + + public String getPath() { + return path; + } + public String getMessage() { + return message; + } + public Boolean getValue() { + return value; + } + + public Integer getIntValue() { + return intValue; + } + public Long getLongValue() { + return longValue; + } + public Double getDoubleValue() { + return doubleValue; + } + + public String getKey() { + return (path != null ? path + "." : "") + this.name(); + } + + public AutoFeatures fromString( String autoFeature ) { + AutoFeatures results = null; + + for ( AutoFeatures af : values() ) { + if ( af.getKey().equalsIgnoreCase( autoFeature )) { + results = af; + break; + } + } + return results; + } + + public void setFileConfig( Map conf ) { + if ( isSection() ) { + // Skip this. The full path will be resolved in each key value for leaf nodes; + + } else if ( getMessage() != null ) { + // create a message entry: + TextNode text = TextNode.valueOf( getMessage() ); + conf.put(getKey(), text); + + } else if ( getValue() != null ) { + // create a boolean entry: + BooleanNode bool = BooleanNode.valueOf( getValue().booleanValue() ); + conf.put( getKey(), bool ); + + } + } + + /** + *

Get a String message from the FileConfiguration, and if the key + * does not exist, return the default value associated with the enum + * entry, if it does not exist, then it returns a blank string value. + *

+ * + * @param conf + * @return + */ + public String getMessage( Map conf ) { + String results = null; + + if ( conf.containsKey(getKey()) && conf.get( getKey() ).isTextNode() ) { + TextNode text = (TextNode) conf.get( getKey() ); + results = text.getValue(); + } + else if ( getMessage() != null ) { + results = getMessage(); + } + + return results; + } + + /** + *

Get's the boolean value from the FileConfiguration, and if the key + * does not exist, then it returns the default value associated with the + * enum entry, if it does not exist, then it returns false. + * @param conf + * @return + */ + public boolean getBoolean( Map conf ) { + boolean results = false; + + if ( conf.containsKey(getKey()) && conf.get( getKey() ).isBooleanNode() ) { + BooleanNode bool = (BooleanNode) conf.get( getKey() ); + results = bool.getValue(); + } + else if ( getValue() != null ) { + results = getValue().booleanValue(); + } + + return results; + } + + /** + *

Get the children nodes to the given item. + *

+ * + * @return + */ + public List getChildren() { + return getChildren(this); + } + + /** + *

Get the children for the specified node. + *

+ * + * @param parent + * @return + */ + public List getChildren(AutoFeatures parent) { + List results = new ArrayList<>(); + + for ( AutoFeatures af : values() ) { + if ( af.getParent() == parent ) { + results.add( af ); + } + } + + return results; + } + + } + + public AutoFeaturesFileConfig() { + + this.config = new LinkedHashMap<>(); + + + // The following is strictly not needed to ensure that the configs are + // created, but this, somehow, does ensure that the order in which + // the sections are written are in the order in which they are defined + // within the enum. + for ( AutoFeatures autoFeat : AutoFeatures.values() ) { + autoFeat.setFileConfig( getConfig() ); + } + + + + // This may sound counter intuitive if the config file does not exist, + // but when trying to load the yaml with the + // config fully loaded with the default values will trigger a save if + // anyone of them do not exist in the config file. + // So do not perform any special first time processing here since it + // is handled within the loadYamlAutoFeatures code. + + YamlFileIO yamlFileIO = Prison.get().getPlatform().getYamlFileIO( getConfigFile() ); + List dne = yamlFileIO.loadYamlAutoFeatures( getConfig() ); + + dne.size(); + +// Set keys = getConfig().keySet(); +// for ( String key : keys ) { +// ValueNode value = getConfig().get( key ); +// Output.get().logInfo( "AutoFeaturesFileConfig: ### %s %s", +// key, value.toString() ); +// } + + } + + + /** + *

This updates AutoFeatures with a String value. + *

+ * + * @param feature + * @param value + */ + public void setFeature( AutoFeatures feature, String value ) { + + if ( feature.isSection() ) { + Output.get().logError( + String.format( "Error: AutoFeature %s is a section (path) and cannot be " + + "used with a value. value = [%s]", + feature.getKey(), + ( value == null ? "null" : value )) ); + + } + else if ( value == null || value.trim().length() == 0 ) { + Output.get().logError( + String.format( "Error: AutoFeature %s value cannot be null or empty.", + feature.getKey()) ); + } + else if ( !feature.isMessage() ) { + Output.get().logError( + String.format( "Error: AutoFeature %s value is not a message type so it " + + "cannot be assigned a message.", + feature.getKey()) ); + } + else { + + TextNode text = TextNode.valueOf( value ); + + getConfig().put( feature.getKey(), text ); + } + + } + + /** + *

This updates AutoFeatures with a boolean value. + *

+ * + * @param feature + * @param value + */ + public void setFeature( AutoFeatures feature, boolean value ) { + + if ( feature.isSection() ) { + Output.get().logError( + String.format( "Error: AutoFeature %s is a section (path) and cannot be " + + "used with a value. value = [%s]", + feature.getKey(), + Boolean.valueOf( value ).toString() ) ); + + } + else if ( !feature.isBoolean() ) { + Output.get().logError( + String.format( "Error: AutoFeature %s value is not a boolean type so it " + + "cannot be assigned a boolean value.", + feature.getKey()) ); + } + else { + + BooleanNode bool = BooleanNode.valueOf( value ); + + getConfig().put( feature.getKey(), bool ); + } + + } + + + /** + * This returns the config's feature value if it is a boolean. If it is not + * a boolean config value, it will return a value false. + * + * @param feature + * @return + */ + public boolean isFeatureBoolean( AutoFeatures feature ) { + return feature.getBoolean( getConfig() ); + } + + public String getFeatureMessage( AutoFeatures feature ) { + return feature.getMessage( getConfig() ); + } + + public boolean saveConf() { + return saveConf( getConfig() ); + } + + /** + *

This function attempts to save the AutoFeatures configurations to the + * file system. This function uses a temporary file to initially perform the save, + * then when it is successfully finished, it then deletes the original file, and + * renames the temp file to the correct file name. This swapping of files + * will prevent the loss of configuration data if something should go wrong in the + * initial saving of the data since the original file will not be deleted first. + *

+ * + * @param afConfig + * @return + */ + private boolean saveConf( Map config ) { + + YamlFileIO yamlFileIO = Prison.get().getPlatform().getYamlFileIO( getConfigFile() ); + return yamlFileIO.saveYamlAutoFeatures( config ); + } + + + public File getConfigFile() { + if ( this.configFile == null ) { + + this.configFile = new File( + Prison.get().getDataFolder() + FILE_NAME__AUTO_FEATURES_CONFIG_YML); + } + return configFile; + } + public void setConfigFile( File configFile ) { + this.configFile = configFile; + } + + public Map getConfig() { + return config; + } + + public void setConfig( Map config ) { + this.config = config; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BooleanNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BooleanNode.java new file mode 100644 index 000000000..a3b0a672c --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BooleanNode.java @@ -0,0 +1,61 @@ +package tech.mcprison.prison.autofeatures; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public class BooleanNode + extends ValueNode { + private static final long serialVersionUID = -1L; + + public final static BooleanNode TRUE = new BooleanNode(true); + public final static BooleanNode FALSE = new BooleanNode(false); + + private final boolean value; + + protected BooleanNode(boolean v) { value = v; } + + + public static BooleanNode getTrue() { return TRUE; } + public static BooleanNode getFalse() { return FALSE; } + + public static BooleanNode valueOf(boolean b) { return b ? TRUE : FALSE; } + + + @Override + public String toString() { + return Boolean.toString( getValue() ); + } + + public boolean getValue() { + return value; + } + + @Override + public boolean isBooleanNode() { + return true; + } + + @Override + public NodeType getNodeType() { + return NodeType.BOOLEAN; + } + + + @Override + public int hashCode() { + return value ? 3 : 1; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (!(o instanceof BooleanNode)) { + return false; + } + return (value == ((BooleanNode) o).value); + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/DoubleNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/DoubleNode.java new file mode 100644 index 000000000..16fef124d --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/DoubleNode.java @@ -0,0 +1,53 @@ +package tech.mcprison.prison.autofeatures; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public class DoubleNode + extends ValueNode { + private static final long serialVersionUID = -1L; + + protected final double value; + + public DoubleNode(double v) { value = v; } + + public static DoubleNode valueOf(double d) { return new DoubleNode(d); } + + @Override + public String toString() { + return Double.toString( getValue() ); + } + + public double getValue() { + return value; + } + + @Override + public boolean isDoubleNode() { return true; } + + @Override + public NodeType getNodeType() { + return NodeType.DOUBLE; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null) return false; + if (o instanceof DoubleNode) { + // We must account for NaNs: NaN does not equal NaN, therefore we have + // to use Double.compare(). + final double otherValue = ((DoubleNode) o).value; + return Double.compare(value, otherValue) == 0; + } + return false; + } + + @Override + public int hashCode() { + // same as hashCode Double.class uses + long l = Double.doubleToLongBits(value); + return ((int) l) ^ (int) (l >> 32); + } +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/IntegerNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/IntegerNode.java new file mode 100644 index 000000000..96a98a8c6 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/IntegerNode.java @@ -0,0 +1,48 @@ +package tech.mcprison.prison.autofeatures; + +public class IntegerNode + extends ValueNode { + + private static final long serialVersionUID = -1L; + + protected final int value; + + public IntegerNode(int v) { value = v; } + + public static IntegerNode valueOf(int i) { return new IntegerNode(i); } + + @Override + public String toString() { + return Integer.toString( getValue() ); + } + + public int getValue() { + return value; + } + + @Override + public boolean isIntegerNode() { + return true; + } + + @Override + public NodeType getNodeType() { + return NodeType.INTEGER; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof IntegerNode) { + return ((IntegerNode) o).value == value; + } + return false; + } + + @Override + public int hashCode() { + return ((int) value) ^ (int) (value >> 32); + } +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/LongNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/LongNode.java new file mode 100644 index 000000000..d68335a69 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/LongNode.java @@ -0,0 +1,51 @@ +package tech.mcprison.prison.autofeatures; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public class LongNode + extends ValueNode { + private static final long serialVersionUID = -1L; + + protected final long value; + + public LongNode(long v) { value = v; } + + public static LongNode valueOf(long l) { return new LongNode(l); } + + @Override + public String toString() { + return Long.toString( getValue() ); + } + + public long getValue() { + return value; + } + + @Override + public boolean isLongNode() { + return true; + } + + @Override + public NodeType getNodeType() { + return NodeType.LONG; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof LongNode) { + return ((LongNode) o).value == value; + } + return false; + } + + @Override + public int hashCode() { + return ((int) value) ^ (int) (value >> 32); + } +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/NullNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/NullNode.java new file mode 100644 index 000000000..d826410ad --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/NullNode.java @@ -0,0 +1,45 @@ +package tech.mcprison.prison.autofeatures; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public class NullNode + extends ValueNode +{ + private static final long serialVersionUID = -1L; + + public final static NullNode instance = new NullNode(); + + public static NullNode getInstance() { return instance; } + + public boolean isNull() { + return true; + } + + @Override + public String toString() { + return Boolean.toString( true ); + } + + @Override + public boolean isNullNode() { return true; } + + @Override + public NodeType getNodeType() { + return NodeType.NULL; + } + + @Override + public boolean equals(Object o) { + return o == null || + o == this || + (o instanceof NullNode); + } + + @Override + public int hashCode() { + return -13; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/TextNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/TextNode.java new file mode 100644 index 000000000..3a7813c94 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/TextNode.java @@ -0,0 +1,74 @@ +package tech.mcprison.prison.autofeatures; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public class TextNode + extends ValueNode +{ + private static final long serialVersionUID = -1L; + + final static TextNode EMPTY_STRING_NODE = new TextNode(""); + + protected final String value; + + public TextNode(String v) { + super(); + value = v; + } + + @Override + public String toString() { + return getValue(); + } + + /** + * Factory method that should be used to construct instances. + * For some common cases, can reuse canonical instances: currently + * this is the case for empty Strings, in future possible for + * others as well. If null is passed, will return null. + * + * @return Resulting {@link TextNode} object, if v + * is NOT null; null if it is. + */ + public static TextNode valueOf(String v) + { + if (v == null) { + return null; + } + if (v.length() == 0) { + return EMPTY_STRING_NODE; + } + return new TextNode(v); + } + + public String getValue() { + return value; + } + + @Override + public NodeType getNodeType() { + return NodeType.STRING; + } + + @Override + public boolean isTextNode() { + return true; + } + + @Override + public boolean equals(Object o) + { + if (o == this) return true; + if (o == null) return false; + if (o instanceof TextNode) { + return ((TextNode) o).value.equals(value); + } + return false; + } + + @Override + public int hashCode() { return value.hashCode(); } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNode.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNode.java new file mode 100644 index 000000000..698787d0e --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNode.java @@ -0,0 +1,44 @@ +package tech.mcprison.prison.autofeatures; + +import java.io.Serializable; + +/** + * Roughly based upon Jackson's class by the same name. + * + */ +public abstract class ValueNode + implements Serializable { + + private static final long serialVersionUID = 1L; + + public enum NodeType { + NULL, + STRING, + BOOLEAN, + LONG, + INTEGER, + DOUBLE + ; + } + + public boolean isNullNode() { return false; } + + public boolean isTextNode() { return false; } + + public boolean isBooleanNode() { return false; } + + public boolean isIntegerNode() { return false; } + + public boolean isLongNode() { return false; } + + public boolean isDoubleNode() { return false; } + + + public abstract NodeType getNodeType(); + + public abstract boolean equals(Object o); + + public abstract int hashCode(); + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNodeFactory.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNodeFactory.java new file mode 100644 index 000000000..d21ab838c --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/ValueNodeFactory.java @@ -0,0 +1,63 @@ +package tech.mcprison.prison.autofeatures; + +import java.util.regex.Pattern; + +public class ValueNodeFactory { + + public Pattern LONG_PATTERN = Pattern.compile("^-?\\d{1,19}$"); + public Pattern DOUBLE_PATTERN = Pattern.compile("^[\\+\\-]{0,1}[0-9]+[\\.\\,]{1}[0-9]+$"); + + + public ValueNode getValueNode( String value ) { + + if ( value == null ) { + return NullNode.getInstance(); + } + + // trim off leading and trailing spaces: + value = value.trim(); + + if ( value != null && + ("true".equalsIgnoreCase( value.trim() ) || + "false".equalsIgnoreCase( value.trim() ))) { + + try { + boolean b = Boolean.parseBoolean( value.trim() ); + return BooleanNode.valueOf( b ); + } + catch ( Exception e ) { + // ignore since not a boolean... let another parser try to consume it. + } + } + + if ( LONG_PATTERN.matcher( value ).matches() ) { + + try { + long l = Long.parseLong( value ); + return LongNode.valueOf( l ); + } + catch ( NumberFormatException e ) { + // ignore since not a long... let another parser try to consume it. + } + + } + + + // Note that the regex pattern does not match to scientific notations + // such as 123.45E2. + if ( DOUBLE_PATTERN.matcher( value ).matches() ) { + + try { + double d = Double.parseDouble( value ); + return DoubleNode.valueOf( d ); + } + catch ( NumberFormatException e ) { + // ignore since not a long... let another parser try to consume it. + } + + } + + return TextNode.valueOf( value ); + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/ArrayWrapper.java b/prison-core/src/main/java/tech/mcprison/prison/chat/ArrayWrapper.java index 3ba54655d..86341b48d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/ArrayWrapper.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/ArrayWrapper.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/FancyMessage.java b/prison-core/src/main/java/tech/mcprison/prison/chat/FancyMessage.java index fce0b463d..92e1ddeeb 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/FancyMessage.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/FancyMessage.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/JsonRepresentedObject.java b/prison-core/src/main/java/tech/mcprison/prison/chat/JsonRepresentedObject.java index afaa35ac0..7ddbe8f61 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/JsonRepresentedObject.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/JsonRepresentedObject.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/JsonString.java b/prison-core/src/main/java/tech/mcprison/prison/chat/JsonString.java index 84cf261be..eb82a0792 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/JsonString.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/JsonString.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/MessagePart.java b/prison-core/src/main/java/tech/mcprison/prison/chat/MessagePart.java index 40872455f..4d960ff7d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/MessagePart.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/MessagePart.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/TextualComponent.java b/prison-core/src/main/java/tech/mcprison/prison/chat/TextualComponent.java index a1153758f..ebb75522f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/TextualComponent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/TextualComponent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -216,7 +216,7 @@ public void setValue(String value) { writer.name(getKey()).value(getValue()); } - @SuppressWarnings("serial") public Map serialize() { + @SuppressWarnings({ "serial", "unused" }) public Map serialize() { return new HashMap() {{ put("key", getKey()); put("value", getValue()); @@ -290,7 +290,7 @@ public void setValue(Map value) { writer.endObject(); } - @SuppressWarnings("serial") public Map serialize() { + @SuppressWarnings({ "serial", "unused" }) public Map serialize() { return new HashMap() {{ put("key", getKey()); for (Entry valEntry : getValue().entrySet()) { diff --git a/prison-core/src/main/java/tech/mcprison/prison/chat/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/chat/package-info.java index 0bc9ff723..7d6988884 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/chat/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/chat/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Arg.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Arg.java index 425766a7c..f88b91747 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Arg.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Arg.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentHandler.java index 576fcd583..b663ebad3 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVariable.java b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVariable.java index f1147ef38..04b7395c2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVariable.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVariable.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVerifier.java b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVerifier.java index 51d810188..20346dc37 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVerifier.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/ArgumentVerifier.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Arguments.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Arguments.java index 8c0a631fd..181e0e793 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Arguments.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Arguments.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Command.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Command.java index 50fe05f33..42174d4fa 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Command.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Command.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Command { +@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) +public @interface Command { /** * The description of this command @@ -55,4 +56,26 @@ * error message stating that they do not have permission to use the command. */ String[] permissions() default {}; + + + /** + * AltPermissions are alternative permissions that are not checked internally, or automatically. + * It is up to the programmer to put hooks in to the code to check on these altPermissions. + * This field of altPermissions is strictly for displaying helpful information to the end users + * and it is only helpful if it is included. + * + * For example the command /rankup has an optional parameter ladderName. If a ladderName is + * provided, then it checks to see if the player has the permission: ranks.rankup.[ladderName]. + * + * Because these permissions are not ever used to check for actual permissions, it is very + * important to provide parameters such as [ladderName] to signify where the server owner, or + * admin, must place the real ladder name within the permission. + * + * This is such a critically helpful feature because otherwise the only way you would know + * that you need this permission is to look at the source code, of which many cannot do, and + * those who can, may not know where to look. So this provides very important information that + * was not available before. + * + */ + String[] altPermissions() default {}; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandArgument.java b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandArgument.java index 4d3862016..27bafd4dc 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandArgument.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandArgument.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandError.java b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandError.java index 4188dd5e4..bfd8996c5 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandError.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandError.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandHandler.java index 00be0e578..bbf55a00f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,8 @@ public class CommandHandler { private Prison plugin; private Map, ArgumentHandler> argumentHandlers = - new HashMap, ArgumentHandler>(); + new HashMap, ArgumentHandler>(); + private Map rootCommands = new HashMap<>(); private PermissionHandler permissionHandler = (sender, permissions) -> { @@ -69,7 +70,8 @@ private String formatArgument(CommandArgument argument) { + ChatColor.DARK_AQUA + argument.getDescription(); } - @Override public String[] getHelpMessage(RegisteredCommand command) { + @Override + public String[] getHelpMessage(RegisteredCommand command) { ArrayList message = new ArrayList(); if (command.isSet()) { @@ -99,15 +101,79 @@ private String formatArgument(CommandArgument argument) { } } } + if ( command.getPermissions() != null && command.getPermissions().length > 0 || + command.getAltPermissions() != null && command.getAltPermissions().length > 0 ) { + + StringBuilder sb = new StringBuilder(); + + if ( command.getPermissions() != null && command.getPermissions().length > 0 ) { + for ( String perm : command.getPermissions() ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + sb.append( perm ); + } + } + if ( command.getAltPermissions() != null && command.getAltPermissions().length > 0 ) { + for ( String altPerm : command.getAltPermissions() ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + sb.append( altPerm ); + } + } + + if ( sb.length() > 0 ) { + message.add(ChatColor.DARK_AQUA + "Permissions:"); + + sb.insert( 0, ChatColor.AQUA ); + sb.insert( 0, " " ); + message.add( sb.toString() ); + } + + } } List subcommands = command.getSuffixes(); if (subcommands.size() > 0) { message.add(ChatColor.DARK_AQUA + "Subcommands:"); + // Force a sorting by use of a TreeSet. Collections.sort() would not work. + TreeSet subCommandSet = new TreeSet<>(); for (RegisteredCommand scommand : subcommands) { - message.add(scommand.getUsage()); + String subCmd = scommand.getUsage(); + + int subCmdSubCnt = scommand.getSuffixes().size(); + + subCommandSet.add(subCmd + (subCmdSubCnt <= 1 ? "" : + ChatColor.DARK_AQUA + " (" + subCmdSubCnt + + " Subcommands)")); } + + for (String subCmd : subCommandSet) { + message.add(subCmd); + } + } + + if ( command.getLabel().equalsIgnoreCase( "prison" ) && rootCommands.size() > 1 ) { + message.add(ChatColor.DARK_AQUA + "Prison Root Commands:"); + // Force a sorting by use of a TreeSet. Collections.sort() would not work. + TreeSet rootCommandSet = new TreeSet<>(); + + // Try adding in all other root commands: + Set rootKeys = rootCommands.keySet(); + + for ( PluginCommand rootKey : rootKeys ) { + String rootCmd = rootKey.getUsage(); + + rootCommandSet.add( rootCmd ); + + } + + for (String rootCmd : rootCommandSet) { + message.add(rootCmd); + } } + return message.toArray(new String[0]); } @@ -264,4 +330,30 @@ public boolean onCommand(CommandSender sender, PluginCommand command, String lab return true; } + +/* + * ###Tab-Complete### + * + * Disabled for now until a full solution can be implemented for tab complete. + * + public List getRootCommandKeys() { + List results = new ArrayList<>(); + + Set keys = rootCommands.keySet(); + for ( PluginCommand pluginCommand : keys ) { + // These are the core command sets: + results.add( pluginCommand.getLabel() ); + + // Then expand them to all the sub commands that are assoicated with the cores: + RootCommand cmd = rootCommands.get( pluginCommand ); + List regCmds = cmd.getSuffixes(); + for ( RegisteredCommand regCmd : regCmds ) { + results.add( pluginCommand.getLabel() + " " + regCmd.getLabel() ); + } + + } + + return results; + } + */ } diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandPagedData.java b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandPagedData.java new file mode 100644 index 000000000..9a759176b --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandPagedData.java @@ -0,0 +1,181 @@ +package tech.mcprison.prison.commands; + +import tech.mcprison.prison.chat.FancyMessage; +import tech.mcprison.prison.output.ButtonComponent; +import tech.mcprison.prison.output.ButtonComponent.Style; +import tech.mcprison.prison.output.ChatDisplay; +import tech.mcprison.prison.output.RowComponent; + +public class CommandPagedData { + public static final int MAX_PAGE_SIZE = 10; + + private String pageCommand; + private String pageCommandSuffix; + + private int curPage = 1; + private boolean showAll = false; + + private int pageSize = MAX_PAGE_SIZE; + private int pages = 1; + private int extraPages = 1; + + private int pageStart = 1; + private int pageEnd = 1; + + public CommandPagedData( String pageCommand, int itemSize, int extraPages, String page) { + this( pageCommand, itemSize, extraPages, page, MAX_PAGE_SIZE ); + } + + /** + * + * @param command The command to run to show the same command, minus the page + * number, but including the leading slash. + * @param itemSize The total number of items that need to be paged through + * @param extraPages Extra pages to be displayed with the the item pages. Example + * would be the /prison info command with the mine details being on page 1. + * @param page The raw page String from the command interface. May contain invalid data. + */ + public CommandPagedData( String pageCommand, int itemSize, int extraPages, String page, int pageSize ) { + super(); + + this.pageCommand = pageCommand; + + if ( "all".equalsIgnoreCase( page )) { + this.showAll = true; + } + + this.curPage = 1; + this.pageSize = isShowAll() ? itemSize : pageSize; + this.pages = isShowAll() ? 1 : (itemSize / getPageSize()) + + (itemSize % getPageSize() == 0 ? 0 : 1); + this.extraPages = isShowAll() ? 0 : extraPages; + + + try { + curPage = Integer.parseInt(page); + } + catch ( NumberFormatException e ) { + // Ignore: Not an integer, will use the default value. + } + + curPage = ( isShowAll() || curPage < 1 ? 1 : + (curPage > (pages + getExtraPages()) ? (pages + getExtraPages()) : curPage )); + + // Just set to defaults for the pre-list pages: + this.pageStart = 0; + this.pageEnd = (itemSize < getPageSize() ? itemSize : getPageSize()); + + if ( isShowAll() ) { + this.pageEnd = itemSize; + this.pages = 1; + } + else if ( (curPage - getExtraPages()) >= 1 ) { + this.pageStart = (curPage - getExtraPages() - 1) * getPageSize(); + this.pageEnd = ((pageStart + getPageSize()) > itemSize ? itemSize : pageStart + getPageSize()); + } + +// Output.get().logInfo( "CommandPagedData: pageStart=" + pageStart + " pageEnd=" + pageEnd); + } + + public void generatePagedCommandFooter( ChatDisplay display ) { + generatePagedCommandFooter( display, null ); + } + + public void generatePagedCommandFooter( ChatDisplay display, String message ) { + // Need to construct a dynamic row of buttons. It may have no buttons, both, or + // a combination of previous page or next page. But it will always have a page + // count between the two. + RowComponent row = new RowComponent(); + if ( getCurPage() > 1 ) + { + row.addFancy( + new ButtonComponent( "&e<-- Prev Page", '-', Style.NEGATIVE) + .runCommand(pageCommand + " " + (getCurPage() - 1) + + (getPageCommandSuffix() == null ? "" : " " + getPageCommandSuffix()), + "View the prior page of search results").getFancyMessage() ); + } + row.addFancy( + new FancyMessage(" &9< &3Page " + curPage + " of " + + (getPages() + getExtraPages() ) + " &9> " + + (message == null || message.trim().length() == 0 ? "" : " " + message ) + "&3")); + + if ( getCurPage() < (getPages() + getExtraPages()) ) + { + row.addFancy( + new ButtonComponent( "&eNext Page -->", '+', Style.POSITIVE) + .runCommand(pageCommand + " " + (getCurPage() + 1) + + (getPageCommandSuffix() == null ? "" : " " + getPageCommandSuffix()), + "View the prior page of search results").getFancyMessage() ); + } + display.addComponent( row ); + } + + + + public String getPageCommand() { + return pageCommand; + } + public void setPageCommand( String pageCommand ) { + this.pageCommand = pageCommand; + } + + public String getPageCommandSuffix() { + return pageCommandSuffix; + } + public void setPageCommandSuffix( String pageCommandSuffix ) { + this.pageCommandSuffix = pageCommandSuffix; + } + + public int getCurPage() { + return curPage; + } + public void setCurPage( int curPage ) { + this.curPage = curPage; + } + + public boolean isShowAll() { + return showAll; + } + public void setShowAll( boolean showAll ) { + this.showAll = showAll; + } + + public int getPageSize() { + return pageSize; + } + public void setPageSize( int pageSize ) { + this.pageSize = pageSize; + } + + public int getPages() { + return pages; + } + public void setPages( int pages ) { + this.pages = pages; + } + + public int getExtraPages() { + return extraPages; + } + + public void setExtraPages( int extraPages ) { + this.extraPages = extraPages; + } + + public int getPageStart() { + return pageStart; + } + public void setPageStart( int pageStart ) { + this.pageStart = pageStart; + } + + public int getPageEnd() { + return pageEnd; + } + public void setPageEnd( int pageEnd ) { + this.pageEnd = pageEnd; + } + + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandUtil.java b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandUtil.java index e678b2049..597cc8fd0 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/CommandUtil.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/CommandUtil.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/ExecutableArgument.java b/prison-core/src/main/java/tech/mcprison/prison/commands/ExecutableArgument.java index ba00d0463..3bf273e2e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/ExecutableArgument.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/ExecutableArgument.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Flag.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Flag.java index 6e0ba4a03..ad4f13d97 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Flag.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Flag.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArg.java b/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArg.java index c4995bc1b..acf290c11 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArg.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArg.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArgument.java b/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArgument.java index 3a7878bcc..49200ed32 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArgument.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/FlagArgument.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Flags.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Flags.java index 2ff928798..c95c78d2f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Flags.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Flags.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/HelpHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/HelpHandler.java index 4ba776f36..c19c61515 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/HelpHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/HelpHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/InvalidVerifyArgument.java b/prison-core/src/main/java/tech/mcprison/prison/commands/InvalidVerifyArgument.java index 838deacb3..944540f44 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/InvalidVerifyArgument.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/InvalidVerifyArgument.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/PermissionHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/PermissionHandler.java index 93c966ca6..36b64347f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/PermissionHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/PermissionHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/PluginCommand.java b/prison-core/src/main/java/tech/mcprison/prison/commands/PluginCommand.java index 642da6731..a559cad56 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/PluginCommand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/PluginCommand.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/ReflectionUtil.java b/prison-core/src/main/java/tech/mcprison/prison/commands/ReflectionUtil.java index f4bc84126..973c893a2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/ReflectionUtil.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/ReflectionUtil.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/RegisterCommandMethodException.java b/prison-core/src/main/java/tech/mcprison/prison/commands/RegisterCommandMethodException.java index 1e8441a72..68a39002b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/RegisterCommandMethodException.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/RegisterCommandMethodException.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/RegisteredCommand.java b/prison-core/src/main/java/tech/mcprison/prison/commands/RegisteredCommand.java index f29dd6f90..ab8b9cdd0 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/RegisteredCommand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/RegisteredCommand.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ public class RegisteredCommand { private RegisteredCommand parent; private String description; private String[] permissions; + private String[] altPermissions; private boolean onlyPlayers; private Method method; private Object methodInstance; @@ -65,11 +66,23 @@ void addSuffixCommand(String suffix, RegisteredCommand command) { boolean doesSuffixCommandExist(String suffix) { return suffixesByName.get(suffix) != null; } + + public String getCompleteLabel() { + return (parent == null ? "" : parent.getCompleteLabel() + " " ) + + (label == null ? "-noCommandLabelDefined-" : label) ; + } void execute(CommandSender sender, String[] args) { if (!testPermission(sender)) { Prison.get().getLocaleManager().getLocalizable("noPermission") .sendTo(sender, Localizable.Level.ERROR); + + Output.get().logInfo( "&cLack of Permission Error: &7Player &3%s &7lacks permission to " + + "run the command &3%s&7. Permissions needed: [&3%s&7]. Alt Permissions: [&3%s&7]", + sender.getName(), getCompleteLabel(), + (permissions == null ? "-none-" : String.join( ", ", permissions )), + (altPermissions == null ? "-none-" : String.join( ", ", altPermissions )) + ); return; } @@ -183,6 +196,10 @@ public String[] getPermissions() { return permissions; } + public String[] getAltPermissions() { + return altPermissions; + } + public RegisteredCommand getSuffixCommand(String suffix) { return suffixesByName.get(suffix); } @@ -223,6 +240,7 @@ void set(Object methodInstance, Method method) { Flags flagsAnnotation = method.getAnnotation(Flags.class); this.description = command.description(); this.permissions = command.permissions(); + this.altPermissions = command.altPermissions(); this.onlyPlayers = command.onlyPlayers(); Class[] methodParameters = method.getParameterTypes(); diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/RootCommand.java b/prison-core/src/main/java/tech/mcprison/prison/commands/RootCommand.java index 38d8e970b..c6aedbb3e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/RootCommand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/RootCommand.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/TransformError.java b/prison-core/src/main/java/tech/mcprison/prison/commands/TransformError.java index 8d5aaa8c0..3d31d3704 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/TransformError.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/TransformError.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/VerifierNotRegistered.java b/prison-core/src/main/java/tech/mcprison/prison/commands/VerifierNotRegistered.java index e54f394ee..55339404e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/VerifierNotRegistered.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/VerifierNotRegistered.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/VerifyError.java b/prison-core/src/main/java/tech/mcprison/prison/commands/VerifyError.java index ea2725aff..4e399b1e1 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/VerifyError.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/VerifyError.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/Wildcard.java b/prison-core/src/main/java/tech/mcprison/prison/commands/Wildcard.java index fa5efe9c9..5043aa39f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/Wildcard.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/Wildcard.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/WildcardArgument.java b/prison-core/src/main/java/tech/mcprison/prison/commands/WildcardArgument.java index 7825b395e..83ccba2fa 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/WildcardArgument.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/WildcardArgument.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/BlockArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/BlockArgumentHandler.java index 97bc5efee..f2c3e3838 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/BlockArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/BlockArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/DoubleArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/DoubleArgumentHandler.java index fc9bdc815..1af2272cd 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/DoubleArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/DoubleArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/IntegerArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/IntegerArgumentHandler.java index 3c9d76fd6..bbae89071 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/IntegerArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/IntegerArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/NumberArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/NumberArgumentHandler.java index 4a460d4d2..3c8e2fa58 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/NumberArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/NumberArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/PlayerArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/PlayerArgumentHandler.java index 700701855..c1de8faa6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/PlayerArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/PlayerArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/StringArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/StringArgumentHandler.java index e0470d5d2..b636a0219 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/StringArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/StringArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/WorldArgumentHandler.java b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/WorldArgumentHandler.java index f1f773767..ac3cec1b3 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/WorldArgumentHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/handlers/WorldArgumentHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/commands/package-info.java index 9817fe5a6..d619b8496 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/commands/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/file/YamlFileIO.java b/prison-core/src/main/java/tech/mcprison/prison/file/YamlFileIO.java new file mode 100644 index 000000000..67bec94af --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/file/YamlFileIO.java @@ -0,0 +1,202 @@ +package tech.mcprison.prison.file; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.autofeatures.BooleanNode; +import tech.mcprison.prison.autofeatures.DoubleNode; +import tech.mcprison.prison.autofeatures.IntegerNode; +import tech.mcprison.prison.autofeatures.LongNode; +import tech.mcprison.prison.autofeatures.TextNode; +import tech.mcprison.prison.autofeatures.ValueNode; +import tech.mcprison.prison.output.Output; + +public abstract class YamlFileIO { + + private File yamlFile; + + public YamlFileIO( File yamlFile ) { + super(); + + this.yamlFile = yamlFile; + } + + public boolean saveYamlAutoFeatures( Map config ) { + + // clears out the pre-existing data that is stored: + clear(); + + + // Create the sections first: + // This controls the order in which the sections will appear in the file: + for ( AutoFeatures autoFeat : AutoFeatures.values() ) { + if ( autoFeat.isSection() ) { + createSection( autoFeat.getKey() ); + } + } + + + Set keys = config.keySet(); + for ( String key : keys ) { + ValueNode value = config.get( key ); + + if ( value.isNullNode() ) { + // skip nulls + set( key, null ); + } + else if ( value.isBooleanNode() ) { + set( key, ((BooleanNode) value).getValue() ); + } + else if ( value.isTextNode() ) { + set( key, ((TextNode) value).getValue() ); + } + else if ( value.isDoubleNode() ) { + set( key, ((DoubleNode) value).getValue() ); + } + else if ( value.isLongNode() ) { + set( key, ((LongNode) value).getValue() ); + } + else if ( value.isIntegerNode() ) { + set( key, ((IntegerNode) value).getValue() ); + } + else { + // invalid type... not supported. +// set( key, value ); + } + } + + return saveYaml(); + } + + /** + *

This function loads a yaml file based upon the AutoFeatures enumeration. + * It loads the proper ValueNode based upon the defined value within the + * enum. If there is a property that does not exist within the yaml file, + * it will add it to the config object with the default value, plus it will + * return a list of all features that do not exist (dne). If any new settings + * are added to the configuration uploading (ie. new features), then it will + * trigger a save to update the file so the new features will exist in there. + *

+ * + *

This way, if the dne List is not empty, then it needs to be saved to + * update what is saved on the file system. Only the new items, with the + * default values, will be added; the original values will not be altered. + *

+ * + * @param config + * @return List of AutoFeatures that were just added to the config file. + */ + public List loadYamlAutoFeatures( Map config ) { + List dne = new ArrayList<>(); + + // Load from the actual yaml file: + loadYaml(); + + Map yaml = new TreeMap<>(); + Set keys = getKeys(); + for ( String key : keys ) { + Object value = getValues().get( key ); + yaml.put( key, value ); + } + + for ( AutoFeatures autoFeat : AutoFeatures.values() ) { + ValueNode value = null; + String key = autoFeat.getKey(); + + if ( autoFeat.isSection() ) { + createSection( key ); + } + else if ( autoFeat.isBoolean() ) { + boolean boolVal = !yaml.containsKey( key ) ? autoFeat.getValue().booleanValue() : + Boolean.parseBoolean( yaml.get( key ).toString() ); + value = BooleanNode.valueOf( boolVal ); + } + else if ( autoFeat.isMessage() ) { + String text = !yaml.containsKey( key ) ? autoFeat.getMessage() : + yaml.get( key ).toString(); + value = TextNode.valueOf( text ); + } + else if ( autoFeat.isInteger() ) { + int intVal = !yaml.containsKey( key ) ? autoFeat.getIntValue().intValue() : + (int) yaml.get( key ); + value = IntegerNode.valueOf( intVal); + } + else if ( autoFeat.isLong() ) { + long longVal = !yaml.containsKey( key ) ? autoFeat.getLongValue().longValue() : + (long) yaml.get( key ); + value = LongNode.valueOf( longVal ); + } + else if ( autoFeat.isDouble() ) { + double doubVal = !yaml.containsKey( key ) ? autoFeat.getDoubleValue().doubleValue() : + (double) yaml.get( key ); + value = DoubleNode.valueOf( doubVal ); + } + + + if ( !keys.contains( autoFeat.getKey() )) { + // AutoFeature does not exist in save file: + dne.add( autoFeat ); + } + + if ( value != null ) { + config.put( autoFeat.getKey(), value ); + } + } + + if ( dne.size() > 0 ) { + // New configs were found. Saving the configs. + saveYamlAutoFeatures( config ); + Output.get().logWarn( + String.format( "&cNotice: &7AutoManager config file was just updated with &c%s&7 new entries. " + + "May need to be configured. &cFile: &7%s", + Integer.toString( dne.size() ), + getYamlFile().getName()) ); + } + + if ( keys.size() == 0 ) { + Output.get().logWarn( + String.format( "Notice: AutoManager config file was just created. " + + "You must configure it to use it. &cFile: &7%s", + getYamlFile().getName()) ); + } + + return dne; + } + + + public File getYamlFile() { + return yamlFile; + } + + + abstract protected boolean loadYaml(); + abstract protected boolean saveYaml(); + + abstract public Set getKeys(); + abstract public Map getValues(); + + abstract protected void clear(); + abstract protected void set( String key, Object value ); + abstract protected void createSection( String key ); + + abstract protected boolean getBoolean( String key ); + abstract protected boolean getBoolean( String key, boolean defaultValue ); + + abstract protected String getString( String key ); + abstract protected String getString( String key, String defaultValue ); + + abstract protected double getDouble( String key ); + abstract protected double getDouble( String key, double defaultValue ); + + abstract protected int getInteger( String key ); + abstract protected int getInteger( String key, int intValue ); + + abstract protected long getLong( String key ); + abstract protected long getLong( String key, long defaultValue ); + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/Action.java b/prison-core/src/main/java/tech/mcprison/prison/gui/Action.java index d6f2739bc..d691cd45c 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/Action.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/Action.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/Button.java b/prison-core/src/main/java/tech/mcprison/prison/gui/Button.java index d05b08816..3c7e4ecff 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/Button.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/Button.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/ClickedButton.java b/prison-core/src/main/java/tech/mcprison/prison/gui/ClickedButton.java index 2ec80e604..48a2daab2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/ClickedButton.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/ClickedButton.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/GUI.java b/prison-core/src/main/java/tech/mcprison/prison/gui/GUI.java index cb7d0459b..af6a59170 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/GUI.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/GUI.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/gui/package-info.java index 800b6b846..f2893f7ea 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyCurrencyIntegration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyCurrencyIntegration.java new file mode 100644 index 000000000..56d693763 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyCurrencyIntegration.java @@ -0,0 +1,48 @@ +package tech.mcprison.prison.integration; + +import java.util.TreeMap; + +import tech.mcprison.prison.internal.Player; + +public abstract class EconomyCurrencyIntegration + extends EconomyIntegration +{ + private TreeMap supportedCurrencies; + + public EconomyCurrencyIntegration( String keyName, String providerName ) { + super( keyName, providerName ); + + this.supportedCurrencies = new TreeMap<>(); + } + + public boolean hasCurrency( String currency ) { + + if ( !getSupportedCurrencies().containsKey( currency ) ) { + boolean valid = supportedCurrency( currency ); + getSupportedCurrencies().put( currency, Boolean.valueOf( valid ) ); + } + + return getSupportedCurrencies().get( currency ).booleanValue(); + } + + public abstract boolean supportedCurrency( String currency ); + + public abstract double getBalance( Player player, String currency ); + + public abstract void setBalance( Player player, double amount, String currency ); + + public abstract void addBalance( Player player, double amount, String currency ); + + public abstract void removeBalance( Player player, double amount, String currency ); + + public abstract boolean canAfford( Player player, double amount, String currency ); + + + public TreeMap getSupportedCurrencies() { + return supportedCurrencies; + } + public void setSupportedCurrencies( TreeMap supportedCurrencies ) { + this.supportedCurrencies = supportedCurrencies; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyIntegration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyIntegration.java index 3adf95402..acfa60304 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyIntegration.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/EconomyIntegration.java @@ -8,15 +8,21 @@ * @author Faizaan A. Datoo * @since API 1.0 */ -public interface EconomyIntegration extends Integration { - +public abstract class EconomyIntegration + extends IntegrationCore { + + public EconomyIntegration( String keyName, String providerName ) { + super( keyName, providerName, IntegrationType.ECONOMY ); + + } + /** * Returns the player's current balance. * * @param player The {@link Player}. * @return a double. */ - double getBalance(Player player); + public abstract double getBalance(Player player); /** * Sets the player's balance. @@ -25,7 +31,7 @@ public interface EconomyIntegration extends Integration { * @param player The {@link Player}. * @param amount The amount. */ - void setBalance(Player player, double amount); + public abstract void setBalance(Player player, double amount); /** * Adds to the player's current balance. @@ -33,7 +39,7 @@ public interface EconomyIntegration extends Integration { * @param player The {@link Player}. * @param amount The amount. */ - void addBalance(Player player, double amount); + public abstract void addBalance(Player player, double amount); /** * Removes from the player's current balance. @@ -41,7 +47,7 @@ public interface EconomyIntegration extends Integration { * @param player The {@link Player}. * @param amount The amount. */ - void removeBalance(Player player, double amount); + public abstract void removeBalance(Player player, double amount); /** * Returns whether or not the player can afford a transaction. @@ -50,10 +56,6 @@ public interface EconomyIntegration extends Integration { * @param amount The amount. * @return true if the player can afford it, false otherwise. */ - boolean canAfford(Player player, double amount); - - @Override default IntegrationType getType() { - return IntegrationType.ECONOMY; - } + public abstract boolean canAfford(Player player, double amount); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/Integration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/Integration.java index 430453362..0cc4ac337 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/Integration.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/Integration.java @@ -10,26 +10,118 @@ */ public interface Integration { - /** + + /** + * If there is an initialization process that is dependent upon the modules being loaded, + * then place it within this function. The integrations are initialized prior to the + * modules, but this is a way to defer initializing something until after they are loaded. + */ + public void deferredInitialization(); + + + /** + * This is used to identify if an integration has been registered with the server engine, + * such as with Bukkit.getPluginManager().isPluginEnabled(). If the + * the given integration has not been registered, then it could be dangerous tying to + * access the class files associated with that plugin since it could result in + * class not found exceptions, or similar. Some plugins may have the same identifier, + * but yet different internal classes depending upon their versions, such as LuckPerms, + * so additional code must be used to verify what is actually loaded. Of course there + * are always exceptions to the rules too. + * + * @return true if the requested plugin has been registered with the server engine + */ + public boolean isRegistered(); + + /** + * Used to identify if the integration (plugin) has been registered with the server + * engine. + * + * @param registered + */ + public void setRegistered( boolean registered ); + + /** * Returns the type of integration that this class provides. * * @return The {@link IntegrationType}. */ - IntegrationType getType(); + public IntegrationType getType(); /** - * Returns the name of the third-party plugin that this class is integrating with. + * Returns true if this class has integrated with the provider successfully, or false + * otherwise. This should also return false if the third-party plugin which this + * integration is built for is not present. This is not the same as isRegistered, + * since a few integrations may have different class files backing it (such as LuckPerms). * - * @return The name of the provider. + * @return true if this class has integrated successfully, false otherwise. + */ + public boolean hasIntegrated(); + + + /** + * The function that is called to perform the integration. Generally the check is done + * prior to calling this function, but may vary. The attempt at integration used to be + * performed in the constructor, but but that prevents storing the failed integrations + * which can then be used to inform the admins what's available. If the integration + * was successful, then hasIntegrated() will return a value of true. + */ + public void integrate(); + + /** + * Returns the name of the internal representation that should be used for this integrator. + * It may not always be the same as providerName, but in most circumstances it will be. + * It's important that this keyName should be unique from any other integration keyName. + * For example, since there are two major kinds of LuckPerms that are supported, + * these should reflect which one is which. This field is used internally within + * collections and for display purposes when listing integrations so it should be + * more descriptive than the providerName. */ - String getProviderName(); + public String getKeyName(); /** - * Returns true if this class has integrated with the provider successfully, or false otherwise. - * This should also return false if the third-party plugin which this integration is built for is not present. + * Returns the name of the third-party plugin that this class is integrating with. This + * must be exactly what is provided in the plugin's plugin.yml file that + * is within the given jar files for the field called name:. * - * @return true if this class has integrated successfully, false otherwise. + * @return The name of the provider. */ - boolean hasIntegrated(); - + public String getProviderName(); + + /** + * The displayName is the value that is actually used when listing all the integrations. + * In general it is the combination of both the providerName and the keyName, but + * different integrations could customize it to better suite their unique requirements, + * such as with Vault. + * + * @return + */ + public String getDisplayName(); + + /** + *

This is a way for an integration to provide additional information when the + * integration is displayed through the /prison version command. This may vary + * from integration to integration, or by type. It is perfectly valid for it + * to return a null value, which indicates there is nothing available, so ignore + * this. + *

+ * + * @return null means nothing is available so ignore + */ + public String getAlternativeInformation(); + + /** + *

To help admins setup integrations, this is the URL that is generally associated + * with the integration. It should usually refer to the spigotmc.org website for + * that resource. + *

+ * + * @return null means nothing is available so this value should be ignored + */ + public String getPluginSourceURL(); + + + public String getDebugInfo(); + public void setDebugInfo( String debugInfo ); + public void addDebugInfo( String debugInfo ); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationCore.java b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationCore.java new file mode 100644 index 000000000..236385f82 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationCore.java @@ -0,0 +1,121 @@ +package tech.mcprison.prison.integration; + +import java.lang.reflect.InvocationTargetException; + +/** + *

This class provides for most of the default implementations for the + * Integration interface, and sets up some of the expected core behaviors. + *

+ * + */ +public class IntegrationCore + implements Integration +{ + + private final String keyName; + private final String providerName; + private final IntegrationType type; + private boolean registered = false; + + private String debugInfo; + + public IntegrationCore( String keyName, String providerName, IntegrationType type ) { + super(); + + this.keyName = keyName; + this.providerName = providerName; + this.type = type; + } + + public void deferredInitialization() { + } + + public boolean isRegistered() { + return registered; + } + public void setRegistered( boolean registered ) + { + this.registered = registered; + } + + /** + * This may not work since the actual class loader for the plugins + * might not be the System class loader. + * + * @param klass + * @return + */ + public boolean isClassLoaded( Class klass ) { + Object results = null; + + if ( klass != null ) { + try { + java.lang.reflect.Method m = + ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] { String.class}); + m.setAccessible(true); + ClassLoader cl = ClassLoader.getSystemClassLoader(); + results = m.invoke( cl, klass.getName() ); + } + catch ( NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | + InvocationTargetException e ) { + // Ignore: the class is not loaded. + e.getStackTrace(); + } + } + + return results != null; + } + + public IntegrationType getType() { + return type; + } + + public boolean hasIntegrated() { + return false; + } + + + public void integrate() { + + } + + + public String getKeyName() { + return keyName; + } + + public String getProviderName() { + return providerName; + } + + public String getDisplayName() { + return getProviderName() + + ( getProviderName().equals( getKeyName() ) ? "" : + " (" + getKeyName() + ")" ); + } + + + public String getAlternativeInformation() { + return null; + } + + public String getPluginSourceURL() { + return null; + } + + + @Override + public String getDebugInfo() { + return debugInfo; + } + @Override + public void setDebugInfo( String debugInfo ) { + this.debugInfo = debugInfo; + } + @Override + public void addDebugInfo( String debugInfo ) { + this.debugInfo = (this.debugInfo == null ? "" : this.debugInfo) + + "(" + debugInfo + ")"; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationManager.java b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationManager.java index 7b9fa4842..ece757386 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationManager.java @@ -1,6 +1,19 @@ package tech.mcprison.prison.integration; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.chat.FancyMessage; +import tech.mcprison.prison.output.DisplayComponent; +import tech.mcprison.prison.output.FancyMessageComponent; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.output.TextComponent; /** * The IntegrationManager stores instances of each {@link Integration} and allows @@ -12,12 +25,478 @@ public class IntegrationManager { private Map> integrations; + + private List deferredIntegrations; public IntegrationManager() { this.integrations = new HashMap<>(); + this.deferredIntegrations = new ArrayList<>(); } + public static final String PRISON_PLACEHOLDER_PREFIX = "prison"; + public static final String PRISON_PLACEHOLDER_PREFIX_EXTENDED = PRISON_PLACEHOLDER_PREFIX + "_"; + + public static final String PRISON_PLACEHOLDER_MINENAME_SUFFIX = "_minename"; + public static final String PRISON_PLACEHOLDER_LADDERNAME_SUFFIX = "_laddername"; + + private PlaceholderProgressBarConfig progressBarConfig; + + public enum PlaceHolderFlags { + + PLAYER, + LADDERS, + MINES, + PLAYERMINES, + + SUPRESS, + ALIAS + ; + } + /** + *

The given place holders should have both the prison prefix and without, + * with the without having the suppress value set. The suppressable items + * will not always be displayed since it would be implied that the prefix + * would have been provided. + *

+ * + *

Update: The placeholders without the prison prefix have been eliminated + * since the prefix is now prepended when it is missing prior to matching to a + * valid placeholder enum. This cuts the number of generated placeholders in half. + * This is significant since with the addition of the aliases there would be about + * 744 placeholders generated if the prison had 30 mines setup! Now a 30 mine prison + * would have about 372. + *

+ * + *

Note: In order to use these placeholders with something like holographic display + * you need to also include the placeholderAPI, + * plugin holographic extension and protocolib. + *

+ * + *

In order to get the holographics extension to work it is critical you read + * their spigot page since you have to specify a refresh speed. + *

+ * + * /hd addline temp2 Mine Size: {slowest}{prison_mines_blocks_size_temp2} + * or + * /hd addline temp2 Mine Size: {slowest}{prison_mines_blocks_size_temp2} + * /hd addline temp2 Mine Size: {slowest}%prison_mines_blocks_size_temp2% + * + * + * https://dev.bukkit.org/projects/holographic-displays + * https://www.spigotmc.org/resources/placeholderapi.6245/ + * https://www.spigotmc.org/resources/protocollib.1997/ + * https://www.spigotmc.org/resources/holographicextension.18461/ + */ + public enum PrisonPlaceHolders { + + no_match__(PlaceHolderFlags.SUPRESS), + + // Rank aliases: + prison_r(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rt(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rc(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rcp(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rcb(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rcr(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rr(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + prison_rrt(PlaceHolderFlags.PLAYER, PlaceHolderFlags.ALIAS), + + + prison_rank(prison_r, PlaceHolderFlags.PLAYER), + prison_rank_tag(prison_rt, PlaceHolderFlags.PLAYER), + prison_rankup_cost(prison_rc, PlaceHolderFlags.PLAYER), + prison_rankup_cost_percent(prison_rcp, PlaceHolderFlags.PLAYER), + prison_rankup_cost_bar(prison_rcb, PlaceHolderFlags.PLAYER), + prison_rankup_cost_remaining(prison_rcr, PlaceHolderFlags.PLAYER), + prison_rankup_rank(prison_rr, PlaceHolderFlags.PLAYER), + prison_rankup_rank_tag(prison_rrt, PlaceHolderFlags.PLAYER), + + + // Ladder aliases: + prison_r_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rt_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rc_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rcp_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rcb_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rcr_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rr_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + prison_rrt_laddername(PlaceHolderFlags.LADDERS, PlaceHolderFlags.ALIAS), + + + prison_rank_laddername(prison_r_laddername, PlaceHolderFlags.LADDERS), + prison_rank_tag_laddername(prison_rt_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_cost_laddername(prison_rc_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_cost_percent_laddername(prison_rcp_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_cost_bar_laddername(prison_rcb_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_cost_remaining_laddername(prison_rcr_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_rank_laddername(prison_rr_laddername, PlaceHolderFlags.LADDERS), + prison_rankup_rank_tag_laddername(prison_rrt_laddername, PlaceHolderFlags.LADDERS), + + + // Mine aliases: + prison_mi_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mif_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mtl_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mtlb_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mtlf_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_ms_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mr_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mrb_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mp_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mpc_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mbm_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + prison_mrc_minename(PlaceHolderFlags.MINES, PlaceHolderFlags.ALIAS), + + + // reset_interval, reset_timeleft, blocks_size, blocks_remaining, blocks_percent + // player_count + // NOTE: Remove PrisonPlaceHolderFlags.SUPRESS when ready to be used: + prison_mines_interval_minename(prison_mi_minename, PlaceHolderFlags.MINES), + prison_mines_interval_formatted_minename(prison_mif_minename, PlaceHolderFlags.MINES), + prison_mines_timeleft_minename(prison_mtl_minename, PlaceHolderFlags.MINES), + prison_mines_timeleft_bar_minename(prison_mtlb_minename, PlaceHolderFlags.MINES), + prison_mines_timeleft_formatted_minename(prison_mtlf_minename, PlaceHolderFlags.MINES), + prison_mines_size_minename(prison_ms_minename, PlaceHolderFlags.MINES), + prison_mines_remaining_minename(prison_mr_minename, PlaceHolderFlags.MINES), + prison_mines_remaining_bar_minename(prison_mrb_minename, PlaceHolderFlags.MINES), + prison_mines_percent_minename(prison_mp_minename, PlaceHolderFlags.MINES), + prison_mines_player_count_minename(prison_mpc_minename, PlaceHolderFlags.MINES), + prison_mines_blocks_mined_minename(prison_mbm_minename, PlaceHolderFlags.MINES), + prison_mines_reset_count_minename(prison_mrc_minename, PlaceHolderFlags.MINES), + + + + // PlayerMine aliases: + prison_mi_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mif_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mtl_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mtlb_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mtlf_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_ms_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mr_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mrb_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mp_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mpc_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mbm_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + prison_mrc_pm(PlaceHolderFlags.PLAYERMINES, PlaceHolderFlags.ALIAS), + + + prison_mines_interval_playermines(prison_mi_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_interval_formatted_playermines(prison_mif_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_timeleft_playermines(prison_mtl_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_timeleft_bar_playermines(prison_mtlb_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_timeleft_formatted_playermines(prison_mtlf_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_size_playermines(prison_ms_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_remaining_playermines(prison_mr_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_remaining_bar_playermines(prison_mrb_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_percent_playermines(prison_mp_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_player_count_playermines(prison_mpc_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_blocks_mined_playermines(prison_mbm_pm, PlaceHolderFlags.PLAYERMINES), + prison_mines_reset_count_playermines(prison_mrc_pm, PlaceHolderFlags.PLAYERMINES), + + + + ; + + + private final PrisonPlaceHolders alias; + private final List flags; + private PrisonPlaceHolders() { + this.flags = new ArrayList<>(); + this.alias = null; + } + private PrisonPlaceHolders(PlaceHolderFlags... flags) { + this.alias = null; + this.flags = getFlags(flags); + } + private PrisonPlaceHolders(PrisonPlaceHolders alias, PlaceHolderFlags... flags) { + this.alias = alias; + this.flags = getFlags(flags); + } + + private List getFlags( PlaceHolderFlags[] flags ) { + List flagz = new ArrayList<>(); + if ( flags != null ) { + for ( PlaceHolderFlags flag : flags ) { + flagz.add( flag ); + } + } + return flagz; + } + + public PrisonPlaceHolders getAlias() + { + return alias; + } + + public boolean hasAlias() { + return alias != null; + } + public boolean isAlias() { + return flags.contains( PlaceHolderFlags.ALIAS ); + } + public boolean isSuppressed() { + return flags.contains( PlaceHolderFlags.SUPRESS ); + } + public boolean hasFlag( PlaceHolderFlags flag ) { + return flags.contains( flag ); + } + public List getFlags() { + return flags; + } + + public static PrisonPlaceHolders fromString( String placeHolder ) { + PrisonPlaceHolders result = no_match__; + + if ( placeHolder != null && placeHolder.trim().length() > 0 ) { + placeHolder = placeHolder.trim(); + + // This allows us to get rid of suppressed placeholders that were used for + // internal matching when placeholder APIs strip off the prefix: + if ( !placeHolder.toLowerCase().startsWith( PRISON_PLACEHOLDER_PREFIX ) ) { + placeHolder = PRISON_PLACEHOLDER_PREFIX + "_" + placeHolder; + } + + for ( PrisonPlaceHolders ph : values() ) { + if ( ph.name().equalsIgnoreCase( placeHolder ) ) { + result = ph; + break; + } + } + } + + return result; + } + + public static List getTypes(PlaceHolderFlags flag) { + List results = new ArrayList<>(); + + if ( flag != null ) { + for ( PrisonPlaceHolders ph : values() ) { + if ( ph.getFlags().contains( flag )) { + results.add( ph ); + } + } + } + + return results; + } + + public static List excludeTypes( + List list, PlaceHolderFlags flag) { + List results = new ArrayList<>(); + + if ( flag != null ) { + for ( PrisonPlaceHolders ph : list ) { + if ( !ph.getFlags().contains( flag )) { + results.add( ph ); + } + } + } + + return results; + } + + public String getChatText() { + return "&a" + name() + + (hasAlias() ? "&7(&b" + getAlias().name() + "&7)" : "") + + (isSuppressed() ? "&4*&a ": " "); + } + + public static String getAllChatTexts() { + return getAllChatTexts(false); + } + + public static String getAllChatTextsOmitSuppressable() { + return getAllChatTexts(true); + } + + private static List getAllChatList( boolean omitSuppressable) { + List results = new ArrayList<>(); + + boolean hasDeprecated = false; + + for ( PrisonPlaceHolders ph : values() ) + { + if ( !omitSuppressable || omitSuppressable && !ph.isSuppressed() && !ph.isAlias() ) { + if ( !hasDeprecated && ph.isSuppressed() ) { + hasDeprecated = true; + } + + results.add( ph.getChatText() ); + } + } + + if ( hasDeprecated ) { + results.add( " &2(&4*&2=&4suppressed&2)" ); + } + + return results; + } + + private static String getAllChatTexts( boolean omitSuppressable) { + StringBuilder sb = new StringBuilder(); + + List placeholders = getAllChatList(omitSuppressable); + + for ( String placeholder : placeholders ) { + sb.append( placeholder ); + } + + return sb.toString(); + } + + } + + + public void reloadPlaceholderBarConfig() { + setProgressBarConfig( loadPlaceholderBarConfig() ); + } + + public PlaceholderProgressBarConfig loadPlaceholderBarConfig() { + PlaceholderProgressBarConfig config = null; + + String barSegmentsStr = Prison.get().getPlatform().getConfigString( + "placeholder.bar-segments" ); + String barPositiveColor = Prison.get().getPlatform().getConfigString( + "placeholder.bar-positive-color" ); + String barPositiveSegment = Prison.get().getPlatform().getConfigString( + "placeholder.bar-positive-segment" ); + String barNegativeColor = Prison.get().getPlatform().getConfigString( + "placeholder.bar-negative-color" ); + String barNegativeSegment = Prison.get().getPlatform().getConfigString( + "placeholder.bar-negative-segment" ); + + + // All 5 must not be null: + if ( barSegmentsStr != null && barPositiveColor != null && barPositiveSegment != null && + barNegativeColor != null && barNegativeSegment != null ) { + + int barSegments = 20; + + try { + barSegments = Integer.parseInt( barSegmentsStr ); + } + catch ( NumberFormatException e ) { + Output.get().logWarn( + "IntegrationManager.loadPlaceholderBarConfigs(): Failure to convert the" + + "/plugins/Prison/config.yml prison-placeholder-configs.progress-bar.bar-segments " + + "to a valid integer. Defaulting to a value of 20 " + + "[" + barSegmentsStr + "] " + e.getMessage() ); + + } + + config = new PlaceholderProgressBarConfig( barSegments, + barPositiveColor, barPositiveSegment, + barNegativeColor, barNegativeSegment ); + } + + if ( config == null ) { + // go with default values because the config.yml is not up to date with + // the default values + + config = new PlaceholderProgressBarConfig( + 20, "&2", "#", "&4", "=" +// 20, "&2", "▊", "&4", "▒" + ); + + Output.get().logInfo( "The /plugins/Prison/config.yml does not contain the " + + "default values for the Placeholder Progress Bar." ); + Output.get().logInfo( "Default values are " + + "being used. To customize the bar, rename the config.yml and it will be " + + "regenerated and then edit to restore prior values."); + + } + + return config; + } + + public PlaceholderProgressBarConfig getProgressBarConfig() { + if ( progressBarConfig == null ) { + progressBarConfig = loadPlaceholderBarConfig(); + } + return progressBarConfig; + } + public void setProgressBarConfig( PlaceholderProgressBarConfig progressBarConfig ) { + this.progressBarConfig = progressBarConfig; + } + + /** + *

This function uses the settings within the config.yml to construct a progress + * bar. It takes two numeric values and constructs it upon those parameters. + * The parameter

value
is the value that changes, and is the value that + * sets where the bar changes. The parameter
valueTotal
is the max value + * of where the
value
is increasing to. + *

+ * + *

The lowest range is always zero and

value
will be set to zero if + * it is negative. If
value
is greater than
valueTotal
+ * then it will be set to that value. The valid range for this function is only 0 percent + * to 100 percent. + *

+ * + *

If the progress bar is moving in the wrong direction, then set the parameter + *

reverse
to true and then the
value
will be inverted by subtracting + * its value from
valueTotal
. + *

+ * + * @param value A value that is changing. Will be set to zero if negative. Will be + * set to valueTotal if greater than that amount. + * @param valueTotal The target value that is non-changing. + * @param reverse Changes the growth direction of the progress bar. + * @return + */ + public String getProgressBar( double value, double valueTotal, boolean reverse ) { + StringBuilder sb = new StringBuilder(); + + // value cannot be greater than valueTotal: + if ( value > valueTotal ) { + value = valueTotal; + } + else if ( value < 0 ) { + value = 0; + } + + // If reverse, then the new value is subtracted from valueTotal: + if ( reverse ) { + value = valueTotal - value; + } + + double percent = value / valueTotal * 100.0; + + PlaceholderProgressBarConfig barConfig = + Prison.get().getIntegrationManager().getProgressBarConfig(); + + String lastColorCode = null; + for ( int i = 0; i < barConfig.getSegments(); i++ ) { + double pct = i / ((double)barConfig.getSegments()) * 100.0; + + if ( pct < percent ) { + if ( lastColorCode == null || + !barConfig.getPositiveColor().equalsIgnoreCase( lastColorCode )) { + sb.append( barConfig.getPositiveColor() ); + lastColorCode = barConfig.getPositiveColor(); + } + sb.append( barConfig.getPositiveSegment() ); + } + else { + if ( lastColorCode == null || + !barConfig.getNegativeColor().equalsIgnoreCase( lastColorCode )) { + sb.append( barConfig.getNegativeColor() ); + lastColorCode = barConfig.getNegativeColor(); + } + sb.append( barConfig.getNegativeSegment() ); + + } + } + + + return sb.toString(); + } + + + /** * Returns a list of all of the {@link Integration}s that are registered under a certain {@link IntegrationType}, if any. * This includes integrations that have not successfully integrated. * If there are none, an empty list will be returned. @@ -58,9 +537,181 @@ public boolean hasForType(IntegrationType type) { * @param i The {@link Integration}. */ public void register(Integration i) { - List integrationList = integrations.getOrDefault(i.getType(), new ArrayList<>()); - integrationList.add(i); - integrations.put(i.getType(), integrationList); + IntegrationType iType = i.getType(); + if ( !integrations.containsKey( iType ) ) { + integrations.put(iType, new ArrayList<>()); + } + integrations.get(iType).add(i); + } + + public EconomyCurrencyIntegration getEconomyForCurrency(String currency) { + EconomyCurrencyIntegration results = null; + + if(integrations.containsKey(IntegrationType.ECONOMY)) { + + List econs = getAllForType(IntegrationType.ECONOMY); + + for ( Integration econ : econs ) { + if ( econ.hasIntegrated() && econ instanceof EconomyCurrencyIntegration ) { + + EconomyCurrencyIntegration currencyEcon = (EconomyCurrencyIntegration) econ; + + if ( currencyEcon.hasCurrency( currency )) { + results = currencyEcon; + break; + } + } + } + + } + return results; + } + + public String getIntegrationDetails( IntegrationType intType ) { + StringBuilder sb = new StringBuilder(); + Set keys = integrations.keySet(); + + for ( IntegrationType key : keys ) { + if ( key == intType ) { + sb.append( key.name() ); + sb.append( ": [" ); + + StringBuilder sb2 = new StringBuilder(); + List integrates = integrations.get( key ); + for ( Integration i : integrates ) { + if ( sb2.length() > 0 ) { + sb2.append( ", " ); + } + sb2.append( i.getDisplayName() ); + sb2.append( " (registered=" ); + sb2.append( i.isRegistered() ); + sb2.append( ", integrated=" ); + sb2.append( i.hasIntegrated() ); + sb2.append( ")" ); + if ( i.getDebugInfo() != null && i.getDebugInfo().trim().length() > 0 ) { + sb2.append( " Debug: {" ); + sb2.append( i.getDebugInfo() ); + sb2.append( "}" ); + } + } + + sb.append( sb2 ); + sb.append( "] " ); + } + } + return sb.toString(); + } + + /** + *

This function formats all the Integrations, both active and inactive, to be displayed + * to a user, or sent to the server logs. This function returns a List to provide a + * degree of flexibility in where it is being used, without this container + * (The integration manager) having to know where the data will be used. This function + * keeps the business logic of relationship of integrations to Integration Types + * internal so as to no leak that knowledge out of this function. + *

+ * + * @return + */ + public List getIntegrationComponents() { + List results = new ArrayList<>(); + + for ( IntegrationType integType : IntegrationType.values() ) + { + results.add( new TextComponent( String.format( "&7Integration Type: &3%s", integType.name() ) )); + + // Generates the placeholder list for the /prison version command, printing + // two placeholders per line. + if ( integType == IntegrationType.PLACEHOLDER ) { + getPlaceholderTemplateList( results ); + } + + List plugins = getAllForType( integType ); + + if ( plugins == null || plugins.size() == 0 ) { + results.add( new TextComponent( " &e&onone" )); + } else { + for ( Integration plugin : plugins ) { + String pluginUrl = plugin.getPluginSourceURL(); + String msg = String.format( " &a%s &7<%s&7> %s", plugin.getDisplayName(), + ( plugin.hasIntegrated() ? "&aActive" : "&cInactive"), + ( pluginUrl == null ? "" : "&7[&eURL&7]")); + FancyMessage fancy = new FancyMessage( msg ); + if ( pluginUrl != null ) { + fancy.command( pluginUrl ).tooltip( "Click to open URL for this plugin.", pluginUrl ); + } + results.add( new FancyMessageComponent(fancy) ); + + String altInfo = plugin.getAlternativeInformation(); + if ( altInfo != null ) { + results.add( new TextComponent( " " + altInfo )); + } + + if ( integType == IntegrationType.ECONOMY && + plugin instanceof EconomyCurrencyIntegration ) { + EconomyCurrencyIntegration econ = (EconomyCurrencyIntegration) plugin; + + StringBuilder sb = new StringBuilder(); + + for ( String currency : econ.getSupportedCurrencies().keySet() ) { + Boolean supported = econ.getSupportedCurrencies().get( currency ); + if ( supported.booleanValue() ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + sb.append( currency ); + } + } + + if ( sb.length() > 0 ) { + results.add( new TextComponent( " &3Currencies: &7" + sb.toString() )); + } + } + } + } + } + + return results; } + public void getPlaceholderTemplateList( List results ) + { + results.add( new TextComponent( " &7Available PlaceHolders: " )); + + List placeholders = PrisonPlaceHolders.getAllChatList(true); + StringBuilder sb = new StringBuilder(); + for ( String placeholder : placeholders ) { + if ( sb.length() == 0) { + sb.append( " " ); + sb.append( placeholder ); + } + else if ( (sb.length() + placeholder.length()) > 90) { + // will be too long combined so write existing sb then start over: + results.add( new TextComponent( sb.toString() )); + sb.setLength( 0 ); + + sb.append( " " ); + sb.append( placeholder ); + } else { + sb.append( placeholder ); + results.add( new TextComponent( sb.toString() )); + sb.setLength( 0 ); + } + } + if ( sb.length() > 0 ) { + results.add( new TextComponent( sb.toString() )); + } + } + + public List getDeferredIntegrations() { + return deferredIntegrations; + } + public void setDeferredIntegrations( List deferredIntegrations ) { + this.deferredIntegrations = deferredIntegrations; + } + + public void addDeferredInitialization( Integration defferedIntegration ) { + getDeferredIntegrations().add( defferedIntegration ); + } + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationType.java b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationType.java index 811ff1f29..8a8f27001 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationType.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/IntegrationType.java @@ -5,6 +5,6 @@ */ public enum IntegrationType { - ECONOMY, PERMISSION, PLACEHOLDER + ECONOMY, PERMISSION, PLACEHOLDER, WORLDGUARD } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/ManagerPlaceholders.java b/prison-core/src/main/java/tech/mcprison/prison/integration/ManagerPlaceholders.java new file mode 100644 index 000000000..9af2b340d --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/ManagerPlaceholders.java @@ -0,0 +1,10 @@ +package tech.mcprison.prison.integration; + +import java.util.List; + +public interface ManagerPlaceholders { + + public List getTranslatedPlaceHolderKeys(); + + public void reloadPlaceholders(); +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/PermissionIntegration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/PermissionIntegration.java index 161efb2ac..f62740e1e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/PermissionIntegration.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/PermissionIntegration.java @@ -1,7 +1,6 @@ package tech.mcprison.prison.integration; import tech.mcprison.prison.internal.Player; -import tech.mcprison.prison.internal.platform.Platform; /** * An {@link Integration} for a permissions plugin. @@ -9,15 +8,21 @@ * @author Faizaan A. Datoo * @since API 1.0 */ -public interface PermissionIntegration extends Integration { +public abstract class PermissionIntegration + extends IntegrationCore { + public PermissionIntegration( String keyName, String providerName ) { + super( keyName, providerName, IntegrationType.PERMISSION ); + + } + /** * Adds a permission to this player. * * @param holder The player that will receive this permission. * @param permission The permission to add. */ - void addPermission(Player holder, String permission); + public abstract void addPermission(Player holder, String permission); /** * Removes a permission from this player. @@ -25,10 +30,6 @@ public interface PermissionIntegration extends Integration { * @param holder The player that will have this permission revoked. * @param permission The permission to remove. */ - void removePermission(Player holder, String permission); - - @Override default IntegrationType getType() { - return IntegrationType.PERMISSION; - } + public abstract void removePermission(Player holder, String permission); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceHolderKey.java b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceHolderKey.java new file mode 100644 index 000000000..d7f509358 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceHolderKey.java @@ -0,0 +1,64 @@ +package tech.mcprison.prison.integration; + +import tech.mcprison.prison.integration.IntegrationManager.PrisonPlaceHolders; + +public class PlaceHolderKey { + + private String key; + private PrisonPlaceHolders placeholder; + private String data; + private boolean primary = true; + private String aliasName; + + public PlaceHolderKey( String key, PrisonPlaceHolders placeholder ) { + this(key, placeholder, true); + } + public PlaceHolderKey( String key, PrisonPlaceHolders placeholder, boolean primary ) { + this.key = key; + this.placeholder = placeholder; + this.primary = primary; + } + public PlaceHolderKey( String key, PrisonPlaceHolders placeholder, String data ) { + this(key, placeholder); + this.data = data; + } + public PlaceHolderKey( String key, PrisonPlaceHolders placeholder, String data, boolean primary ) { + this(key, placeholder, primary); + this.data = data; + } + + public String getKey() { + return key; + } + public void setKey( String key ) { + this.key = key; + } + + public PrisonPlaceHolders getPlaceholder() { + return placeholder; + } + public void setPlaceholder( PrisonPlaceHolders placeholder ) { + this.placeholder = placeholder; + } + + public String getData() { + return data; + } + public void setData( String data ) { + this.data = data; + } + + public boolean isPrimary() { + return primary; + } + public void setPrimary( boolean primary ) { + this.primary = primary; + } + + public String getAliasName() { + return aliasName; + } + public void setAliasName( String aliasName ) { + this.aliasName = aliasName; + } +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderIntegration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderIntegration.java index 83644765d..e36cfa22b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderIntegration.java +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderIntegration.java @@ -1,19 +1,20 @@ package tech.mcprison.prison.integration; -import tech.mcprison.prison.internal.Player; - import java.util.function.Function; +import tech.mcprison.prison.internal.Player; + /** * An integration into a placeholder plugin. */ -public interface PlaceholderIntegration extends Integration { - - void registerPlaceholder(String placeholder, Function action); +public abstract class PlaceholderIntegration + extends IntegrationCore { - @Override - default IntegrationType getType() { - return IntegrationType.PLACEHOLDER; - } + public PlaceholderIntegration( String keyName, String providerName ) { + super( keyName, providerName, IntegrationType.PLACEHOLDER ); + + } + + public abstract void registerPlaceholder(String placeholder, Function action); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderProgressBarConfig.java b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderProgressBarConfig.java new file mode 100644 index 000000000..b6d12bc0d --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/PlaceholderProgressBarConfig.java @@ -0,0 +1,70 @@ +package tech.mcprison.prison.integration; + +public class PlaceholderProgressBarConfig { + + private int segments = 20; + + private String positiveColor; + private String positiveSegment; + + private String negativeColor; + private String negativeSegment; + + public PlaceholderProgressBarConfig() { + super(); + + + } + + public PlaceholderProgressBarConfig( int segments, + String positiveColor, String positiveSegment, + String negativeColor, String negativeSegment ) { + super(); + + this.segments = segments; + + this.positiveColor = positiveColor; + this.positiveSegment = positiveSegment; + + this.negativeColor = negativeColor; + this.negativeSegment = negativeSegment; + } + + public int getSegments(){ + return segments; + } + public void setSegments( int segments ) { + this.segments = segments; + } + + public String getPositiveColor() { + return positiveColor; + } + public void setPositiveColor( String positiveColor ) { + this.positiveColor = positiveColor; + } + + public String getPositiveSegment() { + return positiveSegment; + } + public void setPositiveSegment( String positiveSegment ) { + this.positiveSegment = positiveSegment; + } + + public String getNegativeColor() { + return negativeColor; + } + public void setNegativeColor( String negativeColor ) { + this.negativeColor = negativeColor; + } + + public String getNegativeSegment() { + return negativeSegment; + } + public void setNegativeSegment( String negativeSegment ) { + this.negativeSegment = negativeSegment; + } + + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/Placeholders.java b/prison-core/src/main/java/tech/mcprison/prison/integration/Placeholders.java new file mode 100644 index 000000000..6a417ebb5 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/Placeholders.java @@ -0,0 +1,37 @@ +package tech.mcprison.prison.integration; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import tech.mcprison.prison.integration.IntegrationManager.PlaceHolderFlags; + +public interface Placeholders { + + public Map getPlaceholderDetailCounts(); + + + public int getPlaceholderCount(); + + + public int getPlaceholderRegistrationCount(); + + + public String placeholderTranslate(UUID playerUuid, String playerName, String identifier); + + + public String placeholderTranslateText( String text); + + + public String placeholderTranslateText( UUID playerUuid, String playerName, String text); + + + public List placeholderSearch( UUID playerUuid, String playerName, String[] patterns ); + + + public void reloadPlaceholders(); + + + public void printPlaceholderStats(); + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/integration/WorldGuardIntegration.java b/prison-core/src/main/java/tech/mcprison/prison/integration/WorldGuardIntegration.java new file mode 100644 index 000000000..a04e74787 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/integration/WorldGuardIntegration.java @@ -0,0 +1,48 @@ +package tech.mcprison.prison.integration; + +/** + *

WorldGuard Integration is needed because there are two major versions of + * WorldGuard and WorldEdit that needs to be supported. This is required due + * to the wide range of spigot versions that are supported. + *

+ * + *

The two different versions WorldGuard, and WorldEdit, may have different + * command sets and parameters, not to mention different class names and + * possibly different package names too. So these integrations are to + * provide a common interface in to any platform, and any version. Hopefully! lol + *

+ * + *

Version 6.x is for minecraft versions 1.12.2 and below.

+ * + *

Version 7.x is for minecraft versions 1.13.0 and up. + *

+ * + *

It is stated that minecraft implementations for spigot have significant + * changes from one version to the next, so one version of WorldEdit and + * WorldGuard may not support any of the older versions. Due to these restrictions + * and warnings, carefully make sure you download the version of WorldGuard and + * WorldEdit that matches the server you are running. All v6.x are not the same, nor + * are all v7.x the same. The best we can do is to make sure you are using the + * correct major version for the version of minecraft that you are running on + * your server; the correct minor version is on you to get correct. + *

+ * + */ +public class WorldGuardIntegration + extends IntegrationCore { + + private boolean integrated = false; + + public WorldGuardIntegration( String keyName, String providerName ) { + super( keyName, providerName, IntegrationType.WORLDGUARD ); + } + + public boolean isIntegrated() { + return integrated; + } + public void setIntegrated( boolean integrated ) { + this.integrated = integrated; + } + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/CommandSender.java b/prison-core/src/main/java/tech/mcprison/prison/internal/CommandSender.java index 8e88a7142..4f4b3af36 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/CommandSender.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/CommandSender.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,5 +73,8 @@ public interface CommandSender { * @param json The JSON message. Must be in proper format. */ void sendRaw(String json); + + + public boolean isOp(); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/ItemStack.java b/prison-core/src/main/java/tech/mcprison/prison/internal/ItemStack.java index 22e33680c..a4c5d9ee5 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/ItemStack.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/ItemStack.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/OfflineMcPlayer.java b/prison-core/src/main/java/tech/mcprison/prison/internal/OfflineMcPlayer.java new file mode 100644 index 000000000..6f99e3da0 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/OfflineMcPlayer.java @@ -0,0 +1,29 @@ +package tech.mcprison.prison.internal; + +import java.util.UUID; + +public interface OfflineMcPlayer + extends Player { + /** + * Returns the unique identifier for this player. + */ + UUID getUUID(); + + /** + * Returns the player's display name (nickname), which may include colors. + */ + String getDisplayName(); + + /** + * Sets the player's display name (nickname). + * + * @param newDisplayName The new display name. May include colors, amp-prefixed. + */ + void setDisplayName(String newDisplayName); + + /** + * @return Returns true if the player is online, false otherwise. + */ + boolean isOnline(); + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/Player.java b/prison-core/src/main/java/tech/mcprison/prison/internal/Player.java index c359145be..75a3afcf6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/Player.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/Player.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/Scheduler.java b/prison-core/src/main/java/tech/mcprison/prison/internal/Scheduler.java index 5bffb46fe..7eea4e1c3 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/Scheduler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/Scheduler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/World.java b/prison-core/src/main/java/tech/mcprison/prison/internal/World.java index 74ab75a12..9921126e7 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/World.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/World.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +18,11 @@ package tech.mcprison.prison.internal; +import java.util.List; + import tech.mcprison.prison.internal.block.Block; import tech.mcprison.prison.util.Location; -import java.util.List; - /** * Represents a world on the Minecraft server. * @@ -36,7 +36,7 @@ public interface World { */ String getName(); - /** + /**O * Returns a list of all the players in this world. */ List getPlayers(); @@ -46,6 +46,6 @@ public interface World { * * @param location The {@link Location} of the block. */ - Block getBlockAt(Location location); + public Block getBlockAt(Location location); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Block.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Block.java index 143fb9390..92c3793a4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Block.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Block.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,6 +53,10 @@ public interface Block { * @return The {@link BlockType}. */ BlockType getType(); + + + public PrisonBlock getPrisonBlock(); + /** * Sets the block to a different type. @@ -60,7 +64,11 @@ public interface Block { * @param type The new {@link BlockType}. */ void setType(BlockType type); + + + public void setPrisonBlock( PrisonBlock prisonBlock ); + /** * Returns a snapshot of the state of this block, which you can edit to your liking. * diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockFace.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockFace.java index cc3123d8d..d3d38f4a6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockFace.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockFace.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockState.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockState.java index 1cb97310c..01d8c788b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockState.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BlockState.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BrewingStand.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BrewingStand.java index 67d9d8300..c7283a78c 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/BrewingStand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/BrewingStand.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Door.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Door.java index 275aad40a..d7f1a681d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Door.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Door.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/DoubleChest.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/DoubleChest.java index 212534fe4..6e1f1b387 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/DoubleChest.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/DoubleChest.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Lever.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Lever.java index 408e915ed..76ee39d67 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/block/Lever.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/Lever.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlock.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlock.java new file mode 100644 index 000000000..c3e6cda54 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlock.java @@ -0,0 +1,83 @@ +package tech.mcprison.prison.internal.block; + +/** + *

This class embodies the nature of the block and different behaviors, if + * they exist. + *

+ * + */ +public class PrisonBlock { + private String blockName; + + private double chance; + + private boolean valid = true; + private boolean mineable = true; + + private boolean legacyBlock = false; + + public PrisonBlock( String blockName ) { + this( blockName, 0); + } + + public PrisonBlock( String blockName, double chance ) { + super(); + + this.blockName = blockName; + this.chance = chance; + } + + public String getBlockName() { + return blockName; + } + public void setBlockName( String blockName ) { + this.blockName = blockName; + } + + public double getChance() { + return chance; + } + public void setChance( double chance ) { + this.chance = chance; + } + + public boolean isValid() { + return valid; + } + public void setValid( boolean valid ) { + this.valid = valid; + } + + public boolean isMineable() { + return mineable; + } + public void setMineable( boolean mineable ){ + this.mineable = mineable; + } + + /** + *

This value isLegacyBlock indicates that there was not a direct match + * with the stored (saved) name of the block, and list of valid block types + * for the server. In order to find a successful match, had to use the + * obsolete BlockType to make a connection to the valid block types. + *

+ * + *

If this value is set to true, it only has a purpose during the loading + * of the mine when the server is starting up. If it is set to true, then it + * indicates that the block name that was saved to disk is not directly translatable + * to a valid block type, and therefore once the mine is loaded, if any of the + * blocks have this set, then the mine must be saved to store the + * correct block name. The next time it is loaded, it will not have to + * fall back to the legacy BlockType for a conversion. + *

+ * + * @return + */ + public boolean isLegacyBlock() { + return legacyBlock; + } + public void setLegacyBlock( boolean legacyBlock ) { + this.legacyBlock = legacyBlock; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlockTypes.java b/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlockTypes.java new file mode 100644 index 000000000..3bcf90bbf --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/block/PrisonBlockTypes.java @@ -0,0 +1,56 @@ +package tech.mcprison.prison.internal.block; + +import java.util.ArrayList; +import java.util.List; + +import tech.mcprison.prison.Prison; + +/** + *

This class is a new way of dealing with blocks within prison. + * All blocks will be stored and used as string values. + *

+ * + */ +public class PrisonBlockTypes { + + private List blockTypes; + + public enum InternalBlockTypes { + IGNORE, + NULL_BLOCK + } + + public PrisonBlockTypes() { + super(); + + this.blockTypes = new ArrayList<>(); + + + } + + public void loadServerBlockTypes() { + + // First clear the blockTypes: + getBlockTypes().clear(); + + // Add in the internal block types and mark them as not mineable. + for ( InternalBlockTypes iBlockType : InternalBlockTypes.values() ) { + PrisonBlock block = new PrisonBlock( iBlockType.name() ); + block.setMineable( false ); + + getBlockTypes().add( block ); + } + + // Next using the server's platform, load all of the available blockTypes. + Prison.get().getPlatform().getAllPlatformBlockTypes( getBlockTypes() ); + + } + + public List getBlockTypes() { + return blockTypes; + } + public void setBlockTypes( List blockTypes ) { + this.blockTypes = blockTypes; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/Cancelable.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/Cancelable.java index f3f164c18..3de1605e4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/Cancelable.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/Cancelable.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockBreakEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockBreakEvent.java index 58f7d4e13..149c4bcaa 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockBreakEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockBreakEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockPlaceEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockPlaceEvent.java index 64908271e..488454272 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockPlaceEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/block/BlockPlaceEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/BrewEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/BrewEvent.java index 221366150..7aab8134f 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/BrewEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/BrewEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/CraftItemEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/CraftItemEvent.java index 4a226bdd1..b0b2a3504 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/CraftItemEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/CraftItemEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceBurnEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceBurnEvent.java index 976b2afce..6b33c8231 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceBurnEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceBurnEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceExtractEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceExtractEvent.java index eda9f3bba..fd5a7effd 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceExtractEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceExtractEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceSmeltEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceSmeltEvent.java index 6cbbaf639..999875c01 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceSmeltEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/FurnaceSmeltEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryClickEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryClickEvent.java index b126b4692..066e05fc7 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryClickEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryClickEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCloseEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCloseEvent.java index 3bebc8667..23dfc5ba2 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCloseEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCloseEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCreativeEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCreativeEvent.java index 2992e624d..ec2873ab7 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCreativeEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryCreativeEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryDragEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryDragEvent.java index 997ead5d8..d18902205 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryDragEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryDragEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryEvent.java index 294de8aa3..8f1b685c5 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryInteractEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryInteractEvent.java index aa561a945..fbabf137b 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryInteractEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryInteractEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryMoveItemEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryMoveItemEvent.java index 166e4d66f..3682e7f13 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryMoveItemEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryMoveItemEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryOpenEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryOpenEvent.java index 205d50305..bcedbd3b6 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryOpenEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/InventoryOpenEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareAnvilEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareAnvilEvent.java index daa97a7a7..055cfa904 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareAnvilEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareAnvilEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareItemCraftEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareItemCraftEvent.java index 315a825e1..008830e64 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareItemCraftEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/inventory/PrepareItemCraftEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerChatEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerChatEvent.java index e864150a9..571559337 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerChatEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerChatEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerDropItemEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerDropItemEvent.java index 76f468fc7..92146fef0 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerDropItemEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerDropItemEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerInteractEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerInteractEvent.java index 42bc9daf4..0f34ec64f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerInteractEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerInteractEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerJoinEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerJoinEvent.java index 0c5a10d17..b3df23d64 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerJoinEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerJoinEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerKickEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerKickEvent.java index 5e28acced..a41183f0d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerKickEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerKickEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerPickUpItemEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerPickUpItemEvent.java index c33059107..3d54073d6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerPickUpItemEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerPickUpItemEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerQuitEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerQuitEvent.java index 30227151c..13d12d74d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerQuitEvent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/player/PlayerQuitEvent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/events/world/PrisonWorldLoadEvent.java b/prison-core/src/main/java/tech/mcprison/prison/internal/events/world/PrisonWorldLoadEvent.java new file mode 100644 index 000000000..99c01bd09 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/events/world/PrisonWorldLoadEvent.java @@ -0,0 +1,33 @@ +package tech.mcprison.prison.internal.events.world; + +/** + *

This event monitors the loading of worlds. During the loading of mines, not + * all worlds may be loaded yet, so we need to monitor when they come online + * so we can hook them up to the mines. + *

+ * + *

At this point in time, all we need to know is the world name when it loads. + * All other uses of the world can go through the standard procedures since + * it will then be fully loaded. The key is knowing when the world is avialble + * for use. + *

+ * + */ +public class PrisonWorldLoadEvent { + + private String worldName; + + public PrisonWorldLoadEvent(String worldName) { + super(); + + this.worldName = worldName; + } + + public String getWorldName() { + return worldName; + } + public void setWorldName( String worldName ) { + this.worldName = worldName; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/AnvilInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/AnvilInventory.java index 297491411..8610602c5 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/AnvilInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/AnvilInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BeaconInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BeaconInventory.java index 07e86ed21..5367e2be2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BeaconInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BeaconInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BrewerInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BrewerInventory.java index 1ad58b063..9d601a0d4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BrewerInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/BrewerInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/CraftingInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/CraftingInventory.java index 9012f4ebb..3cb8b8cc2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/CraftingInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/CraftingInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/DoubleChestInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/DoubleChestInventory.java index 51ebfaa8b..33478166d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/DoubleChestInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/DoubleChestInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/EnchantingInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/EnchantingInventory.java index 0d69cd9ec..8cc72e4eb 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/EnchantingInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/EnchantingInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/FurnaceRecipe.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/FurnaceRecipe.java index 5402875d5..d38fd8adb 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/FurnaceRecipe.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/FurnaceRecipe.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Inventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Inventory.java index 9d9003806..1aa117814 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Inventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Inventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryHolder.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryHolder.java index 41b3f572b..16e40a61a 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryHolder.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryHolder.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryType.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryType.java index 48dcbb6c1..d1bc54828 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryType.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/InventoryType.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/PlayerInventory.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/PlayerInventory.java index ec97c31a1..723b4b11d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/PlayerInventory.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/PlayerInventory.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Recipe.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Recipe.java index e894aeab5..734c550ce 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Recipe.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Recipe.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapedRecipe.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapedRecipe.java index 045471a71..73cee8c90 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapedRecipe.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapedRecipe.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapelessRecipe.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapelessRecipe.java index 147daf2af..883aed4f2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapelessRecipe.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/ShapelessRecipe.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Viewable.java b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Viewable.java index 958df5498..759268132 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Viewable.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/inventory/Viewable.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/internal/package-info.java index bcddaa1a7..479705078 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Capability.java b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Capability.java index bd8c6013e..bbf871610 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Capability.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Capability.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/IncapableException.java b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/IncapableException.java index e7d04b9df..c29b09285 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/IncapableException.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/IncapableException.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,9 @@ * @since API 1.0 */ public class IncapableException extends RuntimeException { - - private Capability capability; + private static final long serialVersionUID = 1L; + + private Capability capability; public IncapableException(Capability capability) { this.capability = capability; diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Platform.java b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Platform.java index f4a60b486..4a4b8f4b7 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Platform.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/platform/Platform.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,21 +18,26 @@ package tech.mcprison.prison.internal.platform; +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + import tech.mcprison.prison.commands.PluginCommand; +import tech.mcprison.prison.file.YamlFileIO; import tech.mcprison.prison.gui.GUI; +import tech.mcprison.prison.integration.Placeholders; +import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.Scheduler; import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.internal.scoreboard.ScoreboardManager; +import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.store.Storage; import tech.mcprison.prison.util.Location; -import java.io.File; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - /** * Represents an internal platform that Prison has been implemented for. * The internal platform is responsible for connecting Prison's APIs to the underlying server API. @@ -48,6 +53,18 @@ public interface Platform { */ Optional getWorld(String name); + + /** + *

This function allows the PrisonCommand to get a list of any possible world + * load failures. + *

+ * + * @param display + * @return + */ + public void getWorldLoadErrors( ChatDisplay display ); + + /** * Returns the player with the specified name. */ @@ -63,6 +80,12 @@ public interface Platform { */ List getOnlinePlayers(); +// NOTE: Disabling for now. There is an internal failure within the Prison code base when trying +// to use this, so will revisit in the future. + public Optional getOfflinePlayer(String name); + + public Optional getOfflinePlayer(UUID uuid); + /** * Returns the plugin's version. */ @@ -101,6 +124,14 @@ public interface Platform { */ void dispatchCommand(String cmd); + /** + * Runs a command as the sender and with only the sender's privileges. + * + * @param sender + * @param cmd + */ + public void dispatchCommand(CommandSender sender, String cmd); + /** * Returns the {@link Scheduler}, which can be used to schedule tasks. */ @@ -203,4 +234,37 @@ default Optional getCommand(String label) { * This is a configuration option.kkjksdf;erljnkx.jcsmka.f.fdlwe;s.x. frrer5 */ boolean shouldShowAlerts(); + + + + public void identifyRegisteredPlugins(); + + + + public Placeholders getPlaceholders(); + + + + public YamlFileIO getYamlFileIO( File yamlFile ); + + + public void reloadConfig(); + + + public String getConfigString( String key ); + + + public boolean getConfigBooleanFalse( String key ); + + + public boolean getConfigBooleanTrue( String key ); + + + public void getAllPlatformBlockTypes( List blockTypes ); + + + public PrisonBlock getPrisonBlock( String blockName ); + + + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/DisplaySlot.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/DisplaySlot.java index bde846e94..c6b1ce3c8 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/DisplaySlot.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/DisplaySlot.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Objective.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Objective.java index 8092d0150..6fc79ae18 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Objective.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Objective.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Score.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Score.java index d430921c5..7475ce682 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Score.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Score.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Scoreboard.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Scoreboard.java index 1b7dba3c7..cb9198089 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Scoreboard.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Scoreboard.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/ScoreboardManager.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/ScoreboardManager.java index 9b9d300b7..5a3210fa9 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/ScoreboardManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/ScoreboardManager.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Team.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Team.java index a302a4d3e..c5ba82189 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Team.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/Team.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/package-info.java index 0b42ed367..c2013c71d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/internal/scoreboard/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/jackson/JacksonYaml.java b/prison-core/src/main/java/tech/mcprison/prison/jackson/JacksonYaml.java new file mode 100644 index 000000000..45feaa17e --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/jackson/JacksonYaml.java @@ -0,0 +1,204 @@ +package tech.mcprison.prison.jackson; + +/** + *

This class uses Jackson's YAML databind tools to + * read and write to yaml configuration files. + *

+ * + *

What makes this different, is that the yaml is built + * upon a hierarchy, but when loaded, it must be flattened + * using dot notation to represent the levels within the + * key. + *

+ * + *

Since values of the various properties can have + * different types, internally the Jackson's ValueNode object + * is used since it is extended to contain various kinds of + * values. See list: + *

+ * + *

Possible ValueNodes values: + *

+ *
    + *
  • com.fasterxml.jackson.databind.node.ValueNode
  • + *
  • com.fasterxml.jackson.databind.node.BooleanNode
  • + *
  • com.fasterxml.jackson.databind.node.TextNode
  • + *
  • com.fasterxml.jackson.databind.node.IntNode
  • + *
  • com.fasterxml.jackson.databind.node.DoubleNode
  • + *
+ * + *

Collections based nodes that extend from ValueNode: + *

+ *
    + *
  • com.fasterxml.jackson.databind.node.ArrayNode
  • + *
  • com.fasterxml.jackson.databind.node.ObjectNode (a Map)
  • + *
+ * + */ +public class JacksonYaml { + + public JacksonYaml() { + super(); + + } + +// /** +// *

This function will read a yaml file, which loads as a hierarchical +// * map. Then it will flatten the hierarchical map and return it. +// *

+// * +// * +// * @param file +// */ +// public Map loadYamlConfigFile( File file ) { +// Map map = null; +// +// ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); +// +// // If using something like a custom date formatter, etc: +// mapper.findAndRegisterModules(); +// +// +// try { +// JsonNode tree = mapper.readTree( file ); +// +// map = flattenJson( tree ); +// +//// for (Map.Entry kv : map.entrySet()) { +//// System.out.println(kv.getKey() + ": " + +//// kv.getValue().asText() + " [" + +//// kv.getValue().getClass() + "]"); +//// } +// } +// catch ( IOException e ) { +// Output.get().logError( String.format( "JacksonYaml.loadYamlConfigFile: " + +// "Failure: file= %s :: %s ", file.getAbsoluteFile(), e.getMessage() )); +// } +// +// return map; +// } +// +// +// +// private Map flattenJson(JsonNode input) { +// Map map = new LinkedHashMap<>(); +// flattenJson(input, null, map); +// return map; +// } +// +// private void flattenJson(JsonNode node, String parent, Map map) { +// if (node instanceof ValueNode) { +// map.put(parent, (ValueNode)node); +// } +// else { +// String prefix = parent == null ? "" : parent + "."; +// if (node instanceof ArrayNode) { +// ArrayNode arrayNode = (ArrayNode)node; +// for(int i = 0; i < arrayNode.size(); i++) { +// flattenJson(arrayNode.get(i), prefix + i, map); +// } +// } +// else if (node instanceof ObjectNode) { +// ObjectNode objectNode = (ObjectNode) node; +// for (Iterator> it = objectNode.fields(); it.hasNext(); ) { +// Map.Entry field = it.next(); +// flattenJson(field.getValue(), prefix + field.getKey(), map); +// } +// } +// else { +// Output.get().logWarn( String.format( "JacksonYaml.flattenJson: " + +// "Warning: Unknown node type. node= %s ", node.getClass())); +// +// } +// } +// } +// +// +// +// /** +// *

This function will write a flat map to a yaml file after it +// * expands it to an hierarchical map, expanding the key values on +// * their periods. +// *

+// * +// * @param file Target file to save to +// * @param map A flat map of all the configs +// */ +// public void writeYamlConfigFile( File file, Map map ) { +// +// String filenameTemp = file.getName() + ".tmp.yml"; +// File fileTemp = new File( file.getParentFile(), filenameTemp ); +// +// ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); +// +// // If using something like a custom date formatter, etc: +// mapper.findAndRegisterModules(); +// +// try { +// Map mapExpanded = expandMap( map ); +// +// // Write to the temp file: +// mapper.writeValue( fileTemp, mapExpanded ); +// +// // Delete the original file if it exists: +// if ( file.exists() ) { +// file.delete(); +// } +// +// // Rename the temp file to the actual file: +// fileTemp.renameTo( file ); +// } +// catch ( IOException e ) { +// Output.get().logError( String.format( "JacksonYaml.writeYamlConfigFile: " + +// "Failure: file= %s :: %s ", file.getAbsoluteFile(), e.getMessage() )); +// } +// } +// +// /** +// *

This takes a flat map and expands the keys, breaking on the periods, +// * and builds a multi-level hierarchy. +// *

+// * +// * @param map Flat map +// * @return Hierarchical map +// */ +// private Map expandMap( Map map ) { +// Map m = new LinkedHashMap<>(); +// +// for ( Entry node : map.entrySet() ) { +// +// String key = node.getKey(); +// +// if ( key != null ) { +// +// ValueNode value = node.getValue(); +// +// String[] keyz = key.split( "\\." ); +// +// // There are multiple depths and we must place the value at the +// // leaf nodes. getChildExpandedMap traverses all the children +// // to get to the leaf node, making the nodes if needed. +// Map child = getChildExpandedMap(m, keyz, 0); +// +// child.put( keyz[keyz.length-1], value ); +// } +// } +// +// return m; +// } +// +// @SuppressWarnings( "unchecked" ) +// private Map getChildExpandedMap( Map map, String[] keyz, int pos ) { +// if ( pos < keyz.length - 1 ) { +// String key = keyz[pos]; +// if ( !map.containsKey( key ) ) { +// map.put( key, new LinkedHashMap<>() ); +// } +// return getChildExpandedMap( (Map) map.get( key ), keyz, pos + 1); +// } +// +// return map; +// } + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/localization/LocaleManager.java b/prison-core/src/main/java/tech/mcprison/prison/localization/LocaleManager.java index 1a79fa795..22d7a99b6 100755 --- a/prison-core/src/main/java/tech/mcprison/prison/localization/LocaleManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/localization/LocaleManager.java @@ -29,11 +29,15 @@ package tech.mcprison.prison.localization; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; import java.security.CodeSource; import java.util.Arrays; import java.util.HashMap; @@ -43,7 +47,11 @@ import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; + +import tech.mcprison.prison.Prison; import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleManager; import tech.mcprison.prison.modules.PluginEntity; /** @@ -106,7 +114,11 @@ public class LocaleManager { public LocaleManager(PluginEntity module, String internalPath) { this.module = module; this.internalPath = internalPath; + + // Load the shipped locales first first from the prison jar file: loadShippedLocales(); + + // Then any custom locales will overried and replace the internal locales: loadCustomLocales(); // custom locales will override } @@ -116,6 +128,9 @@ public LocaleManager(PluginEntity module) { private void loadCustomLocales() { File dataFolder = getOwningPlugin().getDataFolder(); + + dataFolder = fixPrisonCoreLanguagePath( dataFolder ); + if (dataFolder.isDirectory()) { File localeFolder = new File(dataFolder, LOCALE_FOLDER); if (localeFolder.exists()) { @@ -150,9 +165,97 @@ private void loadCustomLocales() { + " is not a directory - not loading custom locales"); } } + else { + // The local custom lang directory doesn't exist so create it: + localeFolder.mkdirs(); + + // Now copy all of the default language files that are in the prison jar to this new directory. + // This will make it a lot easier for admins to modify the language files. + extractShippedLocales( localeFolder ); + + } } } + /** + *

The Prison core is not truly a Prison module, so the targetPath that is passed to this function + * is not correctly setup to place the core/lang directory in the correct place. It will try + * to put it in /plugins/Prison/lang which is wrong. This function detects this error and + * will regenerate the targetPath to be /plugins/Prison/module_conf/core/lang. + *

+ * + *

Note: There is a unit test that is ran by the gradle build process that hits this + * function. At the time, the value of PrisonAPI.getModuleManager() is null due to the unit test, + * therefore the following function is using static functions to ensure there is not a + * null pointer exception thrown. The unit test does not do anything with the content of + * these directories, but it still hits these functions, so allowing it to proceed, even if the + * paths are incorrect, will have no impact on anything. + *

+ * + * @param targetPath + * @return The corrected targetPath for the core plugin + */ + private File fixPrisonCoreLanguagePath( File targetPath ) { + if ( !targetPath.getAbsolutePath().startsWith( + ModuleManager.getModuleRootDefault().getAbsolutePath() ) ) { + targetPath = Module.setupDataFolder( Prison.PSEDUO_MODLE_NAME ); + } + return targetPath; + } + + private void extractShippedLocales( File targetPath ) { + + targetPath = fixPrisonCoreLanguagePath( targetPath ); + + CodeSource cs = getOwningPlugin().getClass().getProtectionDomain().getCodeSource(); + if (cs != null) { + try { + URL jar = cs.getLocation(); + ZipInputStream zip = new ZipInputStream(jar.openStream()); + ZipEntry entry; + while ((entry = zip.getNextEntry()) != null) { + String entryName = entry.getName(); + + if (entryName.startsWith(internalPath) && entryName.endsWith(".properties")) { + + String[] arr = entryName.split("/"); + String localeName = arr[arr.length - 1]; + + File newLocal = new File( targetPath, localeName ); + + BufferedInputStream inStream = new BufferedInputStream( zip ); + try ( + OutputStream outStream = Files.newOutputStream( newLocal.toPath(), + StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE ); + ) { + copyStreams( inStream, outStream) ; + } + } + + } + } catch (IOException ex) { + throw new RuntimeException( + "Failed to initialize LocaleManager for plugin " + getOwningPlugin() + + " - Prison cannot continue to load.", ex); + } + } else { + throw new RuntimeException("LocalManager.extractShippedLocales(): " + + "Failed to load code source for plugin " + getOwningPlugin() + + " - Prison cannot continue to load."); + } + + } + + private void copyStreams(InputStream inStream, OutputStream outStream) + throws IOException { + byte[] buf = new byte[8192]; + int length; + while ((length = inStream.read(buf)) > 0) { + outStream.write(buf, 0, length); + } + } + + private void loadShippedLocales() { CodeSource cs = getOwningPlugin().getClass().getProtectionDomain().getCodeSource(); if (cs != null) { @@ -181,6 +284,7 @@ private void loadShippedLocales() { } private void loadLocale(String name, InputStream is, boolean printStackTrace) { + try { Properties temp = new Properties(); temp.load(is); diff --git a/prison-core/src/main/java/tech/mcprison/prison/localization/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/localization/package-info.java index f23816076..0eb959ae4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/localization/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/localization/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/modules/Module.java b/prison-core/src/main/java/tech/mcprison/prison/modules/Module.java index 6b9b08d3b..131683eeb 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/modules/Module.java +++ b/prison-core/src/main/java/tech/mcprison/prison/modules/Module.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,11 @@ package tech.mcprison.prison.modules; +import java.io.File; + import tech.mcprison.prison.Prison; -import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.error.ErrorManager; -import java.io.File; - /** * Represents a module, which is a part of Prison that can be enabled and * disabled independently from the rest of the modules. @@ -57,16 +56,42 @@ public abstract class Module implements PluginEntity { public Module(String name, String version, int target) { this.name = name; this.version = version; - this.dataFolder = new File(PrisonAPI.getModuleManager().getModuleRoot(), - name.toLowerCase().replace(" ", "_")); this.apiTarget = target; + + this.dataFolder = setupDataFolder( name ); + this.status = new ModuleStatus(); - if (!this.dataFolder.exists()) { - this.dataFolder.mkdir(); - } this.errorManager = new ErrorManager(this); } + + public static File setupDataFolder( String name ) { + File dataFolder = new File(ModuleManager.getModuleRootDefault(), + name.toLowerCase().replace(" ", "_")); + if (!dataFolder.exists()) { + dataFolder.mkdir(); + } + return dataFolder; + } + + /** + *

The getBaseCommands() should return all of the base commands that + * would apply to the module. It should include the / as in /mines or + * the /ranks command. It should be viewed as being ran from in game + * to be consistent. + *

+ * + *

An example of what this would return would be: + *

+ * + *
    + *
  • Mines plugin: "&2/mines"
  • + *
  • Ranks plugin: "&2/ranks &2/rankup"
  • + *
+ * + * @return + */ + abstract public String getBaseCommands(); /* * Methods, to be overridden */ diff --git a/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleManager.java b/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleManager.java index 6d4c91b08..d88a57f7e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleManager.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,17 +35,28 @@ */ public class ModuleManager { + public static final String MODULE_MANAGER_DIRECTORY = "module_conf"; + private List modules; + private List disabledModules; private File moduleRoot; public ModuleManager() { modules = new ArrayList<>(); - moduleRoot = new File(PrisonAPI.getPluginDirectory(), "module_conf"); - if (!moduleRoot.exists()) { - moduleRoot.mkdir(); - } + disabledModules = new ArrayList<>(); + + moduleRoot = getModuleRootDefault(); } + public static File getModuleRootDefault() { + File moduleRoot = new File(PrisonAPI.getPluginDirectory(), MODULE_MANAGER_DIRECTORY); + if (!moduleRoot.exists()) { + moduleRoot.mkdir(); + } + + return moduleRoot; + } + /** * Register a new module. */ @@ -77,7 +88,7 @@ private void validateVersion(Module module) { */ public boolean enableModule(Module module) { long startTime = System.currentTimeMillis(); - Output.get().logInfo("%s enable start...", module.getName()); + Output.get().logInfo("%s Module enablement starting...", module.getName()); module.setEnabled(true); module.enable(); @@ -85,12 +96,13 @@ public boolean enableModule(Module module) { if (module.getStatus().getStatus() != ModuleStatus.Status.ENABLED) { // Anything else and we assume that the enable failed. - Output.get().logInfo("%s enable &cfailed&f, in %d milliseconds.", module.getName(), - (System.currentTimeMillis() - startTime)); + + Output.get().logInfo("%s Module enablement &cfailed&f in %d milliseconds. &d[%s&d]", module.getName(), + (System.currentTimeMillis() - startTime), module.getStatus().getMessage() ); return false; } - Output.get().logInfo("%s enable succeeded, in %d milliseconds.", module.getName(), + Output.get().logInfo("%s Module enabled successfully in %d milliseconds.", module.getName(), (System.currentTimeMillis() - startTime)); return true; } @@ -124,6 +136,8 @@ public void disableModule(Module module) { public void unregisterAll() { modules.forEach(this::disableModule); modules.clear(); + + disabledModules.clear(); } /** @@ -149,7 +163,11 @@ public List getModules() { return modules; } - public File getModuleRoot() { + public List getDisabledModules() { + return disabledModules; + } + + public File getModuleRoot() { return moduleRoot; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleStatus.java b/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleStatus.java index 002426f7c..d7f7a4de8 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleStatus.java +++ b/prison-core/src/main/java/tech/mcprison/prison/modules/ModuleStatus.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -74,6 +74,21 @@ public Status getStatus() { return status; } + /** + *

Displays the associated test based upon the three combinations + * of status codes. + *

+ * + * @return + */ + public String getStatusText() { + return (getStatus() == ModuleStatus.Status.ENABLED ? + "&2Enabled" : + (getStatus() == ModuleStatus.Status.FAILED ? + "&cFailed" : "&9&m-Disabled-" )); + } + + /* * Getters & Setters */ @@ -89,6 +104,14 @@ public String getMessage() { public void setMessage(String message) { this.message = message; } + + public void addMessage(String message) { + if ( this.message == null ) { + setMessage(message); + } else { + setMessage( getMessage() + ". " + message); + } + } public enum Status { ENABLED, DISABLED, FAILED diff --git a/prison-core/src/main/java/tech/mcprison/prison/modules/PluginEntity.java b/prison-core/src/main/java/tech/mcprison/prison/modules/PluginEntity.java index c27cc7602..03b3da3f6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/modules/PluginEntity.java +++ b/prison-core/src/main/java/tech/mcprison/prison/modules/PluginEntity.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/modules/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/modules/package-info.java index 86de0ce4a..7f8fdba95 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/modules/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/modules/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/BulletedListComponent.java b/prison-core/src/main/java/tech/mcprison/prison/output/BulletedListComponent.java index 9f3b62aab..82941c26d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/BulletedListComponent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/BulletedListComponent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/ChatDisplay.java b/prison-core/src/main/java/tech/mcprison/prison/output/ChatDisplay.java index 462e6b481..8d3bc8e72 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/ChatDisplay.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/ChatDisplay.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -74,5 +74,12 @@ public void send(CommandSender sender) { component.send(sender); } } + + public void toLog(LogLevel logLevel) { + Output.get().log( title, logLevel ); + for (DisplayComponent component : displayComponents) { + Output.get().log( component.text(), logLevel ); + } + } } diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/DisplayComponent.java b/prison-core/src/main/java/tech/mcprison/prison/output/DisplayComponent.java index c239a48e4..1e790027b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/DisplayComponent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/DisplayComponent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/FancyMessageComponent.java b/prison-core/src/main/java/tech/mcprison/prison/output/FancyMessageComponent.java index c90c7eb5c..c8769183b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/FancyMessageComponent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/FancyMessageComponent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/LogLevel.java b/prison-core/src/main/java/tech/mcprison/prison/output/LogLevel.java index de4785521..6b8c6fc68 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/LogLevel.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/LogLevel.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/Output.java b/prison-core/src/main/java/tech/mcprison/prison/output/Output.java index f57e6b0cb..690cb0040 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/Output.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/Output.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +18,11 @@ package tech.mcprison.prison.output; +import java.util.Arrays; + import tech.mcprison.prison.Prison; import tech.mcprison.prison.internal.CommandSender; -import java.util.Arrays; - /** * Standardized output to the console and to players. * @@ -64,9 +64,13 @@ public String format(String message, LogLevel level, Object... args) { * Log a message with a specified {@link LogLevel} */ public void log(String message, LogLevel level, Object... args) { - Prison.get().getPlatform().log(gen("&3Prison") + " " + (level == LogLevel.INFO ? - "&f" : - level == LogLevel.WARNING ? "&6" : "&c") + String.format(message, args)); + if ( Prison.get() == null || Prison.get().getPlatform() == null ) { + System.err.println("Prison: Output.log Logger failure: " + message ); + } else { + Prison.get().getPlatform().log(gen("&3Prison") + " " + (level == LogLevel.INFO ? + "&f" : + level == LogLevel.WARNING ? "&6" : "&c") + String.format(message, args)); + } } /** diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/TextComponent.java b/prison-core/src/main/java/tech/mcprison/prison/output/TextComponent.java index 19f1bcfec..7336b483b 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/TextComponent.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/TextComponent.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/output/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/output/package-info.java index fbfbf44e3..0954a2a44 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/output/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/output/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/selection/Selection.java b/prison-core/src/main/java/tech/mcprison/prison/selection/Selection.java index 943260cd3..d05003fef 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/selection/Selection.java +++ b/prison-core/src/main/java/tech/mcprison/prison/selection/Selection.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionListener.java b/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionListener.java index b884f7d0c..4c63844d7 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionListener.java +++ b/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionListener.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionManager.java b/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionManager.java index 0484097ac..048e435b3 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/selection/SelectionManager.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,7 +56,14 @@ public Selection getSelection(Player player) { } return selectionMap.get(player.getName()); } + + public void clearSelection(Player player) { + if (!selectionMap.containsKey(player.getName())) { + selectionMap.remove(player.getName()); + } + } + public void setSelection(Player player, Selection selection) { selectionMap.put(player.getName(), selection); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/selection/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/selection/package-info.java index 4ba71405b..38b692d05 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/selection/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/selection/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSortable.java b/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSortable.java new file mode 100644 index 000000000..f1dea1c2e --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSortable.java @@ -0,0 +1,5 @@ +package tech.mcprison.prison.sorting; + +public interface PrisonSortable { + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSorter.java b/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSorter.java new file mode 100644 index 000000000..5ff76a53f --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/sorting/PrisonSorter.java @@ -0,0 +1,10 @@ +package tech.mcprison.prison.sorting; + +import java.util.Set; + +public abstract class PrisonSorter { + + + public abstract Set getSortedSet(); + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/BlockType.java b/prison-core/src/main/java/tech/mcprison/prison/util/BlockType.java index 2623dec61..f5538f0f1 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/BlockType.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/BlockType.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +18,9 @@ package tech.mcprison.prison.util; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -27,7 +29,19 @@ /** - * All of the blocks in the game. + *

All of the blocks in the game. + *

+ * + *

The new field altNames contains a list of String values that will help + * XMaterial map these types to valid Material types. These are spigot version + * Dependent, so some blocks may have different altNames for different spigot + * versions. + *

+ * + *

XMaterial support for spigot 1.8.8: + * MOSS_STONE = mossy_cobblestone, LAPIS_LAZULI_ORE = "lapis_ore", + * LAPIS_LAZULI_BLOCK = "lapis_block", PILLAR_QUARTZ_BLOCK = "quartz_pillar" + *

* * @author Faizaan A. Datoo * @author Camouflage100 @@ -44,8 +58,43 @@ public enum BlockType { * of the Material. */ + IGNORE( -1, "prison:ignore", -1, MaterialType.BLOCK ), + NULL_BLOCK( -2, "prison:null_block", -1, MaterialType.INVALID ), + + // This was auto-generated from WorldEdit's blocks.json // @formatter:off + + + // NOTE: Double slabs are ones that players cannot naturally place, and they are + // similar to the main block type. It appears like they have been replaced, + // but not sure with what. I suspect no one will use them anyway, and if they + // do, then mapping them to their counter part block. There are a few doubles + // that have been mapped to "smooth" in the later versions, so they are used when + // possible. + + + DOUBLE_STONE_SLAB( 43, "minecraft:double_stone_slab", 0, MaterialType.BLOCK, "smooth_stone" ), + DOUBLE_SANDSTONE_SLAB( 43, "minecraft:double_stone_slab", 1, MaterialType.BLOCK, "smooth_sandstone_slab" ), + DOUBLE_WOODEN_SLAB( 43, "minecraft:double_stone_slab", 2, MaterialType.BLOCK, "OAK_PLANKS" ), + DOUBLE_COBBLESTONE_SLAB( 43, "minecraft:double_stone_slab", 3, MaterialType.BLOCK, "COBBLESTONE" ), + DOUBLE_BRICK_SLAB( 43, "minecraft:double_stone_slab", 4, MaterialType.BLOCK, "BRICKS" ), + DOUBLE_STONE_BRICK_SLAB( 43, "minecraft:double_stone_slab", 5, MaterialType.BLOCK, "STONE_BRICKS" ), + DOUBLE_NETHER_BRICK_SLAB( 43, "minecraft:double_stone_slab", 6, MaterialType.BLOCK, "NETHER_BRICKS" ), + DOUBLE_QUARTZ_SLAB( 43, "minecraft:double_stone_slab", 7, MaterialType.BLOCK, "SMOOTH_QUARTZ" ), + + DOUBLE_OAK_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 0, MaterialType.BLOCK, "OAK_PLANKS" ), + DOUBLE_SPRUCE_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 1, MaterialType.BLOCK, "SPRUCE_PLANKS" ), + DOUBLE_BIRCH_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 2, MaterialType.BLOCK, "BIRCH_PLANKS" ), + DOUBLE_JUNGLE_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 3, MaterialType.BLOCK, "JUNGLE_PLANKS" ), + DOUBLE_ACACIA_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 4, MaterialType.BLOCK, "ACACIA_PLANKS" ), + DOUBLE_DARK_OAK_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 5, MaterialType.BLOCK, "DARK_OAK_PLANKS" ), + + DOUBLE_RED_SANDSTONE_SLAB( 181, "minecraft:double_stone_slab2", 0, MaterialType.BLOCK, "RED_SANDSTONE" ), + PURPUR_DOUBLE_SLAB( 204, "minecraft:purpur_double_slab", 0, MaterialType.BLOCK, "PURPUR_BLOCK" ), + + + AIR( 0, "minecraft:air", 0, MaterialType.BLOCK ), STONE( 1, "minecraft:stone", 0, MaterialType.BLOCK ), GRANITE( 1, "minecraft:stone", 1, MaterialType.BLOCK ), @@ -59,44 +108,52 @@ public enum BlockType { COARSE_DIRT( 3, "minecraft:dirt", 1, MaterialType.BLOCK ), PODZOL( 3, "minecraft:dirt", 2, MaterialType.BLOCK ), COBBLESTONE( 4, "minecraft:cobblestone", 0, MaterialType.BLOCK ), - OAK_WOOD_PLANK( 5, "minecraft:planks", 0, MaterialType.BLOCK ), - SPRUCE_WOOD_PLANK( 5, "minecraft:planks", 1, MaterialType.BLOCK ), - BIRCH_WOOD_PLANK( 5, "minecraft:planks", 2, MaterialType.BLOCK ), - JUNGLE_WOOD_PLANK( 5, "minecraft:planks", 3, MaterialType.BLOCK ), - ACACIA_WOOD_PLANK( 5, "minecraft:planks", 4, MaterialType.BLOCK ), - DARK_OAK_WOOD_PLANK( 5, "minecraft:planks", 5, MaterialType.BLOCK ), + + OAK_WOOD_PLANK( 5, "minecraft:planks", 0, MaterialType.BLOCK, "OAK_PLANKS" ), + SPRUCE_WOOD_PLANK( 5, "minecraft:planks", 1, MaterialType.BLOCK, "SPRUCE_PLANKS" ), + BIRCH_WOOD_PLANK( 5, "minecraft:planks", 2, MaterialType.BLOCK, "BIRCH_PLANKS" ), + JUNGLE_WOOD_PLANK( 5, "minecraft:planks", 3, MaterialType.BLOCK, "JUNGLE_PLANKS" ), + ACACIA_WOOD_PLANK( 5, "minecraft:planks", 4, MaterialType.BLOCK, "ACACIA_PLANKS" ), + DARK_OAK_WOOD_PLANK( 5, "minecraft:planks", 5, MaterialType.BLOCK, "DARK_OAK_PLANKS" ), + OAK_SAPLING( 6, "minecraft:sapling", 0, MaterialType.BLOCK ), SPRUCE_SAPLING( 6, "minecraft:sapling", 1, MaterialType.BLOCK ), BIRCH_SAPLING( 6, "minecraft:sapling", 2, MaterialType.BLOCK ), JUNGLE_SAPLING( 6, "minecraft:sapling", 3, MaterialType.BLOCK ), ACACIA_SAPLING( 6, "minecraft:sapling", 4, MaterialType.BLOCK ), DARK_OAK_SAPLING( 6, "minecraft:sapling", 5, MaterialType.BLOCK ), - BEDROCK( 7, "minecraft:bedrock", 0, MaterialType.BLOCK ), - FLOWING_WATER( 8, "minecraft:flowing_water", 0, MaterialType.BLOCK ), + BEDROCK( 7, "minecraft:bedrock", 0, MaterialType.BLOCK ), + + FLOWING_WATER( 8, "minecraft:flowing_water", 0, MaterialType.BLOCK, "WATER" ), + STILL_WATER( 9, "minecraft:water", 0, MaterialType.BLOCK ), - FLOWING_LAVA( 10, "minecraft:flowing_lava", 0, MaterialType.BLOCK ), + FLOWING_LAVA( 10, "minecraft:flowing_lava", 0, MaterialType.BLOCK, "LAVA" ), STILL_LAVA( 11, "minecraft:lava", 0, MaterialType.BLOCK ), + SAND( 12, "minecraft:sand", 0, MaterialType.BLOCK ), RED_SAND( 12, "minecraft:sand", 1, MaterialType.BLOCK ), GRAVEL( 13, "minecraft:gravel", 0, MaterialType.BLOCK ), - GOLD_ORE( 14, "minecraft:gold_ore", 0, MaterialType.BLOCK ), - IRON_ORE( 15, "minecraft:iron_ore", 0, MaterialType.BLOCK ), - COAL_ORE( 16, "minecraft:coal_ore", 0, MaterialType.BLOCK ), + GOLD_ORE( 14, "minecraft:gold_ore", 0, MaterialType.BLOCK ), + IRON_ORE( 15, "minecraft:iron_ore", 0, MaterialType.BLOCK ), + COAL_ORE( 16, "minecraft:coal_ore", 0, MaterialType.BLOCK ), + OAK_WOOD( 17, "minecraft:log", 0, MaterialType.BLOCK ), SPRUCE_WOOD( 17, "minecraft:log", 1, MaterialType.BLOCK ), BIRCH_WOOD( 17, "minecraft:log", 2, MaterialType.BLOCK ), JUNGLE_WOOD( 17, "minecraft:log", 3, MaterialType.BLOCK ), + OAK_LEAVES( 18, "minecraft:leaves", 0, MaterialType.BLOCK ), SPRUCE_LEAVES( 18, "minecraft:leaves", 1, MaterialType.BLOCK ), BIRCH_LEAVES( 18, "minecraft:leaves", 2, MaterialType.BLOCK ), - UNGL_LEAVES( 18, "minecraft:leaves", 3, MaterialType.BLOCK ), /// ??? Should this be JUNGLE_LEAVES ??? JUNGLE_LEAVES( 18, "minecraft:leaves", 3, MaterialType.BLOCK ), SPONGE( 19, "minecraft:sponge", 0, MaterialType.BLOCK ), WET_SPONGE( 19, "minecraft:sponge", 1, MaterialType.BLOCK ), GLASS( 20, "minecraft:glass", 0, MaterialType.BLOCK ), + LAPIS_LAZULI_ORE( 21, "minecraft:lapis_ore", 0, MaterialType.BLOCK ), LAPIS_LAZULI_BLOCK( 22, "minecraft:lapis_block", 0, MaterialType.BLOCK ), + DISPENSER( 23, "minecraft:dispenser", 0, MaterialType.BLOCK ), SANDSTONE( 24, "minecraft:sandstone", 0, MaterialType.BLOCK ), CHISELED_SANDSTONE( 24, "minecraft:sandstone", 1, MaterialType.BLOCK ), @@ -107,10 +164,10 @@ public enum BlockType { DETECTOR_RAIL( 28, "minecraft:detector_rail", 0, MaterialType.BLOCK ), STICKY_PISTON( 29, "minecraft:sticky_piston", 0, MaterialType.BLOCK ), COBWEB( 30, "minecraft:web", 0, MaterialType.BLOCK ), - DEAD_SHRUB( 31, "minecraft:tallgrass", 0, MaterialType.BLOCK ), + DEAD_SHRUB( 31, "minecraft:tallgrass", 0, MaterialType.BLOCK, "DEAD_BUSH" ), TALL_GRASS( 31, "minecraft:tallgrass", 1, MaterialType.BLOCK ), FERN( 31, "minecraft:tallgrass", 2, MaterialType.BLOCK ), - DEAD_BUSH( 32, "minecraft:deadbush", 0, MaterialType.BLOCK ), + DEAD_BUSH( 32, "minecraft:deadbush", 0, MaterialType.BLOCK, "DEAD_BUSH" ), PISTON( 33, "minecraft:piston", 0, MaterialType.BLOCK ), PISTON_HEAD( 34, "minecraft:piston_head", 0, MaterialType.BLOCK ), WHITE_WOOL( 35, "minecraft:wool", 0, MaterialType.BLOCK ), @@ -129,11 +186,12 @@ public enum BlockType { GREEN_WOOL( 35, "minecraft:wool", 13, MaterialType.BLOCK ), RED_WOOL( 35, "minecraft:wool", 14, MaterialType.BLOCK ), BLACK_WOOL( 35, "minecraft:wool", 15, MaterialType.BLOCK ), + DANDELION( 37, "minecraft:yellow_flower", 0, MaterialType.BLOCK ), - POPPY( 38, "minecraft:red_flower", 0, MaterialType.BLOCK ), + POPPY( 38, "minecraft:red_flower", 0, MaterialType.BLOCK, "RED_ROSE" ), BLUE_ORCHID( 38, "minecraft:red_flower", 1, MaterialType.BLOCK ), ALLIUM( 38, "minecraft:red_flower", 2, MaterialType.BLOCK ), - AZURE_BLUET( 38, "minecraft:red_flower", 3, MaterialType.BLOCK ), + AZURE_BLUET( 38, "minecraft:red_flower", 3, MaterialType.BLOCK, "AZURE_BLUET" ), RED_TULIP( 38, "minecraft:red_flower", 4, MaterialType.BLOCK ), ORANGE_TULIP( 38, "minecraft:red_flower", 5, MaterialType.BLOCK ), WHITE_TULIP( 38, "minecraft:red_flower", 6, MaterialType.BLOCK ), @@ -143,26 +201,21 @@ public enum BlockType { RED_MUSHROOM( 40, "minecraft:red_mushroom", 0, MaterialType.BLOCK ), GOLD_BLOCK( 41, "minecraft:gold_block", 0, MaterialType.BLOCK ), IRON_BLOCK( 42, "minecraft:iron_block", 0, MaterialType.BLOCK ), - DOUBLE_STONE_SLAB( 43, "minecraft:double_stone_slab", 0, MaterialType.BLOCK ), - DOUBLE_SANDSTONE_SLAB( 43, "minecraft:double_stone_slab", 1, MaterialType.BLOCK ), - DOUBLE_WOODEN_SLAB( 43, "minecraft:double_stone_slab", 2, MaterialType.BLOCK ), - DOUBLE_COBBLESTONE_SLAB( 43, "minecraft:double_stone_slab", 3, MaterialType.BLOCK ), - DOUBLE_BRICK_SLAB( 43, "minecraft:double_stone_slab", 4, MaterialType.BLOCK ), - DOUBLE_STONE_BRICK_SLAB( 43, "minecraft:double_stone_slab", 5, MaterialType.BLOCK ), - DOUBLE_NETHER_BRICK_SLAB( 43, "minecraft:double_stone_slab", 6, MaterialType.BLOCK ), - DOUBLE_QUARTZ_SLAB( 43, "minecraft:double_stone_slab", 7, MaterialType.BLOCK ), + + STONE_SLAB( 44, "minecraft:stone_slab", 0, MaterialType.BLOCK ), SANDSTONE_SLAB( 44, "minecraft:stone_slab", 1, MaterialType.BLOCK ), WOODEN_SLAB( 44, "minecraft:stone_slab", 2, MaterialType.BLOCK ), COBBLESTONE_SLAB( 44, "minecraft:stone_slab", 3, MaterialType.BLOCK ), - BRICK_SLAB( 44, "minecraft:stone_slab", 4, MaterialType.BLOCK ), + BRICK_SLAB( 44, "minecraft:stone_slab", 4, MaterialType.BLOCK, "STONE_BRICK_SLAB" ), STONE_BRICK_SLAB( 44, "minecraft:stone_slab", 5, MaterialType.BLOCK ), NETHER_BRICK_SLAB( 44, "minecraft:stone_slab", 6, MaterialType.BLOCK ), QUARTZ_SLAB( 44, "minecraft:stone_slab", 7, MaterialType.BLOCK ), BRICKS( 45, "minecraft:brick_block", 0, MaterialType.BLOCK ), TNT( 46, "minecraft:tnt", 0, MaterialType.BLOCK ), BOOKSHELF( 47, "minecraft:bookshelf", 0, MaterialType.BLOCK ), - MOSS_STONE( 48, "minecraft:mossy_cobblestone", 0, MaterialType.BLOCK ), + MOSS_STONE( 48, "minecraft:mossy_cobblestone", 0, MaterialType.BLOCK, "MOSSY_COBBLESTONE" ), + OBSIDIAN( 49, "minecraft:obsidian", 0, MaterialType.BLOCK ), TORCH( 50, "minecraft:torch", 0, MaterialType.BLOCK ), FIRE( 51, "minecraft:fire", 0, MaterialType.BLOCK ), @@ -177,7 +230,7 @@ public enum BlockType { FARMLAND( 60, "minecraft:farmland", 0, MaterialType.BLOCK ), FURNACE( 61, "minecraft:furnace", 0, MaterialType.BLOCK ), BURNING_FURNACE( 62, "minecraft:lit_furnace", 0, MaterialType.BLOCK ), - STANDING_SIGN_BLOCK( 63, "minecraft:standing_sign", 0, MaterialType.BLOCK ), + STANDING_SIGN_BLOCK( 63, "minecraft:standing_sign", 0, MaterialType.BLOCK, "OAK_SIGN" ), OAK_DOOR_BLOCK( 64, "minecraft:wooden_door", 0, MaterialType.BLOCK ), LADDER( 65, "minecraft:ladder", 0, MaterialType.BLOCK ), RAIL( 66, "minecraft:rail", 0, MaterialType.BLOCK ), @@ -186,7 +239,9 @@ public enum BlockType { LEVER( 69, "minecraft:lever", 0, MaterialType.BLOCK ), STONE_PRESSURE_PLATE( 70, "minecraft:stone_pressure_plate", 0, MaterialType.BLOCK ), IRON_DOOR_BLOCK( 71, "minecraft:iron_door", 0, MaterialType.BLOCK ), - WOODEN_PRESSURE_PLATE( 72, "minecraft:wooden_pressure_plate", 0, MaterialType.BLOCK ), + + WOODEN_PRESSURE_PLATE( 72, "minecraft:wooden_pressure_plate", 0, MaterialType.BLOCK, + "OAK_PRESSURE_PLATE", "WOOD_PLATE" ), REDSTONE_ORE( 73, "minecraft:redstone_ore", 0, MaterialType.BLOCK ), GLOWING_REDSTONE_ORE( 74, "minecraft:lit_redstone_ore", 0, MaterialType.BLOCK ), @@ -201,7 +256,7 @@ public enum BlockType { CACTUS( 81, "minecraft:cactus", 0, MaterialType.BLOCK ), CLAY( 82, "minecraft:clay", 0, MaterialType.BLOCK ), - SUGAR_CANES( 83, "minecraft:reeds", 0, MaterialType.BLOCK ), + SUGAR_CANES( 83, "minecraft:reeds", 0, MaterialType.BLOCK, "SUGAR_CANE" ), JUKEBOX( 84, "minecraft:jukebox", 0, MaterialType.BLOCK ), OAK_FENCE( 85, "minecraft:fence", 0, MaterialType.BLOCK ), PUMPKIN( 86, "minecraft:pumpkin", 0, MaterialType.BLOCK ), @@ -210,10 +265,14 @@ public enum BlockType { SOUL_SAND( 88, "minecraft:soul_sand", 0, MaterialType.BLOCK ), GLOWSTONE( 89, "minecraft:glowstone", 0, MaterialType.BLOCK ), NETHER_PORTAL( 90, "minecraft:portal", 0, MaterialType.BLOCK ), - JACK_OLANTERN( 91, "minecraft:lit_pumpkin", 0, MaterialType.BLOCK ), + + JACK_OLANTERN( 91, "minecraft:lit_pumpkin", 0, MaterialType.BLOCK, "jack_o_lantern" ), + CAKE_BLOCK( 92, "minecraft:cake", 0, MaterialType.BLOCK ), - REDSTONE_REPEATER_BLOCK_OFF( 93, "minecraft:unpowered_repeater", 0, MaterialType.BLOCK ), - REDSTONE_REPEATER_BLOCK_ON( 94, "minecraft:powered_repeater", 0, MaterialType.BLOCK ), + + REDSTONE_REPEATER_BLOCK_OFF( 93, "minecraft:unpowered_repeater", 0, MaterialType.BLOCK, "REPEATER" ), + REDSTONE_REPEATER_BLOCK_ON( 94, "minecraft:powered_repeater", 0, MaterialType.BLOCK, "REPEATER" ), + WHITE_STAINED_GLASS( 95, "minecraft:stained_glass", 0, MaterialType.BLOCK ), ORANGE_STAINED_GLASS( 95, "minecraft:stained_glass", 1, MaterialType.BLOCK ), MAGENTA_STAINED_GLASS( 95, "minecraft:stained_glass", 2, MaterialType.BLOCK ), @@ -230,19 +289,25 @@ public enum BlockType { GREEN_STAINED_GLASS( 95, "minecraft:stained_glass", 13, MaterialType.BLOCK ), RED_STAINED_GLASS( 95, "minecraft:stained_glass", 14, MaterialType.BLOCK ), BLACK_STAINED_GLASS( 95, "minecraft:stained_glass", 15, MaterialType.BLOCK ), - WOODEN_TRAPDOOR( 96, "minecraft:trapdoor", 0, MaterialType.BLOCK ), - STONE_MONSTER_EGG( 97, "minecraft:monster_egg", 0, MaterialType.BLOCK ), - COBBLESTONE_MONSTER_EGG( 97, "minecraft:monster_egg", 1, MaterialType.BLOCK ), - STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 2, MaterialType.BLOCK ), - MOSSY_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 3, MaterialType.BLOCK ), - CRACKED_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 4, MaterialType.BLOCK ), - CHISELED_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 5, MaterialType.BLOCK ), - STONE_BRICKS( 98, "minecraft:stonebrick", 0, MaterialType.BLOCK ), - MOSSY_STONE_BRICKS( 98, "minecraft:stonebrick", 1, MaterialType.BLOCK ), - CRACKED_STONE_BRICKS( 98, "minecraft:stonebrick", 2, MaterialType.BLOCK ), - CHISELED_STONE_BRICKS( 98, "minecraft:stonebrick", 3, MaterialType.BLOCK ), + WOODEN_TRAPDOOR( 96, "minecraft:trapdoor", 0, MaterialType.BLOCK, "oak_trapdoor" ), + + STONE_MONSTER_EGG( 97, "minecraft:monster_egg", 0, MaterialType.BLOCK, "INFESTED_STONE" ), + COBBLESTONE_MONSTER_EGG( 97, "minecraft:monster_egg", 1, MaterialType.BLOCK, "INFESTED_COBBLESTONE" ), + STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 2, MaterialType.BLOCK, "INFESTED_STONE_BRICKS" ), + MOSSY_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 3, MaterialType.BLOCK, "INFESTED_MOSSY_STONE_BRICKS" ), + CRACKED_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 4, MaterialType.BLOCK, "INFESTED_CRACKED_STONE_BRICKS" ), + CHISELED_STONE_BRICK_MONSTER_EGG( 97, "minecraft:monster_egg", 5, MaterialType.BLOCK, "INFESTED_CHISELED_STONE_BRICKS" ), + + STONE_BRICKS( 98, "minecraft:stonebrick", 0, MaterialType.BLOCK, "STONE_BRICKS" ), + MOSSY_STONE_BRICKS( 98, "minecraft:stonebrick", 1, MaterialType.BLOCK, "MOSSY_STONE_BRICKS" ), + CRACKED_STONE_BRICKS( 98, "minecraft:stonebrick", 2, MaterialType.BLOCK, "CRACKED_STONE_BRICKS" ), + CHISELED_STONE_BRICKS( 98, "minecraft:stonebrick", 3, MaterialType.BLOCK, "CHISELED_STONE_BRICKS" ), + BROWN_MUSHROOM_BLOCK( 99, "minecraft:brown_mushroom_block", 0, MaterialType.BLOCK ), + HUGE_MUSHROOM_1( 99, "minecraft:brown_mushroom_block", 14, MaterialType.BLOCK ), RED_MUSHROOM_BLOCK( 100, "minecraft:red_mushroom_block", 0, MaterialType.BLOCK ), + HUGE_MUSHROOM_2( 100, "minecraft:red_mushroom_block", 14, MaterialType.BLOCK ), + IRON_BARS( 101, "minecraft:iron_bars", 0, MaterialType.BLOCK ), GLASS_PANE( 102, "minecraft:glass_pane", 0, MaterialType.BLOCK ), MELON_BLOCK( 103, "minecraft:melon_block", 0, MaterialType.BLOCK ), @@ -254,6 +319,7 @@ public enum BlockType { STONE_BRICK_STAIRS( 109, "minecraft:stone_brick_stairs", 0, MaterialType.BLOCK ), MYCELIUM( 110, "minecraft:mycelium", 0, MaterialType.BLOCK ), LILY_PAD( 111, "minecraft:waterlily", 0, MaterialType.BLOCK ), + NETHER_BRICK( 112, "minecraft:nether_brick", 0, MaterialType.BLOCK ), NETHER_BRICK_FENCE( 113, "minecraft:nether_brick_fence", 0, MaterialType.BLOCK ), NETHER_BRICK_STAIRS( 114, "minecraft:nether_brick_stairs", 0, MaterialType.BLOCK ), @@ -265,14 +331,11 @@ public enum BlockType { END_PORTAL_FRAME( 120, "minecraft:end_portal_frame", 0, MaterialType.BLOCK ), END_STONE( 121, "minecraft:end_stone", 0, MaterialType.BLOCK ), DRAGON_EGG( 122, "minecraft:dragon_egg", 0, MaterialType.BLOCK ), - REDSTONE_LAMP_INACTIVE( 123, "minecraft:redstone_lamp", 0, MaterialType.BLOCK ), - REDSTONE_LAMP_ACTIVE( 124, "minecraft:lit_redstone_lamp", 0, MaterialType.BLOCK ), - DOUBLE_OAK_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 0, MaterialType.BLOCK ), - DOUBLE_SPRUCE_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 1, MaterialType.BLOCK ), - DOUBLE_BIRCH_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 2, MaterialType.BLOCK ), - DOUBLE_JUNGLE_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 3, MaterialType.BLOCK ), - DOUBLE_ACACIA_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 4, MaterialType.BLOCK ), - DOUBLE_DARK_OAK_WOOD_SLAB( 125, "minecraft:double_wooden_slab", 5, MaterialType.BLOCK ), + + REDSTONE_LAMP_INACTIVE( 123, "minecraft:redstone_lamp", 0, MaterialType.BLOCK, "REDSTONE_LAMP_OFF" ), + REDSTONE_LAMP_ACTIVE( 124, "minecraft:lit_redstone_lamp", 0, MaterialType.BLOCK, "REDSTONE_LAMP", "REDSTONE_LAMP_ON" ), + + OAK_WOOD_SLAB( 126, "minecraft:wooden_slab", 0, MaterialType.BLOCK ), SPRUCE_WOOD_SLAB( 126, "minecraft:wooden_slab", 1, MaterialType.BLOCK ), BIRCH_WOOD_SLAB( 126, "minecraft:wooden_slab", 2, MaterialType.BLOCK ), @@ -296,40 +359,46 @@ public enum BlockType { FLOWER_POT( 140, "minecraft:flower_pot", 0, MaterialType.BLOCK ), CARROTS( 141, "minecraft:carrots", 0, MaterialType.BLOCK ), POTATOES( 142, "minecraft:potatoes", 0, MaterialType.BLOCK ), - WOODEN_BUTTON( 143, "minecraft:wooden_button", 0, MaterialType.BLOCK ), + WOODEN_BUTTON( 143, "minecraft:wooden_button", 0, MaterialType.BLOCK, "OAK_BUTTON", "wood_button" ), MOB_HEAD( 144, "minecraft:skull", 0, MaterialType.BLOCK ), ANVIL( 145, "minecraft:anvil", 0, MaterialType.BLOCK ), TRAPPED_CHEST( 146, "minecraft:trapped_chest", 0, MaterialType.BLOCK ), WEIGHTED_PRESSURE_PLATE_LIGHT( 147, "minecraft:light_weighted_pressure_plate", 0, MaterialType.BLOCK ), WEIGHTED_PRESSURE_PLATE_HEAVY( 148, "minecraft:heavy_weighted_pressure_plate", 0, MaterialType.BLOCK ), - REDSTONE_COMPARATOR_INACTIVE( 149, "minecraft:unpowered_comparator", 0, MaterialType.BLOCK ), - REDSTONE_COMPARATOR_ACTIVE( 150, "minecraft:powered_comparator", 0, MaterialType.BLOCK ), + + REDSTONE_COMPARATOR_INACTIVE( 149, "minecraft:unpowered_comparator", 0, MaterialType.BLOCK, "COMPARATOR" ), + REDSTONE_COMPARATOR_ACTIVE( 150, "minecraft:powered_comparator", 0, MaterialType.BLOCK, "COMPARATOR" ), + DAYLIGHT_SENSOR( 151, "minecraft:daylight_detector", 0, MaterialType.BLOCK ), REDSTONE_BLOCK( 152, "minecraft:redstone_block", 0, MaterialType.BLOCK ), NETHER_QUARTZ_ORE( 153, "minecraft:quartz_ore", 0, MaterialType.BLOCK ), HOPPER( 154, "minecraft:hopper", 0, MaterialType.BLOCK ), QUARTZ_BLOCK( 155, "minecraft:quartz_block", 0, MaterialType.BLOCK ), CHISELED_QUARTZ_BLOCK( 155, "minecraft:quartz_block", 1, MaterialType.BLOCK ), - PILLAR_QUARTZ_BLOCK( 155, "minecraft:quartz_block", 2, MaterialType.BLOCK ), + + PILLAR_QUARTZ_BLOCK( 155, "minecraft:quartz_block", 2, MaterialType.BLOCK, "QUARTZ_PILLAR" ), + QUARTZ_STAIRS( 156, "minecraft:quartz_stairs", 0, MaterialType.BLOCK ), ACTIVATOR_RAIL( 157, "minecraft:activator_rail", 0, MaterialType.BLOCK ), DROPPER( 158, "minecraft:dropper", 0, MaterialType.BLOCK ), - WHITE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 0, MaterialType.BLOCK ), - ORANGE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 1, MaterialType.BLOCK ), - MAGENTA_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 2, MaterialType.BLOCK ), - LIGHT_BLUE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 3, MaterialType.BLOCK ), - YELLOW_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 4, MaterialType.BLOCK ), - LIME_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 5, MaterialType.BLOCK ), - PINK_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 6, MaterialType.BLOCK ), - GRAY_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 7, MaterialType.BLOCK ), - LIGHT_GRAY_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 8, MaterialType.BLOCK ), - CYAN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 9, MaterialType.BLOCK ), - PURPLE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 10, MaterialType.BLOCK ), - BLUE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 11, MaterialType.BLOCK ), - BROWN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 12, MaterialType.BLOCK ), - GREEN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 13, MaterialType.BLOCK ), - RED_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 14, MaterialType.BLOCK ), - BLACK_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 15, MaterialType.BLOCK ), + + WHITE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 0, MaterialType.BLOCK, "WHITE_TERRACOTTA" ), + ORANGE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 1, MaterialType.BLOCK, "ORANGE_TERRACOTTA" ), + MAGENTA_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 2, MaterialType.BLOCK, "MAGENTA_TERRACOTTA" ), + LIGHT_BLUE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 3, MaterialType.BLOCK, "LIGHT_BLUE_TERRACOTTA" ), + YELLOW_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 4, MaterialType.BLOCK, "YELLOW_TERRACOTTA" ), + LIME_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 5, MaterialType.BLOCK, "LIME_TERRACOTTA" ), + PINK_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 6, MaterialType.BLOCK, "PINK_TERRACOTTA" ), + GRAY_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 7, MaterialType.BLOCK, "GRAY_TERRACOTTA" ), + LIGHT_GRAY_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 8, MaterialType.BLOCK, "LIGHT_GRAY_TERRACOTTA" ), + CYAN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 9, MaterialType.BLOCK, "CYAN_TERRACOTTA" ), + PURPLE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 10, MaterialType.BLOCK, "PURPLE_TERRACOTTA" ), + BLUE_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 11, MaterialType.BLOCK, "BLUE_TERRACOTTA" ), + BROWN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 12, MaterialType.BLOCK, "BROWN_TERRACOTTA" ), + GREEN_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 13, MaterialType.BLOCK, "GREEN_TERRACOTTA" ), + RED_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 14, MaterialType.BLOCK, "RED_TERRACOTTA" ), + BLACK_STAINED_CLAY( 159, "minecraft:stained_hardened_clay", 15, MaterialType.BLOCK, "BLACK_TERRACOTTA" ), + WHITE_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 0, MaterialType.BLOCK ), ORANGE_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 1, MaterialType.BLOCK ), MAGENTA_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 2, MaterialType.BLOCK ), @@ -346,7 +415,8 @@ public enum BlockType { GREEN_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 13, MaterialType.BLOCK ), RED_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 14, MaterialType.BLOCK ), BLACK_STAINED_GLASS_PANE( 160, "minecraft:stained_glass_pane", 15, MaterialType.BLOCK ), - ACACIA_LEAVES( 161, "minecraft:leaves2", 0, MaterialType.BLOCK ), + + ACACIA_LEAVES( 161, "minecraft:leaves2", 0, MaterialType.BLOCK, "ACACIA_LEAVES" ), DARK_OAK_LEAVES( 161, "minecraft:leaves2", 1, MaterialType.BLOCK ), ACACIA_WOOD( 162, "minecraft:log2", 0, MaterialType.BLOCK ), DARK_OAK_WOOD( 162, "minecraft:log2", 1, MaterialType.BLOCK ), @@ -376,7 +446,7 @@ public enum BlockType { GREEN_CARPET( 171, "minecraft:carpet", 13, MaterialType.BLOCK ), RED_CARPET( 171, "minecraft:carpet", 14, MaterialType.BLOCK ), BLACK_CARPET( 171, "minecraft:carpet", 15, MaterialType.BLOCK ), - HARDENED_CLAY( 172, "minecraft:hardened_clay", 0, MaterialType.BLOCK ), + HARDENED_CLAY( 172, "minecraft:hardened_clay", 0, MaterialType.BLOCK, "TERRACOTTA", "HARD_CLAY" ), BLOCK_OF_COAL( 173, "minecraft:coal_block", 0, MaterialType.BLOCK ), PACKED_ICE( 174, "minecraft:packed_ice", 0, MaterialType.BLOCK ), SUNFLOWER( 175, "minecraft:double_plant", 0, MaterialType.BLOCK ), @@ -392,8 +462,9 @@ public enum BlockType { CHISELED_RED_SANDSTONE( 179, "minecraft:red_sandstone", 1, MaterialType.BLOCK ), SMOOTH_RED_SANDSTONE( 179, "minecraft:red_sandstone", 2, MaterialType.BLOCK ), RED_SANDSTONE_STAIRS( 180, "minecraft:red_sandstone_stairs", 0, MaterialType.BLOCK ), - DOUBLE_RED_SANDSTONE_SLAB( 181, "minecraft:double_stone_slab2", 0, MaterialType.BLOCK ), + RED_SANDSTONE_SLAB( 182, "minecraft:stone_slab2", 0, MaterialType.BLOCK ), + SPRUCE_FENCE_GATE( 183, "minecraft:spruce_fence_gate", 0, MaterialType.BLOCK ), BIRCH_FENCE_GATE( 184, "minecraft:birch_fence_gate", 0, MaterialType.BLOCK ), JUNGLE_FENCE_GATE( 185, "minecraft:jungle_fence_gate", 0, MaterialType.BLOCK ), @@ -415,7 +486,7 @@ public enum BlockType { PURPUR_BLOCK( 201, "minecraft:purpur_block", 0, MaterialType.BLOCK ), PURPUR_PILLAR( 202, "minecraft:purpur_pillar", 0, MaterialType.BLOCK ), PURPUR_STAIRS( 203, "minecraft:purpur_stairs", 0, MaterialType.BLOCK ), - PURPUR_DOUBLE_SLAB( 204, "minecraft:purpur_double_slab", 0, MaterialType.BLOCK ), + PURPUR_SLAB( 205, "minecraft:purpur_slab", 0, MaterialType.BLOCK ), END_STONE_BRICKS( 206, "minecraft:end_bricks", 0, MaterialType.BLOCK ), BEETROOT_BLOCK( 207, "minecraft:beetroots", 0, MaterialType.BLOCK ), @@ -509,7 +580,7 @@ public enum BlockType { MILK_BUCKET( 335, "minecraft:milk_bucket", 0 ), BRICK( 336, "minecraft:brick", 0, MaterialType.BLOCK ), CLAY_BALL( 337, "minecraft:clay_ball", 0 ), - SUGAR_CANES_ITEM( 338, "minecraft:reeds", 0, MaterialType.BLOCK ), + SUGAR_CANES_ITEM( 338, "minecraft:reeds", 0, MaterialType.BLOCK, "SUGAR_CANE" ), PAPER( 339, "minecraft:paper", 0 ), BOOK( 340, "minecraft:book", 0 ), SLIMEBALL( 341, "minecraft:slime_ball", 0 ), @@ -526,11 +597,15 @@ public enum BlockType { PUFFERFISH( 349, "minecraft:fish", 3 ), COOKED_FISH( 350, "minecraft:cooked_fish", 0 ), COOKED_SALMON( 350, "minecraft:cooked_fish", 1 ), + INK_SACK( 351, "minecraft:dye", 0 ), ROSE_RED( 351, "minecraft:dye", 1 ), CACTUS_GREEN( 351, "minecraft:dye", 2 ), COCO_BEANS( 351, "minecraft:dye", 3 ), + + // NOTE: May actually be minecraft:ink_sack which is what XMaterial uses? LAPIS_LAZULI( 351, "minecraft:dye", 4 ), + PURPLE_DYE( 351, "minecraft:dye", 5 ), CYAN_DYE( 351, "minecraft:dye", 6 ), LIGHT_GRAY_DYE( 351, "minecraft:dye", 7 ), @@ -542,6 +617,8 @@ public enum BlockType { MAGENTA_DYE( 351, "minecraft:dye", 13 ), ORANGE_DYE( 351, "minecraft:dye", 14 ), BONE_MEAL( 351, "minecraft:dye", 15 ), + + BONE( 352, "minecraft:bone", 0 ), SUGAR( 353, "minecraft:sugar", 0 ), CAKE( 354, "minecraft:cake", 0 ), @@ -628,7 +705,7 @@ public enum BlockType { ENCHANTED_BOOK( 403, "minecraft:enchanted_book", 0 ), REDSTONE_COMPARATOR( 404, "minecraft:comparator", 0, MaterialType.BLOCK ), NETHER_BRICK_ITEM( 405, "minecraft:netherbrick", 0, MaterialType.BLOCK ), - NETHER_QUARTZ( 406, "minecraft:quartz", 0, MaterialType.BLOCK ), + NETHER_QUARTZ( 406, "minecraft:quartz", 0 ), MINECART_WITH_TNT( 407, "minecraft:tnt_minecart", 0 ), MINECART_WITH_HOPPER( 408, "minecraft:hopper_minecart", 0 ), PRISMARINE_SHARD( 409, "minecraft:prismarine_shard", 0 ), @@ -683,6 +760,32 @@ public enum BlockType { DISC_11( 2266, "minecraft:record_11", 0 ), WAIT_DISC( 2267, "minecraft:record_wait", 0 ), + +// Testing to see if we can inject 1.13 block types: :( nope... does not work. +// Commenting out for now, will revisit later. +// +// AIR_113( "air", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// +// QUARTZ_113( "quartz", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// NETHER_QUARTZ_ORE_113( "nether_quartz_ore", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// QUARTZ_BLOCK_113( "quartz_block", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// CHISELED_QUARTZ_BLOCK_113( "chiseled_quartz_block", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// QUARTZ_PILLAR_113( "quartz_pillar", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// QUARTZ_SLAB_113( "quartz_slab", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// SMOOTH_QUARTZ_113( "smooth_quartz", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// +// OAK_LOG_113( "oak_log", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// SPRUCE_LOG_113( "spruce_log", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// BIRCH_LOG_113( "birch_log", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// JUNGLE_LOG_113( "jungle_log", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// +// OAK_WOOD_113( "oak_wood", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// SPRUCE_WOOD_113( "spruce_wood", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// BIRCH_WOOD_113( "birch_wood", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// JUNGLE_WOOD_113( "jungle_wood", MaterialType.BLOCK, MaterialVersion.v1_13 ), +// + + ; // @formatter:on @@ -690,13 +793,41 @@ public enum BlockType { private final String id; private final short data; private final MaterialType materialType; + private final MaterialVersion materialVersion; + + private final List altNames; BlockType(int legacyId, String id, int data, MaterialType materialType) { this.legacyId = legacyId; this.id = (id != null ? id : "minecraft:" + this.name().toLowerCase()); this.data = (short) data; this.materialType = materialType; + this.materialVersion = MaterialVersion.v1_8; + + this.altNames = new ArrayList<>(); + } + + + BlockType(int legacyId, String id, int data, MaterialType materialType, String... altNames) { + this( legacyId, id, data, materialType ); + + for ( String altName : altNames ) { + this.altNames.add( altName ); + } } + + + + BlockType(String id, MaterialType materialType, MaterialVersion materialVersion ) { + this.legacyId = -1; + this.id = (id != null ? id : "minecraft:" + this.name().toLowerCase()); + this.data = 0; + this.materialType = materialType; + this.materialVersion = materialVersion; + + this.altNames = new ArrayList<>(); + } + BlockType(MaterialType materialType) { this(0, null, 0, materialType); } @@ -709,6 +840,41 @@ public enum BlockType { this(legacyId, id, data, MaterialType.NOT_SET); } + /** + *

This function is for legacy versions of spigot that + * uses the data value. This function will returns a + * string value of a material name that + * XMaterial will be able to use to look up the correct + * bukkit material type. + *

+ * + *

The way it needs to be constructed, is by taking the id, + * dropping the "minecraft:" prefix, then if data is non-zero, + * add a colon and the value of data. + *

+ * + * + * @return + */ + public String getXMaterialNameLegacy() { + String xMatName = getId().replace( "minecraft:", "" ) + + ( getData() > 0 ? ":" + getData() : "" ); + return xMatName; + } + + /** + *

This function will return the lower case name of the BlockType. + * This should match + * @return + */ + public String getXMaterialName() { + return name().toLowerCase(); + } + + public List getXMaterialAltNames() { + return getAltNames(); + } + public static BlockType getBlock(int legacyId) { return getBlock(legacyId, (short) 0); } @@ -737,6 +903,9 @@ public static BlockType getBlock(String key) { if ( blockType == null ) { blockType = getBlockById( key ); } + if ( blockType == null ) { + blockType = getBlockByXMaterialName(key); + } return blockType; } @@ -763,7 +932,7 @@ private static BlockType getBlockById(String id) { Short.parseShort(id.split(":")[1])); } Prison prison = Prison.get(); - if ( prison != null && prison.getIntegrationManager() != null ) { + if ( prison != null && prison.getItemManager() != null ) { Set>> entrySet = prison.getItemManager().getItems().entrySet(); for (Map.Entry> entry : entrySet) { if (entry.getValue().contains(id.toLowerCase())) { @@ -784,6 +953,21 @@ private static BlockType getBlockByName(String name) { } return null; } + + private static BlockType getBlockByXMaterialName(String name) { + for (BlockType block : values()) { + if (block.getXMaterialAltNames().size() > 0 ) { + for ( String altName : block.getXMaterialAltNames() ) { + + if ( altName.equalsIgnoreCase(name)) { + return block; + } + } + } + + } + return null; + } public static BlockType getBlockWithData(int id, short data) { for (BlockType block : values()) { @@ -820,11 +1004,18 @@ public boolean isItem() { return materialType == MaterialType.ITEM; } - public MaterialType getMaterialType() - { + public MaterialType getMaterialType() { return materialType; } + public MaterialVersion getMaterialVersion() { + return materialVersion; + } + + public List getAltNames() { + return altNames; + } + @Override public String toString() { return id + ":" + data; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Bounds.java b/prison-core/src/main/java/tech/mcprison/prison/util/Bounds.java index 56b363cd4..65128dbda 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Bounds.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Bounds.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ package tech.mcprison.prison.util; +import tech.mcprison.prison.internal.World; + /** * Represents the area between two {@link Location}s. * @@ -68,6 +70,21 @@ public Bounds(Location min, Location max) { (getzBlockMax() - getzBlockMin() + 1); } + public void setWorld( World world ) { + if ( world != null ) { + + if ( getMin().getWorld() == null ) { + getMin().setWorld( world ); + } + if ( getMax().getWorld() == null ) { + getMax().setWorld( world ); + } + if ( getCenter().getWorld() == null ) { + getCenter().setWorld( world ); + } + } + } + /** * Returns the width of the area. * This will always be positive. @@ -109,6 +126,14 @@ public double getArea() { + getHeight() * getWidth()); } + public boolean within(Location location) { + return within( location, false ); + } + + public boolean withinIncludeTopOfMine(Location location) { + return within( location, true ); + } + /** * Returns whether or not a single point is within these boundaries. Ensure the same worlds * are being compared too. @@ -120,30 +145,90 @@ public double getArea() { * @param location The {@link Location} to check. * @return true if the location is within the bounds, false otherwise. */ - public boolean within(Location location) { + private boolean within(Location location, boolean includeTopOfMine ) { boolean results = false; - // TODO fix Bounds.within() get working with junit tests (currently it won't run) until then cannot test changes: - // If the worlds don't match, then don't waste time calculating if the passed - // location is within the Bounds. Some unit tests pass in nulls. -// if ( min.getWorld() == null && max.getWorld() == null && location.getWorld() == null || -// min.getWorld() != null && max.getWorld() != null && location.getWorld() != null && -// min.getWorld().getName().equalsIgnoreCase( max.getWorld().getName()) && -// min.getWorld().getName().equalsIgnoreCase( location.getWorld().getName() )) { - + if ( withinSameWorld( location )) { double ourX = Math.floor(location.getX()); double ourY = Math.floor(location.getY()); double ourZ = Math.floor(location.getZ()); results = ourX >= getxMin() && ourX <= getxMax() // Within X - && ourY >= (getyMin() - 1) && ourY <= getyMax() // Within Y + && ourY >= (getyMin() - 1) && ourY <= (getyMax() + (includeTopOfMine ? 1 : 0)) // Within Y && ourZ >= getzMin() && ourZ <= getzMax(); // Within Z -// } + } return results; } + + public boolean withinSameWorld(Location location) { + return getCenter().getWorld() != null && location.getWorld() != null && + getCenter().getWorld().getName().equalsIgnoreCase( + location.getWorld().getName() ); + } + + /** + *

This function will determine if the given location is within a radius of the Bounds' + * center. This will return a true or false value. + *

+ * + *

To keep the calculations simple, if the worlds are the same, then it will create a new + * Bounds object and then utilize the computed values for width and length by feeding it in + * to a Pythagorean theorem to compute the hypotenuse (distance) between these two points. + * There maybe other unused values calculated, but going for the simplicity of reusing existing code. + *

+ * + * @param location + * @param radius + * @return + */ + public boolean within(Location location, long radius) { + boolean results = false; + + if ( withinSameWorld( location ) ) { + + // Ignore y since this is radius from the center axis of the mine: + double distance = getDistance(location); + + results = distance <= radius; + } + + return results; + } + /** + *

Ignore Y since this is the radius from the center axis of the mine. + *

+ * @return + */ + public double getDistance() { + double deltaX = getMin().getX() - getMax().getX(); + double deltaZ = getMin().getZ() - getMax().getZ(); + double distance = Math.sqrt( (deltaX * deltaX) + (deltaZ * deltaZ) ); + return Math.round( distance ); + } + + public double getDistance(Location location) { + double deltaX = getCenter().getX() - location.getX(); + double deltaZ = getCenter().getZ() - location.getZ(); + double distance = Math.sqrt( (deltaX * deltaX) + (deltaZ * deltaZ) ); + return Math.round( distance ); + } + + public double getDistance3d(Location location) { + double deltaX = getCenter().getX() - location.getX(); + double deltaY = getCenter().getY() - location.getY(); + double deltaZ = getCenter().getZ() - location.getZ(); + double distance = Math.sqrt( (deltaX * deltaX) + (deltaY * deltaY) + (deltaZ * deltaZ) ); + return distance; + } + + public String getDimensions() { + return "&7" + Math.round(getWidth()) + "&8x&7" + + Math.round(getHeight()) + "&8x&7" + Math.round(getLength()); + } + public Location getMin() { return min; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/ChatColor.java b/prison-core/src/main/java/tech/mcprison/prison/util/ChatColor.java index bf8cbcb0b..cba2bd4c1 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/ChatColor.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/ChatColor.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/ClassUtil.java b/prison-core/src/main/java/tech/mcprison/prison/util/ClassUtil.java index f838555c2..1e48a49b9 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/ClassUtil.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/ClassUtil.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/CollectionUtil.java b/prison-core/src/main/java/tech/mcprison/prison/util/CollectionUtil.java index 722776293..bc66ecbec 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/CollectionUtil.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/CollectionUtil.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/EventExceptionHandler.java b/prison-core/src/main/java/tech/mcprison/prison/util/EventExceptionHandler.java index 5b0e8a91a..d98a1082f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/EventExceptionHandler.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/EventExceptionHandler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Gamemode.java b/prison-core/src/main/java/tech/mcprison/prison/util/Gamemode.java index 84c1ffd46..3160500af 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Gamemode.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Gamemode.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/ItemManager.java b/prison-core/src/main/java/tech/mcprison/prison/util/ItemManager.java index 4758ffa73..ddd9b60fb 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/ItemManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/ItemManager.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,25 +43,39 @@ public ItemManager() throws Exception { items = ArrayListMultimap.create(); if (!file.exists()) { - InputStream inputStream = getClass().getResourceAsStream("/items.csv"); - Files.copy(inputStream, Paths.get(file.getPath())); - } - BufferedReader in = new BufferedReader(new FileReader(file)); - String inputLine; - - while ((inputLine = in.readLine()) != null) { - try { - if (!inputLine.startsWith("#")) { - String[] array = inputLine.split(","); - String itemName = array[0]; - int id = Integer.parseInt(array[1]); - short data = Short.parseShort(array[2]); - items.put(BlockType.getBlockWithData(id, data), itemName.toLowerCase()); - } - } catch (Exception e) { - throw new IOException("Error while reading items.csv -- it's probably invalid", e); + try ( + // make sure the InputStream is properly closed. May not be 100% needed here: + InputStream inputStream = getClass().getResourceAsStream("/items.csv"); + ) + { + Files.copy(inputStream, Paths.get(file.getPath())); + } + catch (Exception e) { + throw new IOException("Error while copying items.csv from the jar resource to a " + + "file within the plugins directory:", e); } } + try ( + // Was a memory leak... always must be closed, so the try with resource ensures that it is: + BufferedReader in = new BufferedReader(new FileReader(file)); + ) + { + String inputLine; + + while ((inputLine = in.readLine()) != null) { + if (!inputLine.startsWith("#")) { + String[] array = inputLine.split(","); + String itemName = array[0]; + int id = Integer.parseInt(array[1]); + short data = Short.parseShort(array[2]); + items.put(BlockType.getBlockWithData(id, data), itemName.toLowerCase()); + } + } + + } + catch (Exception e) { + throw new IOException("Error while reading items.csv -- it's probably invalid", e); + } } public Map> getItems() { diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Location.java b/prison-core/src/main/java/tech/mcprison/prison/util/Location.java index 1953209a4..7e106f77f 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Location.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Location.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/MaterialType.java b/prison-core/src/main/java/tech/mcprison/prison/util/MaterialType.java index 32088a362..7776831f4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/MaterialType.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/MaterialType.java @@ -5,5 +5,8 @@ public enum MaterialType BLOCK, ITEM, - NOT_SET; + NOT_SET, + + INVALID; + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/MaterialVersion.java b/prison-core/src/main/java/tech/mcprison/prison/util/MaterialVersion.java new file mode 100644 index 000000000..3c2dc4600 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/util/MaterialVersion.java @@ -0,0 +1,9 @@ +package tech.mcprison.prison.util; + +public enum MaterialVersion +{ + v1_8, + + v1_13 + ; +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Scoreboard.java b/prison-core/src/main/java/tech/mcprison/prison/util/Scoreboard.java index de45b73cc..079fe3da1 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Scoreboard.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Scoreboard.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Text.java b/prison-core/src/main/java/tech/mcprison/prison/util/Text.java index 08fc10d65..1c91a43f5 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Text.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Text.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/Vector.java b/prison-core/src/main/java/tech/mcprison/prison/util/Vector.java index 6e4531b26..6c404219c 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/Vector.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/Vector.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/package-info.java b/prison-core/src/main/java/tech/mcprison/prison/util/package-info.java index c46e52677..4dc47e8f2 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/package-info.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/package-info.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/main/resources/lang/mines/de_DE.properties b/prison-core/src/main/resources/lang/mines/de_DE.properties index 37a67732a..0a537d40c 100644 --- a/prison-core/src/main/resources/lang/mines/de_DE.properties +++ b/prison-core/src/main/resources/lang/mines/de_DE.properties @@ -1,5 +1,5 @@ -reset_warning=&7Alle Minen wurden zurückgesetzt in &3%1&7. -reset_message=&7Alle Minen wurden zurückgesetzt. +reset_warning=&7Alle Minen %1 wurden zurückgesetzt in &3%2&7. +reset_message=&7Alle Minen %1 wurden zurückgesetzt. not_allowed=&7Hier darfst du nicht verminen. autosmelt_enable=&bAutosmelt &7wurde aktiviert. autosmelt_disable=&bAutosmelt &7wurde deaktiviert. diff --git a/prison-core/src/main/resources/lang/mines/en_US.properties b/prison-core/src/main/resources/lang/mines/en_US.properties index 5d598f11e..14ccbcc4c 100644 --- a/prison-core/src/main/resources/lang/mines/en_US.properties +++ b/prison-core/src/main/resources/lang/mines/en_US.properties @@ -1,5 +1,5 @@ -reset_warning=&7All mines are going to reset in &3%1&7. -reset_message=&7All mines have been reset. +reset_warning=&7Mine %1 will reset in &3%2&7. +reset_message=&7The mine %1 has been reset. not_allowed=&7You are not allowed to mine here. autosmelt_enable=&bAutosmelt &7has been &aenabled&7. autosmelt_disable=&bAutosmelt &7has been &cdisabled&7. diff --git a/prison-core/src/main/resources/lang/mines/es_ES.properties b/prison-core/src/main/resources/lang/mines/es_ES.properties index debb27447..2dc813912 100644 --- a/prison-core/src/main/resources/lang/mines/es_ES.properties +++ b/prison-core/src/main/resources/lang/mines/es_ES.properties @@ -1,5 +1,5 @@ -reset_warning=&7Todas las minas se reiniciarán en &3%1&7. -reset_message=&7Todas las minas han sido reiniciadas. +reset_warning=&7Todas las minas %1 se reiniciarán en &3%2&7. +reset_message=&7Todas las minas %1 han sido reiniciadas. not_allowed=&7No tienes permitido minar aquí. autosmelt_enable=&7La &bFundición Automática &7ha sido &aactivada&7. autosmelt_disable=&7La &bFundición Automática &7ha sido &cdesactivada&7. diff --git a/prison-core/src/main/resources/lang/mines/hu_HU.properties b/prison-core/src/main/resources/lang/mines/hu_HU.properties index dd31c9e56..ea5eb56f2 100644 --- a/prison-core/src/main/resources/lang/mines/hu_HU.properties +++ b/prison-core/src/main/resources/lang/mines/hu_HU.properties @@ -1,5 +1,5 @@ -reset_warning=&7Minden bánya újratöltve &3%1&7. -reset_message=&7Az összes bánya vissza lett állítva. +reset_warning=&7Minden %1 bánya újratöltve &3%2&7. +reset_message=&7Az összes %1 bánya vissza lett állítva. not_allowed=&7A bányászás nem megengedett. autosmelt_enable=&bAz auto. égetés &7engedélyezve. autosmelt_disable=&bAz auto. égetés &7letiltva. diff --git a/prison-core/src/main/resources/lang/mines/it_IT.properties b/prison-core/src/main/resources/lang/mines/it_IT.properties index 787ef0df3..a5c964e2d 100644 --- a/prison-core/src/main/resources/lang/mines/it_IT.properties +++ b/prison-core/src/main/resources/lang/mines/it_IT.properties @@ -1,5 +1,5 @@ -reset_warning=&7Tutte le miniere stanno per essere resettate tra &3%1&7. -reset_message=&7Tutte le miniere sono state resettate. +reset_warning=&7Tutte le miniere %1 stanno per essere resettate tra &3%2&7. +reset_message=&7Tutte le miniere %1 sono state resettate. not_allowed=&7Non sei ammesso a scvare qua. autosmelt_enable=&bAutoFusione &7è stata &aabilitata&7. autosmelt_disable=&bAutoFusione &7è stata &cdisabilitata&7. diff --git a/prison-core/src/main/resources/lang/mines/nl_BE.properties b/prison-core/src/main/resources/lang/mines/nl_BE.properties index 0d04c4245..c165f1e2e 100644 --- a/prison-core/src/main/resources/lang/mines/nl_BE.properties +++ b/prison-core/src/main/resources/lang/mines/nl_BE.properties @@ -1,5 +1,5 @@ -reset_warning=&Alle mijnen worden hersteld in &3%1&7. -reset_message=&7Alle mijnen zijn hersteld. +reset_warning=&Alle mijnen %1 worden hersteld in &3%2&7. +reset_message=&7Alle mijnen %1 zijn hersteld. not_allowed=&7Je mag hier niet mijnen. autosmelt_enable=&bAutosmelt &7is aangezet. autosmelt_disable=&bAutosmelt &7is afgezet. diff --git a/prison-core/src/main/resources/lang/mines/nl_NL.properties b/prison-core/src/main/resources/lang/mines/nl_NL.properties index 0d04c4245..c165f1e2e 100644 --- a/prison-core/src/main/resources/lang/mines/nl_NL.properties +++ b/prison-core/src/main/resources/lang/mines/nl_NL.properties @@ -1,5 +1,5 @@ -reset_warning=&Alle mijnen worden hersteld in &3%1&7. -reset_message=&7Alle mijnen zijn hersteld. +reset_warning=&Alle mijnen %1 worden hersteld in &3%2&7. +reset_message=&7Alle mijnen %1 zijn hersteld. not_allowed=&7Je mag hier niet mijnen. autosmelt_enable=&bAutosmelt &7is aangezet. autosmelt_disable=&bAutosmelt &7is afgezet. diff --git a/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java b/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java index 7b615fb74..29ad335dd 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java +++ b/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,22 +18,35 @@ package tech.mcprison.prison; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.UUID; + +import tech.mcprison.prison.PrisonCommand.RegisteredPluginsData; import tech.mcprison.prison.commands.PluginCommand; import tech.mcprison.prison.file.FileStorage; +import tech.mcprison.prison.file.YamlFileIO; import tech.mcprison.prison.gui.GUI; +import tech.mcprison.prison.integration.IntegrationManager.PlaceHolderFlags; +import tech.mcprison.prison.integration.Placeholders; +import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.Scheduler; import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.internal.platform.Capability; import tech.mcprison.prison.internal.platform.Platform; import tech.mcprison.prison.internal.scoreboard.ScoreboardManager; +import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.store.Storage; import tech.mcprison.prison.util.ChatColor; import tech.mcprison.prison.util.Location; -import java.io.File; -import java.util.*; - /** * @author Faizaan A. Datoo */ @@ -51,6 +64,11 @@ public TestPlatform(File pluginDirectory, boolean suppressOutput) { return Optional.of(new TestWorld(name)); } + @Override + public void getWorldLoadErrors( ChatDisplay display ) { + + } + @Override public Optional getPlayer(String name) { return null; } @@ -63,7 +81,17 @@ public TestPlatform(File pluginDirectory, boolean suppressOutput) { return new ArrayList<>(); } - @Override public String getPluginVersion() { + @Override + public Optional getOfflinePlayer( String name ) { + return null; + } + + @Override + public Optional getOfflinePlayer( UUID uuid ) { + return null; + } + + @Override public String getPluginVersion() { return "Tests"; } @@ -91,7 +119,11 @@ public boolean shouldShowAlerts() { @Override public void dispatchCommand(String cmd) { } - @Override public Scheduler getScheduler() { + @Override + public void dispatchCommand( CommandSender sender, String cmd ) { + } + + @Override public Scheduler getScheduler() { return new TestScheduler(); } @@ -134,4 +166,77 @@ public boolean shouldShowAlerts() { return new FileStorage(getPluginDirectory()); } + @Override + public void identifyRegisteredPlugins() { + PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); + + // reset so it will reload cleanly: + cmdVersion.getRegisteredPluginData().clear(); + + RegisteredPluginsData rpd1 = cmdVersion.addRegisteredPlugin( "TestPlugin", "3.2.1-alpha.13" ); + RegisteredPluginsData rpd2 = cmdVersion.addRegisteredPlugin( "AnotherPlugin", "0.1.1" ); + + cmdVersion.addPluginDetails( rpd1.getPluginName(), rpd1.getPluginVersion(), "lol", new ArrayList<>() ); + cmdVersion.addPluginDetails( rpd2.getPluginName(), rpd2.getPluginVersion(), "crime", new ArrayList<>() ); + cmdVersion.addPluginDetails( rpd2.getPluginName(), rpd2.getPluginVersion(), "justice", new ArrayList<>() ); + } + + + + public Map getPlaceholderDetailCounts() { + Map placeholderDetails = new TreeMap<>(); + + return placeholderDetails; + } + + public int getPlaceholderCount() { + return 0; + } + + + public int getPlaceholderRegistrationCount() { + return 0; + } + + + @Override + public Placeholders getPlaceholders() { + return null; + } + + + @Override + public YamlFileIO getYamlFileIO( File yamlFile ) { + return null; + } + + @Override + public void reloadConfig() { + } + + @Override + public String getConfigString( String key ) { + return null; + } + + @Override + public boolean getConfigBooleanFalse( String key ) { + return false; + } + + @Override + public boolean getConfigBooleanTrue( String key ) { + return false; + } + + @Override + public void getAllPlatformBlockTypes( List blockTypes ) { + + } + + + @Override + public PrisonBlock getPrisonBlock( String blockName ) { + return null; + } } diff --git a/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java b/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java index 5bc994942..c005e6419 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java +++ b/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/test/java/tech/mcprison/prison/TestWorld.java b/prison-core/src/test/java/tech/mcprison/prison/TestWorld.java index 05ea3709d..bf17f16c7 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/TestWorld.java +++ b/prison-core/src/test/java/tech/mcprison/prison/TestWorld.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/test/java/tech/mcprison/prison/autofeatures/ValueNodeFactoryTest.java b/prison-core/src/test/java/tech/mcprison/prison/autofeatures/ValueNodeFactoryTest.java new file mode 100644 index 000000000..0d509ec6e --- /dev/null +++ b/prison-core/src/test/java/tech/mcprison/prison/autofeatures/ValueNodeFactoryTest.java @@ -0,0 +1,261 @@ +package tech.mcprison.prison.autofeatures; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class ValueNodeFactoryTest + extends + ValueNodeFactory +{ + + @Test + public void testNormalValues() + { + { + ValueNode vn = getValueNode( null ); + assertNotNull( vn ); + assertTrue( vn.isNullNode() ); + assertTrue( vn instanceof NullNode ); + assertTrue( ((NullNode) vn).isNull() ); + } + + { + ValueNode vn = getValueNode( "true" ); + assertNotNull( vn ); + assertTrue( vn.isBooleanNode() ); + assertTrue( vn instanceof BooleanNode ); + assertTrue( ((BooleanNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "false" ); + assertNotNull( vn ); + assertTrue( vn.isBooleanNode() ); + assertTrue( vn instanceof BooleanNode ); + assertFalse( ((BooleanNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "1" ); + assertNotNull( vn ); + assertTrue( vn.isLongNode() ); + assertTrue( vn instanceof LongNode ); + assertEquals( 1L, ((LongNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "123456789" ); + assertNotNull( vn ); + assertTrue( vn.isLongNode() ); + assertTrue( vn instanceof LongNode ); + assertEquals( 123456789L, ((LongNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "-123456789" ); + assertNotNull( vn ); + assertTrue( vn.isLongNode() ); + assertTrue( vn instanceof LongNode ); + assertEquals( -123456789L, ((LongNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "0.0" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 0d, ((DoubleNode) vn).getValue(), 0.01 ); + } + + { + ValueNode vn = getValueNode( "0.001" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 0.001d, ((DoubleNode) vn).getValue(), 0.0001 ); + } + + { + ValueNode vn = getValueNode( "1234567.8901" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 1234567.8901d, ((DoubleNode) vn).getValue(), 0.001 ); + } + + { + ValueNode vn = getValueNode( "-1234567.8901" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( -1234567.8901d, ((DoubleNode) vn).getValue(), 0.001 ); + } + + { + // NOTE: Cannot parse scientific notation: + ValueNode vn = getValueNode( "123.45E2" ); + assertNotNull( vn ); + assertFalse( vn.isDoubleNode() ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); +// assertEquals( 12345.0d, ((DoubleNode) vn).getValue(), 0.01 ); + } + + + { + ValueNode vn = getValueNode( "" ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "", ((TextNode) vn).getValue() ); + } + + { + // Note: Leading and trailing spaces are removed. + ValueNode vn = getValueNode( " test " ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "test", ((TextNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "test 123" ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "test 123", ((TextNode) vn).getValue() ); + } + + + + + } + + /** + * The purpose of these tests are to try to test the edge cases of what may not + * be normally encountered. + */ + @Test + public void testStrangeValues() + { + // null is null, so nothing else to test with NullNode. + + { + ValueNode vn = getValueNode( " true" ); + assertNotNull( vn ); + assertTrue( vn.isBooleanNode() ); + assertTrue( vn instanceof BooleanNode ); + assertTrue( ((BooleanNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( " false " ); + assertNotNull( vn ); + assertTrue( vn.isBooleanNode() ); + assertTrue( vn instanceof BooleanNode ); + assertFalse( ((BooleanNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( " 1 " ); + assertNotNull( vn ); + assertTrue( vn.isLongNode() ); + assertTrue( vn instanceof LongNode ); + assertEquals( 1L, ((LongNode) vn).getValue() ); + } + + { + // Expected fail: No space between number and sign + ValueNode vn = getValueNode( " - 123456789 " ); + assertNotNull( vn ); + assertFalse( vn.isLongNode() ); + assertTrue( vn.isTextNode() ); +// assertTrue( vn instanceof LongNode ); +// assertEquals( 123456789L, ((LongNode) vn).getValue() ); + } + + { + // Expected fail: No double signs + ValueNode vn = getValueNode( "-123456789-" ); + assertNotNull( vn ); + assertFalse( vn.isLongNode() ); + assertTrue( vn.isTextNode() ); +// assertTrue( vn instanceof LongNode ); +// assertEquals( -123456789L, ((LongNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "0.0" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 0d, ((DoubleNode) vn).getValue(), 0.01 ); + } + + { + ValueNode vn = getValueNode( "0.001" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 0.001d, ((DoubleNode) vn).getValue(), 0.0001 ); + } + + { + ValueNode vn = getValueNode( "1234567.8901" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( 1234567.8901d, ((DoubleNode) vn).getValue(), 0.001 ); + } + + { + ValueNode vn = getValueNode( "-1234567.8901" ); + assertNotNull( vn ); + assertTrue( vn.isDoubleNode() ); + assertTrue( vn instanceof DoubleNode ); + assertEquals( -1234567.8901d, ((DoubleNode) vn).getValue(), 0.001 ); + } + + { + // NOTE: Cannot parse scientific notation: + ValueNode vn = getValueNode( "123.45E2" ); + assertNotNull( vn ); + assertFalse( vn.isDoubleNode() ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); +// assertEquals( 12345.0d, ((DoubleNode) vn).getValue(), 0.01 ); + } + + + { + ValueNode vn = getValueNode( "" ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "", ((TextNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( " test\n " ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "test", ((TextNode) vn).getValue() ); + } + + { + ValueNode vn = getValueNode( "test 123 " ); + assertNotNull( vn ); + assertTrue( vn.isTextNode() ); + assertTrue( vn instanceof TextNode ); + assertEquals( "test 123", ((TextNode) vn).getValue() ); + } + + + + + } + +} diff --git a/prison-core/src/test/java/tech/mcprison/prison/chat/ChatTest.java b/prison-core/src/test/java/tech/mcprison/prison/chat/ChatTest.java index 4cdae7e92..34befeafe 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/chat/ChatTest.java +++ b/prison-core/src/test/java/tech/mcprison/prison/chat/ChatTest.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-core/src/test/java/tech/mcprison/prison/selection/SelectionTest.java b/prison-core/src/test/java/tech/mcprison/prison/selection/SelectionTest.java index c3ef286cf..b1d2a8bfa 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/selection/SelectionTest.java +++ b/prison-core/src/test/java/tech/mcprison/prison/selection/SelectionTest.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,7 +44,9 @@ public class SelectionTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void setUp() throws Exception { - Prison.get().init(new TestPlatform(temporaryFolder.newFolder("test"), false)); + TestPlatform testPlatform = new TestPlatform(temporaryFolder.newFolder("test"), false); + Prison.get() + .init(testPlatform, "1.12.X-test.1"); } @Test public void testSelection() throws Exception { diff --git a/prison-core/src/test/java/tech/mcprison/prison/util/BoundsTest.java b/prison-core/src/test/java/tech/mcprison/prison/util/BoundsTest.java index 9dc9263f5..d7dc0a26c 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/util/BoundsTest.java +++ b/prison-core/src/test/java/tech/mcprison/prison/util/BoundsTest.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,11 +44,11 @@ public class BoundsTest { } @Test public void within() throws Exception { -// TestWorld world1 = new TestWorld("test1"); + TestWorld world1 = new TestWorld("test1"); // TestWorld world2 = new TestWorld("test2"); - Location loc1 = new Location(null, 0.0, 4.0, 0.0); - Location loc2 = new Location(null, 10.0, 10.0, 10.0); - Location loc3 = new Location(null, 5.0, 5.0, 5.0); + Location loc1 = new Location(world1, 0.0, 4.0, 0.0); + Location loc2 = new Location(world1, 10.0, 10.0, 10.0); + Location loc3 = new Location(world1, 5.0, 5.0, 5.0); Bounds bounds = new Bounds(loc1, loc2); Location toCheck = loc3; diff --git a/prison-mines/build.gradle b/prison-mines/build.gradle index bd2f8aace..6d560c986 100644 --- a/prison-mines/build.gradle +++ b/prison-mines/build.gradle @@ -9,6 +9,6 @@ repositories { } dependencies { - compile project(':prison-core') + implementation project(':prison-core') testCompile group: 'junit', name: 'junit', version: '4.12' } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/MineException.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/MineException.java index d29dc45cf..5ae4efee2 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/MineException.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/MineException.java @@ -2,7 +2,9 @@ public class MineException extends Exception { - public MineException(String msg) { + private static final long serialVersionUID = 1L; + + public MineException(String msg) { super(msg); } } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesChatHandler.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesChatHandler.java new file mode 100644 index 000000000..7d11051b7 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesChatHandler.java @@ -0,0 +1,39 @@ +package tech.mcprison.prison.mines; + +import com.google.common.eventbus.Subscribe; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.internal.events.player.PlayerChatEvent; + +public class MinesChatHandler { + + public MinesChatHandler() { + Prison.get().getEventBus().register(this); + } + + + @Subscribe + public void onPlayerChat(PlayerChatEvent e) { + String newFormat = e.getFormat(); + + // Now translates + String translated = Prison.get().getPlatform().getPlaceholders() + .placeholderTranslateText( newFormat ); + + e.setFormat( translated ); + +// MineManager mm = PrisonMines.getInstance().getMineManager(); +// +// List placeholderKeys = mm.getTranslatedPlaceHolderKeys(); +// +// for ( PlaceHolderKey placeHolderKey : placeholderKeys ) { +// String key = "{" + placeHolderKey.getKey() + "}"; +// if ( newFormat.contains( key )) { +// newFormat = newFormat.replace(key, Text.translateAmpColorCodes( +// mm.getTranslateMinesPlaceHolder( placeHolderKey ) )); +// } +// } +// +// e.setFormat(newFormat); + } +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesListener.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesListener.java index 1ba516383..4e0aa7b52 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesListener.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/MinesListener.java @@ -1,15 +1,9 @@ package tech.mcprison.prison.mines; import com.google.common.eventbus.Subscribe; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import tech.mcprison.prison.internal.ItemStack; -import tech.mcprison.prison.internal.Player; -import tech.mcprison.prison.internal.events.block.BlockBreakEvent; -import tech.mcprison.prison.internal.inventory.PlayerInventory; + +import tech.mcprison.prison.internal.events.world.PrisonWorldLoadEvent; import tech.mcprison.prison.selection.SelectionCompletedEvent; -import tech.mcprison.prison.util.BlockType; import tech.mcprison.prison.util.Bounds; /** @@ -25,89 +19,99 @@ public void onSelectionComplete(SelectionCompletedEvent e) { + "&7 blocks. Type /mines create to create it."); } - /** - * Powertool helper - */ + @Subscribe - public void onBlockBreak(BlockBreakEvent e) { - if (PrisonMines.getInstance().getPlayerManager().hasAutosmelt(e.getPlayer())) { - smelt(e.getBlockLocation().getBlockAt() - .getDrops(((PlayerInventory) e.getPlayer().getInventory()).getItemInRightHand())); - } - if (PrisonMines.getInstance().getPlayerManager().hasAutopickup(e.getPlayer())) { - e.getPlayer().getInventory() - .addItem(e.getBlockLocation().getBlockAt().getDrops().toArray(new ItemStack[]{})); - e.getBlockLocation().getBlockAt().setType(BlockType.AIR); - e.setCanceled(true); - } - if (PrisonMines.getInstance().getPlayerManager().hasAutoblock(e.getPlayer())) { - block(e.getPlayer()); - } + public void onWorldLoadListener( PrisonWorldLoadEvent e ) { + + String worldName = e.getWorldName(); + PrisonMines.getInstance().getMineManager().assignAvailableWorld( worldName ); + } + + +// /** +// * Powertool helper +// */ +// @Subscribe +// public void onBlockBreak(BlockBreakEvent e) { +// if (PrisonMines.getInstance().getPlayerManager().hasAutosmelt(e.getPlayer())) { +// smelt(e.getBlockLocation().getBlockAt() +// .getDrops(((PlayerInventory) e.getPlayer().getInventory()).getItemInRightHand())); +// } +// if (PrisonMines.getInstance().getPlayerManager().hasAutopickup(e.getPlayer())) { +// e.getPlayer().getInventory() +// .addItem(e.getBlockLocation().getBlockAt().getDrops().toArray(new ItemStack[]{})); +// e.getBlockLocation().getBlockAt().setType(BlockType.AIR); +// e.setCanceled(true); +// } +// if (PrisonMines.getInstance().getPlayerManager().hasAutoblock(e.getPlayer())) { +// block(e.getPlayer()); +// } +// } - private void smelt(List drops) { - drops.replaceAll(x -> { - if (x.getMaterial() == BlockType.GOLD_ORE) { - return new ItemStack(x.getAmount(), BlockType.GOLD_INGOT); - } else if (x.getMaterial() == BlockType.IRON_ORE) { - return new ItemStack(x.getAmount(), BlockType.IRON_INGOT); - } else { - return x; - } - }); - } +// private void smelt(List drops) { +// drops.replaceAll(x -> { +// if (x.getMaterial() == BlockType.GOLD_ORE) { +// return new ItemStack(x.getAmount(), BlockType.GOLD_INGOT); +// } else if (x.getMaterial() == BlockType.IRON_ORE) { +// return new ItemStack(x.getAmount(), BlockType.IRON_INGOT); +// } else { +// return x; +// } +// }); +// } - private void block(Player player) { - List itemList = Arrays.asList(player.getInventory().getItems()); - List giveBack = new ArrayList<>(); - itemList.replaceAll(x -> { - if (x != null) { - if (x.getMaterial() == BlockType.DIAMOND) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.DIAMOND_BLOCK); - } else if (x.getMaterial() == BlockType.EMERALD) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.EMERALD_BLOCK); - } else if (x.getMaterial() == BlockType.IRON_INGOT) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.IRON_BLOCK); - } else if (x.getMaterial() == BlockType.GLOWSTONE_DUST) { - if (x.getAmount() % 4 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 4, BlockType.GLOWSTONE); - } else if (x.getMaterial() == BlockType.GOLD_INGOT) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.GOLD_BLOCK); - } else if (x.getMaterial() == BlockType.COAL) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.BLOCK_OF_COAL); - } else if (x.getMaterial() == BlockType.REDSTONE) { - if (x.getAmount() % 9 > 0) { - giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); - } - return new ItemStack(x.getAmount() / 9, BlockType.REDSTONE_BLOCK); - } else if (x.getMaterial() == BlockType.LAPIS_LAZULI) { - return new ItemStack(x.getAmount() / 9, BlockType.LAPIS_LAZULI_BLOCK); - } else { - return x; - } - } else { - return x; - } - }); - player.getInventory().setItems(itemList); - player.getInventory().addItem((ItemStack[]) giveBack.toArray()); - } +// private void block(Player player) { +// List itemList = Arrays.asList(player.getInventory().getItems()); +// List giveBack = new ArrayList<>(); +// itemList.replaceAll(x -> { +// if (x != null) { +// if (x.getMaterial() == BlockType.DIAMOND) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.DIAMOND_BLOCK); +// } else if (x.getMaterial() == BlockType.EMERALD) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.EMERALD_BLOCK); +// } else if (x.getMaterial() == BlockType.IRON_INGOT) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.IRON_BLOCK); +// } else if (x.getMaterial() == BlockType.GLOWSTONE_DUST) { +// if (x.getAmount() % 4 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 4, BlockType.GLOWSTONE); +// } else if (x.getMaterial() == BlockType.GOLD_INGOT) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.GOLD_BLOCK); +// } else if (x.getMaterial() == BlockType.COAL) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.BLOCK_OF_COAL); +// } else if (x.getMaterial() == BlockType.REDSTONE) { +// if (x.getAmount() % 9 > 0) { +// giveBack.add(new ItemStack(x.getAmount() % 9, x.getMaterial())); +// } +// return new ItemStack(x.getAmount() / 9, BlockType.REDSTONE_BLOCK); +// } else if (x.getMaterial() == BlockType.LAPIS_LAZULI) { +// return new ItemStack(x.getAmount() / 9, BlockType.LAPIS_LAZULI_BLOCK); +// } else { +// return x; +// } +// } else { +// return x; +// } +// }); +// player.getInventory().setItems(itemList); +// player.getInventory().addItem((ItemStack[]) giveBack.toArray()); +// } } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/PrisonMines.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/PrisonMines.java index 85486119d..d0b3f8b9b 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/PrisonMines.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/PrisonMines.java @@ -19,16 +19,16 @@ package tech.mcprison.prison.mines; import java.io.File; -import java.util.ArrayList; import java.util.List; -import java.util.ListIterator; import java.util.Optional; +import java.util.TreeMap; import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.convert.ConversionManager; import tech.mcprison.prison.error.ErrorManager; import tech.mcprison.prison.file.JsonFileIO; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.localization.LocaleManager; import tech.mcprison.prison.mines.commands.MinesCommands; import tech.mcprison.prison.mines.data.Mine; @@ -38,6 +38,7 @@ import tech.mcprison.prison.modules.Module; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.store.Database; +import tech.mcprison.prison.util.Location; /** * The Prison 3 Mines Module @@ -45,43 +46,67 @@ * @author The MC-Prison Team */ public class PrisonMines extends Module { - - /* - * Fields & Constants - */ + public static final String MODULE_NAME = "Mines"; private static PrisonMines i = null; private MinesConfig config; - private List worlds; +// private List worlds; private LocaleManager localeManager; private Database db; private ErrorManager errorManager; private JsonFileIO jsonFileIO; - private MineManager mines; + private MineManager mineManager; private PlayerManager player; + + /** + *

playerCache tries to provide a faster way to identify which mine a player is + * in. The theory is that there is a very high chance it will be the last mine + * they were in. So this records the last mine they were in, and if that is not + * where they are, then, and only then, do we check all mines to see if it may + * be something else. + *

+ * + */ + private final TreeMap playerCache; + + + public PrisonMines(String version) { - super("Mines", version, 3); + super(MODULE_NAME, version, 3); + + this.playerCache = new TreeMap<>(); } public static PrisonMines getInstance() { return i; } + @Override + public String getBaseCommands() { + return "&7/&2mines"; + } + public void enable() { i = this; - errorManager = new ErrorManager(this); + this.errorManager = new ErrorManager(this); this.jsonFileIO = new JsonFileIO( errorManager, getStatus() ); initDb(); initConfig(); - localeManager = new LocaleManager(this, "lang/mines"); + this.localeManager = new LocaleManager(this, "lang/mines"); - initWorlds(); - initMines(); +// initWorlds(); + + this.mineManager = new MineManager(); + getMineManager().loadFromDbCollection(this); + + player = new PlayerManager(); + +// initMines(); PrisonAPI.getEventBus().register(new MinesListener()); Prison.get().getCommandHandler().registerCommands(new MinesCommands()); @@ -90,9 +115,6 @@ public void enable() { ConversionManager.getInstance().registerConversionAgent(new MinesConversionAgent()); } -// private void initGson() { -// gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); -// } private void initDb() { Optional dbOptional = @@ -124,42 +146,61 @@ private void initConfig() { config = (MinesConfig) getJsonFileIO().readJsonFile( configFile, config ); } -// -// if (!configFile.exists()) { -// try { -// configFile.createNewFile(); -// String json = gson.toJson(config); -// Files.write(configFile.toPath(), json.getBytes()); -// } catch (IOException e) { -// errorManager.throwError( -// new Error("Failed to create config").appendStackTrace("while creating", e)); -// getStatus().toFailed("Failed to create config"); -// } -// } else { -// try { -// String json = new String(Files.readAllBytes(configFile.toPath())); -// config = gson.fromJson(json, MinesConfig.class); -// } catch (IOException e) { -// errorManager.throwError( -// new Error("Failed to load config").appendStackTrace("while loading", e)); -// getStatus().toFailed("Failed to load config"); -// } -// } } - private void initWorlds() { - ListIterator iterator = config.worlds.listIterator(); - worlds = new ArrayList<>(); - while (iterator.hasNext()) { - worlds.add(iterator.next().toLowerCase()); - } - } + + + + /** + *

Search all mines to find if the given block is located within any + * of the mines. If not, then return a null. + *

+ * + * @param block + * @return + */ + public Mine findMineLocation( Location locationToCheck ) { + Mine mine = null; + for ( Mine m : getMines() ) { + if ( m.isInMine( locationToCheck ) ) { + mine = m; + break; + } + } + return mine; + } - private void initMines() { - mines = MineManager.fromDb(); - player = new PlayerManager(); - Prison.get().getPlatform().getScheduler().runTaskTimer(mines.getTimerTask(), 20, 20); - } + public TreeMap getPlayerCache() { + return playerCache; + } + + public Mine findMineLocation( Player player ) { + Mine result = null; + + Long playerUUIDLSB = Long.valueOf( player.getUUID().getLeastSignificantBits() ); + + // Get the cached mine, if it exists: + Mine mine = getPlayerCache().get( playerUUIDLSB ); + + if ( mine == null || !mine.isInMine( player.getLocation() ) ) { + // Look for the correct mine to use. + // Set mine to null so if cannot find the right one it will return a null: + mine = findMineLocation( player.getLocation() ); + + // Store the mine in the player cache if not null: + if ( mine != null ) { + getPlayerCache().put( playerUUIDLSB, mine ); + } + } + + return result; + } + +// private void initMines() { +//// mines = MineManager.fromDb(); +//// player = new PlayerManager(); +//// Prison.get().getPlatform().getScheduler().runTaskTimer(mines.getTimerTask(), 20, 20); +// } public JsonFileIO getJsonFileIO() { @@ -173,8 +214,7 @@ public JsonFileIO getJsonFileIO() * */ public void disable() { - -// mines.saveMines(); + // Nothing to do... } public MinesConfig getConfig() { @@ -186,20 +226,24 @@ public Database getDb() { } public MineManager getMineManager() { - return mines; + return mineManager; } public List getMines() { - return mines.getMines(); + return getMineManager().getMines(); } + public Mine getMine(String mineName) { + return getMineManager().getMine(mineName); + } + public LocaleManager getMinesMessages() { return localeManager; } - public List getWorlds() { - return worlds; - } +// public List getWorlds() { +// return worlds; +// } public PlayerManager getPlayerManager() { return player; diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/commands/MinesCommands.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/commands/MinesCommands.java index e9f2735bb..8ef929c41 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/commands/MinesCommands.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/commands/MinesCommands.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; import org.apache.commons.lang3.StringUtils; @@ -29,16 +32,21 @@ import tech.mcprison.prison.chat.FancyMessage; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; +import tech.mcprison.prison.commands.CommandPagedData; +import tech.mcprison.prison.commands.Wildcard; import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.localization.Localizable; import tech.mcprison.prison.mines.PrisonMines; import tech.mcprison.prison.mines.data.Block; import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.mines.data.MineData; +import tech.mcprison.prison.mines.data.MineData.MineNotificationMode; +import tech.mcprison.prison.mines.managers.MineManager; import tech.mcprison.prison.output.BulletedListComponent; -import tech.mcprison.prison.output.ButtonComponent; -import tech.mcprison.prison.output.ButtonComponent.Style; import tech.mcprison.prison.output.ChatDisplay; +import tech.mcprison.prison.output.FancyMessageComponent; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.output.RowComponent; import tech.mcprison.prison.selection.Selection; @@ -55,10 +63,11 @@ public class MinesCommands { private String lastMineReferenced; private Long lastMineReferencedTimestamp; + - private boolean performCheckMineExists(CommandSender sender, String name) { - name = Text.stripColor( name ); - if (!PrisonMines.getInstance().getMineManager().getMine(name).isPresent()) { + private boolean performCheckMineExists(CommandSender sender, String mineName) { + mineName = Text.stripColor( mineName ); + if (PrisonMines.getInstance().getMine(mineName) == null) { PrisonMines.getInstance().getMinesMessages().getLocalizable("mine_does_not_exist") .sendTo(sender); return false; @@ -66,12 +75,27 @@ private boolean performCheckMineExists(CommandSender sender, String name) { return true; } - @Command(identifier = "mines create", description = "Creates a new mine.", permissions = "mines.create") + @Command(identifier = "mines create", description = "Creates a new mine.", + onlyPlayers = false, permissions = "mines.create") public void createCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the new mine.") String name) { + @Wildcard(join=true) + @Arg(name = "mineName", description = "The name of the new mine.", def = " ") String mineName) { + + if ( mineName == null || mineName.contains( " " ) || mineName.trim().length() == 0 ) { + sender.sendMessage( "&3Names cannot contain spaces or be empty. &b[&d" + mineName + "&b]" ); + return; + } + mineName = mineName.trim(); + + Player player = getPlayer( sender ); + + if (player == null || !player.isOnline()) { + sender.sendMessage( "&3You must be a player in the game to run this command." ); + return; + } PrisonMines pMines = PrisonMines.getInstance(); - Selection selection = Prison.get().getSelectionManager().getSelection((Player) sender); + Selection selection = Prison.get().getSelectionManager().getSelection(player); if (!selection.isComplete()) { pMines.getMinesMessages().getLocalizable("select_bounds") .sendTo(sender, Localizable.Level.ERROR); @@ -85,31 +109,48 @@ public void createCommand(CommandSender sender, return; } - if (PrisonMines.getInstance().getMines().stream() - .anyMatch(mine -> mine.getName().equalsIgnoreCase(name))) { + if (PrisonMines.getInstance().getMine(mineName) != null) { pMines.getMinesMessages().getLocalizable("mine_exists") .sendTo(sender, Localizable.Level.ERROR); return; } - setLastMineReferenced(name); + setLastMineReferenced(mineName); - Mine mine = new Mine().setBounds(selection.asBounds()).setName(name); + Mine mine = new Mine(mineName, selection); pMines.getMineManager().add(mine); pMines.getMinesMessages().getLocalizable("mine_created").sendTo(sender); + + // Delete the selection: + Prison.get().getSelectionManager().clearSelection((Player) sender); + } - @Command(identifier = "mines set spawn", description = "Set the mine's spawn to where you're standing.", permissions = "mines.set") + @Command(identifier = "mines set spawn", description = "Set the mine's spawn to where you're standing.", + onlyPlayers = false, permissions = "mines.set") public void spawnpointCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to edit.") String name) { + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName) { - if (!performCheckMineExists(sender, name)) { + Player player = getPlayer( sender ); + + if (player == null || !player.isOnline()) { + sender.sendMessage( "&3You must be a player in the game to run this command." ); + return; + } + + if (!performCheckMineExists(sender, mineName)) { return; } PrisonMines pMines = PrisonMines.getInstance(); - if (!PrisonMines.getInstance().getMineManager().getMine(name).get().getWorld() - .isPresent()) { + Mine mine = pMines.getMine(mineName); + + if ( !mine.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + if (!mine.getWorld().isPresent()) { pMines.getMinesMessages().getLocalizable("missing_world") .sendTo(sender); return; @@ -117,159 +158,285 @@ public void spawnpointCommand(CommandSender sender, if (!((Player) sender).getLocation().getWorld().getName() .equalsIgnoreCase( - pMines.getMineManager().getMine(name).get().getWorld().get() - .getName())) { + mine.getWorldName())) { pMines.getMinesMessages().getLocalizable("spawnpoint_same_world") .sendTo(sender); return; } - setLastMineReferenced(name); + setLastMineReferenced(mineName); - Mine mine = pMines.getMineManager().getMine(name).get(); mine.setSpawn(((Player) sender).getLocation()); pMines.getMineManager().saveMine(mine); pMines.getMinesMessages().getLocalizable("spawn_set").sendTo(sender); } - @Command(identifier = "mines block add", permissions = "mines.block", onlyPlayers = false, description = "Adds a block to a mine.") + @Command(identifier = "mines block add", permissions = "mines.block", onlyPlayers = false, + description = "Adds a block to a mine.") public void addBlockCommand(CommandSender sender, @Arg(name = "mineName", description = "The name of the mine to add the block to.") - String mine, + String mineName, @Arg(name = "block", description = "The block's name or ID.") String block, @Arg(name = "chance", description = "The percent chance (out of 100) that this block will occur.") double chance) { - if (!performCheckMineExists(sender, mine)) { + if (!performCheckMineExists(sender, mineName)) { return; } PrisonMines pMines = PrisonMines.getInstance(); - setLastMineReferenced(mine); + setLastMineReferenced(mineName); - Mine m = pMines.getMineManager().getMine(mine).get(); - - BlockType blockType = BlockType.getBlock(block); - if (blockType == null || blockType.getMaterialType() != MaterialType.BLOCK ) { - pMines.getMinesMessages().getLocalizable("not_a_block") - .withReplacements(block).sendTo(sender); - return; - } - - if (m.isInMine(blockType)) { - pMines.getMinesMessages().getLocalizable("block_already_added") - .sendTo(sender); - return; - } - - if ( chance <= 0 ) { - sender.sendMessage( "The percent chance must have a value greater than zero." ); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); return; } - final double[] totalComp = {chance}; - m.getBlocks().forEach(block1 -> totalComp[0] += block1.getChance()); - if (totalComp[0] > 100.0d) { - pMines.getMinesMessages().getLocalizable("mine_full") - .sendTo(sender, Localizable.Level.ERROR); - return; + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) ) { + + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( block ); + if ( prisonBlock != null ) { + pMines.getMinesMessages().getLocalizable("not_a_block"). + withReplacements(block).sendTo(sender); + return; + } + + + if (m.isInMine(prisonBlock)) { + pMines.getMinesMessages().getLocalizable("block_already_added"). + sendTo(sender); + return; + } + + if ( chance <= 0 ) { + sender.sendMessage( "The percent chance must have a value greater than zero." ); + return; + } + + final double[] totalComp = {chance}; + + m.getPrisonBlocks().forEach(block1 -> totalComp[0] += block1.getChance()); + if (totalComp[0] > 100.0d) { + pMines.getMinesMessages().getLocalizable("mine_full"). + sendTo(sender, Localizable.Level.ERROR); + return; + } + + prisonBlock.setChance( chance ); + m.getPrisonBlocks().add( prisonBlock ); + } + else { + + BlockType blockType = BlockType.getBlock(block); + if (blockType == null || blockType.getMaterialType() != MaterialType.BLOCK ) { + pMines.getMinesMessages().getLocalizable("not_a_block") + .withReplacements(block).sendTo(sender); + return; + } + + if (m.isInMine(blockType)) { + pMines.getMinesMessages().getLocalizable("block_already_added") + .sendTo(sender); + return; + } + + if ( chance <= 0 ) { + sender.sendMessage( "The percent chance must have a value greater than zero." ); + return; + } + + final double[] totalComp = {chance}; + + m.getBlocks().forEach(block1 -> totalComp[0] += block1.getChance()); + if (totalComp[0] > 100.0d) { + pMines.getMinesMessages().getLocalizable("mine_full") + .sendTo(sender, Localizable.Level.ERROR); + return; + } + + m.getBlocks().add(new Block(blockType, chance)); } - m.getBlocks().add(new Block(blockType, chance)); pMines.getMineManager().saveMine( m ); pMines.getMinesMessages().getLocalizable("block_added") - .withReplacements(block, mine).sendTo(sender); - getBlocksList(m).send(sender); + .withReplacements(block, mineName).sendTo(sender); + getBlocksList(m, null).send(sender); - pMines.getMineManager().clearCache(); + //pMines.getMineManager().clearCache(); } @Command(identifier = "mines block set", permissions = "mines.block", onlyPlayers = false, description = "Changes the percentage of a block in a mine.") public void setBlockCommand(CommandSender sender, @Arg(name = "mineName", description = "The name of the mine to edit.") - String mine, + String mineName, @Arg(name = "block", description = "The block's name or ID.") String block, @Arg(name = "chance", description = "The percent chance (out of 100) that this block will occur.") double chance) { - if (!performCheckMineExists(sender, mine)) { + if (!performCheckMineExists(sender, mineName)) { return; } - setLastMineReferenced(mine); + setLastMineReferenced(mineName); PrisonMines pMines = PrisonMines.getInstance(); - Mine m = pMines.getMineManager().getMine(mine).get(); + Mine m = pMines.getMine(mineName); - BlockType blockType = BlockType.getBlock(block); - if (blockType == null) { - pMines.getMinesMessages().getLocalizable("not_a_block") - .withReplacements(block).sendTo(sender); - return; + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; } - - // Change behavior: If trying to change a block that is not in the mine, then instead add it: - if (!m.isInMine(blockType)) { - addBlockCommand( sender, mine, block, chance ); + + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) ) { + + + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( block ); + if ( prisonBlock == null ) { + pMines.getMinesMessages().getLocalizable("not_a_block"). + withReplacements(block).sendTo(sender); + return; + } + + + // Change behavior: If trying to change a block that is not in the mine, then instead add it: + if (!m.isInMine(prisonBlock)) { + addBlockCommand( sender, mineName, block, chance ); // pMines.getMinesMessages().getLocalizable("block_not_removed") // .sendTo(sender); - return; - } - - // If it's 0, just delete it! - if (chance <= 0.0d) { - deleteBlock( sender, pMines, m, blockType ); + return; + } + + // If it's 0, just delete it! + if (chance <= 0.0d) { + deleteBlock( sender, pMines, m, prisonBlock ); // delBlockCommand(sender, mine, block); - return; - } - - final double[] totalComp = {chance}; - m.getBlocks().forEach(block1 -> { - if (block1.getType() == blockType) { - totalComp[0] -= block1.getChance(); - } else { - totalComp[0] += block1.getChance(); - } - }); - if (totalComp[0] > 100.0d) { - pMines.getMinesMessages().getLocalizable("mine_full") - .sendTo(sender, Localizable.Level.ERROR); - return; + return; + } + + + double totalChance = chance; + PrisonBlock blockToUpdate = null; + for ( PrisonBlock blk : m.getPrisonBlocks() ) { + if ( blk.getBlockName().equalsIgnoreCase( prisonBlock.getBlockName() ) ) { + totalChance -= blk.getChance(); + blockToUpdate = blk; + } + else { + totalChance += blk.getChance(); + } + } + + if (totalChance > 100.0d) { + pMines.getMinesMessages().getLocalizable("mine_full"). + sendTo(sender, Localizable.Level.ERROR); + return; + } + + blockToUpdate.setChance( chance ); + +// // total chance is not being calculated correctly... +// +// final double[] totalComp = {chance}; +// m.getPrisonBlocks().forEach(block1 -> { +// totalComp[0] -= block1.getChance(); +// }); +// +// if (totalComp[0] > 100.0d) { +// pMines.getMinesMessages().getLocalizable("mine_full") +// .sendTo(sender, Localizable.Level.ERROR); +// return; +// } +// +// for (PrisonBlock blockObject : m.getPrisonBlocks()) { +// if (blockObject.getBlockName().equalsIgnoreCase( prisonBlock.getBlockName() )) { +// blockObject.setChance(chance); +// } +// } + } - - for (Block blockObject : m.getBlocks()) { - if (blockObject.getType() == blockType) { - blockObject.setChance(chance); - } + else { + + BlockType blockType = BlockType.getBlock(block); + if (blockType == null) { + pMines.getMinesMessages().getLocalizable("not_a_block"). + withReplacements(block).sendTo(sender); + return; + } + + // Change behavior: If trying to change a block that is not in the mine, then instead add it: + if (!m.isInMine(blockType)) { + addBlockCommand( sender, mineName, block, chance ); +// pMines.getMinesMessages().getLocalizable("block_not_removed") +// .sendTo(sender); + return; + } + + // If it's 0, just delete it! + if (chance <= 0.0d) { + deleteBlock( sender, pMines, m, blockType ); +// delBlockCommand(sender, mine, block); + return; + } + + + double totalChance = chance; + Block blockToUpdate = null; + for ( Block blk : m.getBlocks() ) { + if ( blk.getType() == blockType ) { + totalChance -= blk.getChance(); + blockToUpdate = blk; + } + else { + totalChance += blk.getChance(); + } + } + + if (totalChance > 100.0d) { + pMines.getMinesMessages().getLocalizable("mine_full"). + sendTo(sender, Localizable.Level.ERROR); + return; + } + + blockToUpdate.setChance( chance ); + } + + pMines.getMineManager().saveMine( m ); pMines.getMinesMessages().getLocalizable("block_set") - .withReplacements(block, mine).sendTo(sender); - getBlocksList(m).send(sender); + .withReplacements(block, mineName).sendTo(sender); + getBlocksList(m, null).send(sender); - pMines.getMineManager().clearCache(); + //pMines.getMineManager().clearCache(); } @Command(identifier = "mines block remove", permissions = "mines.block", onlyPlayers = false, description = "Deletes a block from a mine.") public void delBlockCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to edit.") String mine, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, @Arg(name = "block", def = "AIR", description = "The block's name or ID.") String block) { - if (!performCheckMineExists(sender, mine)) { + if (!performCheckMineExists(sender, mineName)) { return; } - setLastMineReferenced(mine); + setLastMineReferenced(mineName); PrisonMines pMines = PrisonMines.getInstance(); - Mine m = pMines.getMineManager().getMine(mine).get(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } BlockType blockType = BlockType.getBlock(block); if (blockType == null) { @@ -278,7 +445,7 @@ public void delBlockCommand(CommandSender sender, return; } - if (m.isInMine(blockType)) { + if (!m.isInMine(blockType)) { pMines.getMinesMessages().getLocalizable("block_not_removed") .sendTo(sender); return; @@ -287,19 +454,59 @@ public void delBlockCommand(CommandSender sender, deleteBlock( sender, pMines, m, blockType ); } + /** + * Delete only the first occurrence of a block with the given BlockType. + * + * @param sender + * @param pMines + * @param m + * @param prisonBlock + */ + private void deleteBlock( CommandSender sender, PrisonMines pMines, Mine m, PrisonBlock prisonBlock ) + { + PrisonBlock rBlock = null; + for ( PrisonBlock block : m.getPrisonBlocks() ) { + if (block.getBlockName().equalsIgnoreCase( prisonBlock.getBlockName() )) { + rBlock = block; + break; + } + } + if ( m.getPrisonBlocks().remove( rBlock )) { + pMines.getMineManager().saveMine( m ); + + pMines.getMinesMessages().getLocalizable("block_deleted"). + withReplacements(prisonBlock.getBlockName(), m.getName()).sendTo(sender); + getBlocksList(m, null).send(sender); + } + } + /** + * Delete only the first occurrence of a block with the given BlockType. + * + * @param sender + * @param pMines + * @param m + * @param blockType + */ private void deleteBlock( CommandSender sender, PrisonMines pMines, Mine m, BlockType blockType ) { - m.getBlocks().removeIf(x -> x.getType() == blockType); - pMines.getMineManager().saveMine( m ); - - pMines.getMinesMessages().getLocalizable("block_deleted") - .withReplacements(blockType.name(), m.getName()).sendTo(sender); - getBlocksList(m).send(sender); - - pMines.getMineManager().clearCache(); + Block rBlock = null; + for ( Block block : m.getBlocks() ) { + if ( block.getType() == blockType ) { + rBlock = block; + break; + } + } + if ( m.getBlocks().remove( rBlock )) { + pMines.getMineManager().saveMine( m ); + + pMines.getMinesMessages().getLocalizable("block_deleted") + .withReplacements(blockType.name(), m.getName()).sendTo(sender); + getBlocksList(m, null).send(sender); + } } - @Command(identifier = "mines block search", permissions = "mines.block", description = "Searches for a block to add to a mine.") + @Command(identifier = "mines block search", permissions = "mines.block", + description = "Searches for a block to add to a mine.") public void searchBlockCommand(CommandSender sender, @Arg(name = "search", def = " ", description = "Any part of the block's name or ID.") String search, @Arg(name = "page", def = "1", description = "Page of search results (optional)") String page ) { @@ -314,7 +521,7 @@ public void searchBlockCommand(CommandSender sender, display.send(sender); - pMines.getMineManager().clearCache(); + //pMines.getMineManager().clearCache(); } private ChatDisplay blockSearchBuilder(String search, String page) @@ -330,20 +537,26 @@ private ChatDisplay blockSearchBuilder(String search, String page) } } - int curPage = 1; - int pageSize = 10; - int pages = (blocks.size() / pageSize) + 1; - try - { - curPage = Integer.parseInt(page); - } - catch ( NumberFormatException e ) - { - // Ignore: Not an integer, will use the default value. - } - curPage = ( curPage < 1 ? 1 : (curPage > pages ? pages : curPage )); - int pageStart = (curPage - 1) * pageSize; - int pageEnd = ((pageStart + pageSize) > blocks.size() ? blocks.size() : pageStart + pageSize); + + CommandPagedData cmdPageData = new CommandPagedData( + "/mines block search " + search, blocks.size(), + 0, page ); + + // Same page logic as in mines info +// int curPage = 1; +// int pageSize = 10; +// int pages = (blocks.size() / pageSize) + 1; +// try +// { +// curPage = Integer.parseInt(page); +// } +// catch ( NumberFormatException e ) +// { +// // Ignore: Not an integer, will use the default value. +// } +// curPage = ( curPage < 1 ? 1 : (curPage > pages ? pages : curPage )); +// int pageStart = (curPage - 1) * pageSize; +// int pageEnd = ((pageStart + pageSize) > blocks.size() ? blocks.size() : pageStart + pageSize); ChatDisplay display = new ChatDisplay("Block Search (" + blocks.size() + ")"); @@ -351,7 +564,7 @@ private ChatDisplay blockSearchBuilder(String search, String page) BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); - for ( int i = pageStart; i < pageEnd; i++ ) + for ( int i = cmdPageData.getPageStart(); i < cmdPageData.getPageEnd(); i++ ) { BlockType block = blocks.get(i); FancyMessage msg = @@ -364,27 +577,10 @@ private ChatDisplay blockSearchBuilder(String search, String page) } display.addComponent(builder.build()); - // Need to construct a dynamic row of buttons. It may have no buttons, both, or - // a combination of previous page or next page. But it will always have a page - // count between the two. - RowComponent row = new RowComponent(); - if ( curPage > 1 ) - { - row.addFancy( - new ButtonComponent( "&e<-- Prev Page", '-', Style.NEGATIVE) - .runCommand("/mines block search " + search + " " + (curPage - 1), - "View the prior page of search results").getFancyMessage() ); - } - row.addFancy( - new FancyMessage(" &9< &3Page " + curPage + " of " + pages + " &9> ") ); - if ( curPage < pages ) - { - row.addFancy( - new ButtonComponent( "&eNext Page -->", '+', Style.POSITIVE) - .runCommand("/mines block search " + search + " " + (curPage + 1), - "View the prior page of search results").getFancyMessage() ); - } - display.addComponent( row ); + // This command plus parameters used: +// String pageCmd = "/mines block search " + search; + + cmdPageData.generatePagedCommandFooter( display ); return display; } @@ -392,13 +588,13 @@ private ChatDisplay blockSearchBuilder(String search, String page) @Command(identifier = "mines delete", permissions = "mines.delete", onlyPlayers = false, description = "Deletes a mine.") public void deleteCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to delete.") String name, + @Arg(name = "mineName", description = "The name of the mine to delete.") String mineName, @Arg(name = "confirm", def = "", description = "Confirm that the mine should be deleted") String confirm) { - if (!performCheckMineExists(sender, name)) { + if (!performCheckMineExists(sender, mineName)) { return; } - setLastMineReferenced(name); + setLastMineReferenced(mineName); // They have 1 minute to confirm. long now = System.currentTimeMillis(); @@ -407,25 +603,44 @@ public void deleteCommand(CommandSender sender, setConfirmTimestamp( null ); PrisonMines pMines = PrisonMines.getInstance(); - pMines.getMineManager().removeMine(pMines.getMineManager().getMine(name).get()); + + Mine mine = pMines.getMine(mineName); + + if ( !mine.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + // Remove from the manager: + pMines.getMineManager().removeMine(mine); + + // Terminate the running task for mine resets. Will allow it to be garbage collected. + mine.terminateJob(); + + setLastMineReferenced(null); + pMines.getMinesMessages().getLocalizable("mine_deleted").sendTo(sender); } else if ( getConfirmTimestamp() == null || ((now - getConfirmTimestamp()) >= 1000 * 60 ) ) { setConfirmTimestamp( now ); - ChatDisplay chatDisplay = new ChatDisplay("&cDelete " + name); + ChatDisplay chatDisplay = new ChatDisplay("&cDelete " + mineName); BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); builder.add( new FancyMessage( "&3Confirm the deletion of this mine" ) - .suggest("/mines delete " + name + " cancel")); + .suggest("/mines delete " + mineName + " cancel")); builder.add( new FancyMessage( "&3Click &eHERE&3 to display the command" ) - .suggest("/mines delete " + name + " cancel")); + .suggest("/mines delete " + mineName + " cancel")); + + builder.add( new FancyMessage( + "&3Enter: &7/mines delete " + mineName + " confirm" ) + .suggest("/mines delete " + mineName + " cancel")); builder.add( new FancyMessage( "&3Then change &ecancel&3 to &econfirm&3." ) - .suggest("/mines delete " + name + " cancel")); + .suggest("/mines delete " + mineName + " cancel")); builder.add( new FancyMessage("You have 1 minute to respond.")); @@ -435,13 +650,13 @@ public void deleteCommand(CommandSender sender, } else if (confirm != null && "cancel".equalsIgnoreCase( confirm )) { setConfirmTimestamp( null ); - ChatDisplay display = new ChatDisplay("&cDelete " + name); + ChatDisplay display = new ChatDisplay("&cDelete " + mineName); display.text("&8Delete canceled."); display.send( sender ); } else { - ChatDisplay display = new ChatDisplay("&cDelete " + name); + ChatDisplay display = new ChatDisplay("&cDelete " + mineName); display.text("&8Delete confirmation failed. Try again."); display.send( sender ); @@ -449,66 +664,330 @@ public void deleteCommand(CommandSender sender, } - @Command(identifier = "mines info", permissions = "mines.info", onlyPlayers = false, description = "Lists information about a mine.") + @Command(identifier = "mines info", permissions = "mines.info", onlyPlayers = false, + description = "Lists information about a mine.") public void infoCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to view.") String name) { - if (!performCheckMineExists(sender, name)) { + @Arg(name = "mineName", description = "The name of the mine to view.") String mineName, + @Arg(name = "page", def = "1", + description = "Page of search results (optional) [1-n, ALL]") String page + ) { + if (!performCheckMineExists(sender, mineName)) { return; } - setLastMineReferenced(name); + + setLastMineReferenced(mineName); PrisonMines pMines = PrisonMines.getInstance(); - Mine m = pMines.getMineManager().getMine(name).get(); - - ChatDisplay chatDisplay = new ChatDisplay(m.getName()); + MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + + + CommandPagedData cmdPageData = null; + + + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) ) { + + cmdPageData = new CommandPagedData( + "/mines info " + m.getName(), m.getPrisonBlocks().size(), + 1, page ); + } + else { + + cmdPageData = new CommandPagedData( + "/mines info " + m.getName(), m.getBlocks().size(), + 1, page ); + } + +// // Same page logic as in mines block search: +// int curPage = 1; +// int pageSize = 10; +// int pages = (m.getBlocks().size() / pageSize) + 1; +// try +// { +// curPage = Integer.parseInt(page); +// } +// catch ( NumberFormatException e ) +// { +// // Ignore: Not an integer, will use the default value. +// } +// curPage = ( curPage < 1 ? 1 : (curPage > pages ? pages : curPage )); +// int pageStart = (curPage - 1) * pageSize; +// int pageEnd = ((pageStart + pageSize) > m.getBlocks().size() ? m.getBlocks().size() : pageStart + pageSize); - String worldName = m.getWorld().isPresent() ? m.getWorld().get().getName() : "&cmissing"; - chatDisplay.text("&3World: &7%s", worldName); + - String minCoords = m.getBounds().getMin().toBlockCoordinates(); - String maxCoords = m.getBounds().getMax().toBlockCoordinates(); - chatDisplay.text("&3Bounds: &7%s &8to &7%s", minCoords, maxCoords); + DecimalFormat dFmt = new DecimalFormat("#,##0"); + DecimalFormat fFmt = new DecimalFormat("#,##0.00"); + + ChatDisplay chatDisplay = new ChatDisplay("&bMine: &3" + m.getName()); + // Display Mine Info only: + if ( cmdPageData.getCurPage() == 1 ) { + + if ( !m.isEnabled() ) { + chatDisplay.text("&cWarning!! This mine is &lDISABLED&r&c!!" ); + } + + + String worldName = m.getWorld().isPresent() ? m.getWorld().get().getName() : "&cmissing"; + chatDisplay.text("&3World: &7%s", worldName); + + String minCoords = m.getBounds().getMin().toBlockCoordinates(); + String maxCoords = m.getBounds().getMax().toBlockCoordinates(); + chatDisplay.text("&3Bounds: &7%s &8to &7%s", minCoords, maxCoords); + Player player = getPlayer( sender ); + + chatDisplay.text("&3Center: &7%s &3%s &7%s", + m.getBounds().getCenter().toBlockCoordinates(), + (player == null ? "" : "Distance:"), + (player == null ? "" : fFmt.format( m.getBounds().getDistance3d( player.getLocation() ) )) + ); + if ( mMan.isMineStats() ) { + } + + + String spawnPoint = m.getSpawn() != null ? m.getSpawn().toBlockCoordinates() : "&cnot set"; + chatDisplay.text("&3Spawnpoint: &7%s", spawnPoint); + + if ( mMan.isMineStats() ) { + RowComponent rowStats = new RowComponent(); + rowStats.addTextComponent( " -- &7 Stats :: " ); + rowStats.addTextComponent( m.statsMessage() ); + + chatDisplay.addComponent(rowStats); + } + + + { + RowComponent row = new RowComponent(); + double rtMinutes = m.getResetTime() / 60.0D; + row.addTextComponent( "&3Reset time: &7%s &3Secs (&7%.2f &3Mins)", + Integer.toString(m.getResetTime()), rtMinutes ); + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Mine Reset Count: &7%s ", + dFmt.format(m.getResetCount()) ); + + if ( m.isUsePagingOnReset() ) { + row.addTextComponent( " &7-= &5Reset Paging Enabled &7=-" ); + } + else { + row.addTextComponent( " &7-= &3Reset Paging Disabled &7=-" ); + } + + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + + long targetResetTime = m.getTargetResetTime(); + double remaining = ( targetResetTime <= 0 ? 0d : + (targetResetTime - System.currentTimeMillis()) / 1000d); + double rtMinutes = remaining / 60.0D; + + row.addTextComponent( "&3Time Remaining Until Reset: &7%s &3Secs (&7%.2f &3Mins)", + dFmt.format( remaining ), rtMinutes ); + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Notification Mode: &7%s &7%s", + m.getNotificationMode().name(), + ( m.getNotificationMode() == MineNotificationMode.radius ? + dFmt.format( m.getNotificationRadius() ) + " blocks" : "" ) ); + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Notifications Filtered by Permissions: %s", + ( m.isUseNotificationPermission() ? "&2Enabled" : "&dDisabled" ) ); + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Notification Permission: &7%s", + m.getMineNotificationPermissionName() ); + chatDisplay.addComponent( row ); + } + + + + // chatDisplay.text("&3Size: &7%d&8x&7%d&8x&7%d", Math.round(m.getBounds().getWidth()), // Math.round(m.getBounds().getHeight()), Math.round(m.getBounds().getLength())); + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Size: &7%d&8x&7%d&8x&7%d", Math.round(m.getBounds().getWidth()), + Math.round(m.getBounds().getHeight()), Math.round(m.getBounds().getLength()) ); + + row.addTextComponent( " &3Volume: &7%s &3Blocks", + dFmt.format( Math.round(m.getBounds().getTotalBlockCount())) ); + chatDisplay.addComponent( row ); + } + + + { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Blocks Remaining: &7%s %s%% ", + dFmt.format( m.getRemainingBlockCount() ), + fFmt.format( m.getPercentRemainingBlockCount() ) ); + + chatDisplay.addComponent( row ); + } + + { + RowComponent row = new RowComponent(); + if ( m.isZeroBlockResetDisabled() ) { + row.addTextComponent( "&3Zero Blocks Reset Delay: &cDISABLED"); + } else { + if ( m.getResetThresholdPercent() == 0 ) { + row.addTextComponent( "&3Zero Blocks Reset Delay: &7%s &3Seconds", + fFmt.format( m.getZeroBlockResetDelaySec() )); + } + else { + row.addTextComponent( "&7Threshold &3Reset Delay: &7%s &3Seconds", + fFmt.format( m.getZeroBlockResetDelaySec() )); + } + } + + chatDisplay.addComponent( row ); + } + + + { + RowComponent row = new RowComponent(); + if ( m.getResetThresholdPercent() == 0 ) { + row.addTextComponent( "&3Reset Threshold: &cDISABLED"); + } else { + + double blocks = m.getBounds().getTotalBlockCount() * + m.getResetThresholdPercent() / 100.0d; + row.addTextComponent( "&3Reset Threshold: &7%s &3Percent (&7%s &3blocks)", + fFmt.format( m.getResetThresholdPercent() ), + dFmt.format( blocks ) ); + } + + chatDisplay.addComponent( row ); + } + + + if ( m.isSkipResetEnabled() ) { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Skip Reset &2Enabled&3: &3Threshold: &7%s &3Skip Limit: &7%s", + fFmt.format( m.getSkipResetPercent() ), dFmt.format( m.getSkipResetBypassLimit() )); + chatDisplay.addComponent( row ); + + if ( m.getSkipResetBypassCount() > 0 ) { + RowComponent row2 = new RowComponent(); + row2.addTextComponent( " &3Skipping Enabled: Skip Count: &7%s", + dFmt.format( m.getSkipResetBypassCount() )); + chatDisplay.addComponent( row2 ); + } + } else { + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Skip Mine Reset if no Activity: &cnot set"); + chatDisplay.addComponent( row ); + } + + + if ( m.getResetCommands() != null && m.getResetCommands().size() > 0 ) { +// RowComponent row = new RowComponent(); +// row.addTextComponent( "&3Reset Commands: &7%s ", +// dFmt.format( m.getResetCommands().size() ) ); + + BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); + + FancyMessage msg = new FancyMessage(String.format("&3Reset Commands: &7%s", + dFmt.format( m.getResetCommands().size() ))) + .suggest("/mines command list " + m.getName()) + .tooltip("&7Click to list to view the reset commands."); + + builder.add(msg); + + chatDisplay.addComponent( builder.build() ); + } - RowComponent row = new RowComponent(); - row.addTextComponent( "&3Size: &7%d&8x&7%d&8x&7%d", Math.round(m.getBounds().getWidth()), - Math.round(m.getBounds().getHeight()), Math.round(m.getBounds().getLength()) ); + + } - row.addTextComponent( " &3Volume: &7%d &3Blocks", Math.round(m.getBounds().getTotalBlockCount()) ); - chatDisplay.addComponent( row ); + if ( cmdPageData.isShowAll() || cmdPageData.getCurPage() > 1 ) { + chatDisplay.text("&3Blocks:"); + chatDisplay.text("&8Click on a block's name to edit its chances of appearing."); + BulletedListComponent list = getBlocksList(m, cmdPageData); + + chatDisplay.addComponent(list); + } + int blockSize = 0; - String spawnPoint = m.getSpawn() != null ? m.getSpawn().toBlockCoordinates() : "&cnot set"; - chatDisplay.text("&3Spawnpoint: &7%s", spawnPoint); - - chatDisplay.text("&3Blocks:"); - chatDisplay.text("&8Click on a block's name to edit its chances of appearing."); - BulletedListComponent list = getBlocksList(m); - chatDisplay.addComponent(list); + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) ) { + blockSize = m.getPrisonBlocks().size(); + } + else { + blockSize = m.getBlocks().size(); + } + + String message = blockSize != 0 ? null : " &cNo Blocks Defined"; + cmdPageData.generatePagedCommandFooter( chatDisplay, message ); chatDisplay.send(sender); } - private BulletedListComponent getBlocksList(Mine m) { + private BulletedListComponent getBlocksList(Mine m, CommandPagedData cmdPageData) { BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); DecimalFormat dFmt = new DecimalFormat("##0.00"); double totalChance = 0.0d; - for (Block block : m.getBlocks()) { - double chance = Math.round(block.getChance() * 100.0d) / 100.0d; - totalChance += chance; - - String blockName = - StringUtils.capitalize(block.getType().name().replaceAll("_", " ").toLowerCase()); - String percent = dFmt.format(chance) + "%"; - FancyMessage msg = new FancyMessage(String.format("&7%s - %s (%s)", - percent, block.getType().name(), blockName)) - .suggest("/mines block set " + m.getName() + " " + block.getType().name() + " %") - .tooltip("&7Click to edit the block's chance."); - builder.add(msg); + int count = 0; + + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) ) { + + for (PrisonBlock block : m.getPrisonBlocks()) { + double chance = Math.round(block.getChance() * 100.0d) / 100.0d; + totalChance += chance; + + if ( cmdPageData == null || + count++ >= cmdPageData.getPageStart() && count <= cmdPageData.getPageEnd() ) { + String blockName = + StringUtils.capitalize(block.getBlockName().replaceAll("_", " ").toLowerCase()); + String percent = dFmt.format(chance) + "%"; + FancyMessage msg = new FancyMessage(String.format("&7%s - %s (%s)", + percent, block.getBlockName(), blockName)) + .suggest("/mines block set " + m.getName() + " " + block.getBlockName() + " %") + .tooltip("&7Click to edit the block's chance."); + builder.add(msg); + + } + } + } + else { + + for (Block block : m.getBlocks()) { + double chance = Math.round(block.getChance() * 100.0d) / 100.0d; + totalChance += chance; + + if ( cmdPageData == null || + count++ >= cmdPageData.getPageStart() && count <= cmdPageData.getPageEnd() ) { + String blockName = + StringUtils.capitalize(block.getType().name().replaceAll("_", " ").toLowerCase()); + String percent = dFmt.format(chance) + "%"; + FancyMessage msg = new FancyMessage(String.format("&7%s - %s (%s)", + percent, block.getType().name(), blockName)) + .suggest("/mines block set " + m.getName() + " " + block.getType().name() + " %") + .tooltip("&7Click to edit the block's chance."); + builder.add(msg); + + } + } } if (totalChance < 100.0d) { @@ -520,21 +999,29 @@ private BulletedListComponent getBlocksList(Mine m) { @Command(identifier = "mines reset", permissions = "mines.reset", description = "Resets a mine.") public void resetCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to reset.") String name) { + @Arg(name = "mineName", description = "The name of the mine to reset.") String mineName) { - if (!performCheckMineExists(sender, name)) { + if (!performCheckMineExists(sender, mineName)) { return; } - setLastMineReferenced(name); + setLastMineReferenced(mineName); PrisonMines pMines = PrisonMines.getInstance(); + + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + try { - pMines.getMineManager().getMine(name).get().reset(); + m.manualReset(); } catch (Exception e) { pMines.getMinesMessages().getLocalizable("mine_reset_fail") .sendTo(sender); - Output.get().logError("Couldn't reset mine " + name, e); + Output.get().logError("Couldn't reset mine " + mineName, e); } pMines.getMinesMessages().getLocalizable("mine_reset").sendTo(sender); @@ -542,53 +1029,675 @@ public void resetCommand(CommandSender sender, @Command(identifier = "mines list", permissions = "mines.list", onlyPlayers = false) - public void listCommand(CommandSender sender) { + public void listCommand(CommandSender sender, + @Arg(name = "sort", def = "alpha", + description = "Sort the list by either alpha or active [alpha, active]. " + + " Most active mines are based upon blocks mined since server restart.") + String sort, + @Arg(name = "page", def = "1", + description = "Page of search results (optional) [1-n, ALL]") String page + ) { ChatDisplay display = new ChatDisplay("Mines"); display.text("&8Click a mine's name to see more information."); - BulletedListComponent.BulletedListBuilder builder = - new BulletedListComponent.BulletedListBuilder(); - + Player player = getPlayer( sender ); + + if ( sort != null && !sort.equalsIgnoreCase( "alpha" ) && + !sort.equalsIgnoreCase( "active" )) { + if ( "ALL".equalsIgnoreCase( sort )) { + // The user did not specify a sort order, but instead this is the page number + // so fix it for them: + sort = "alpha"; + page = "ALL"; + } + else { + try { + int test = Integer.parseInt( sort ); + + // This is actually the page number so default to alpha sort: + sort = "alpha"; + page = Integer.toString( test ); + } + catch ( NumberFormatException e ) { + // Oof... this isn't a page number, so report an error. + sender.sendMessage( "Invalid sort order. Use either alpha, " + + "active, or a page number such as [1-n, ALL]" ); + } + } + } + PrisonMines pMines = PrisonMines.getInstance(); - for (Mine m : pMines.getMines()) { + MineManager mMan = pMines.getMineManager(); + + + // Sort mines by: total blocks mined, name + List mineList = pMines.getMines(); + + // Sort first by name, then blocks mined so final sort order will be: + // Most blocks mined, then alphabetical + mineList.sort( (a, b) -> a.getName().compareToIgnoreCase( b.getName()) ); + + // for now hold off on sorting by total blocks mined. + if ( "active".equalsIgnoreCase( sort )) { + mineList.sort( (a, b) -> Long.compare(b.getTotalBlocksMined(), a.getTotalBlocksMined()) ); + } + + + CommandPagedData cmdPageData = new CommandPagedData( + "/mines list " + sort, pMines.getMines().size(), + 0, page, 7 ); + + BulletedListComponent list = + getMinesLineItemList(pMines.getMines(), player, cmdPageData, mMan.isMineStats()); + + display.addComponent(list); + + + cmdPageData.generatePagedCommandFooter( display ); + + display.send(sender); + } + + + private BulletedListComponent getMinesLineItemList( List mines, Player player, + CommandPagedData cmdPageData, boolean isMineStatsEnabled ) + { + BulletedListComponent.BulletedListBuilder builder = + new BulletedListComponent.BulletedListBuilder(); + + DecimalFormat dFmt = new DecimalFormat("#,##0"); + DecimalFormat fFmt = new DecimalFormat("#,##0.00"); + + int count = 0; + + for (Mine m : mines) { - RowComponent row = new RowComponent(); - - row.addTextComponent( m.getWorldName() + " - " ); + if ( cmdPageData == null || + count++ >= cmdPageData.getPageStart() && count <= cmdPageData.getPageEnd() ) { + + RowComponent row = new RowComponent(); + + //row.addTextComponent( m.getWorldName() + " " ); + + row.addFancy( + new FancyMessage( String.format("&3Mine: &7%s ", m.getName()) ) + .command("/mines info " + m.getName()) + .tooltip("&7Click to view info.")); + + boolean hasCmds = m.getResetCommands().size() > 0; + if ( hasCmds ) { + row.addFancy( + new FancyMessage( String.format(" &cCmds: &7%s ", + Integer.toString( m.getResetCommands().size() )) ) + .command("/mines commands list " + m.getName()) + .tooltip("&7Click to view commands.")); + } + + + + if ( !m.isEnabled() ) { + row.addFancy( + new FancyMessage( "&cDISABLED!! " ) + .command("/mines info " + m.getName()) + .tooltip("&7Click to view possible reason why the mine is " + + "disabled. World may not exist? ")); + } + + row.addFancy( + new FancyMessage("&eTP ").command("/mines tp " + m.getName()) + .tooltip("&7Click to TP to the mine")); + + + if ( m.isUsePagingOnReset() ) { + row.addFancy( + new FancyMessage("&5Pgd ") + .tooltip("&7Paging Used during Mine Reset")); + } + + + row.addTextComponent( " &3Reset: &7" ); + + row.addFancy( + new FancyMessage(dFmt.format(m.getRemainingTimeSec())) + .tooltip( "Estimated time in seconds before the mine resets" ) ); + row.addTextComponent( " sec &3(&b" ); + row.addFancy( + new FancyMessage(dFmt.format(m.getResetTime())) + .tooltip( "Reset time in seconds" ) ); + row.addTextComponent( " sec&3)&b" ); + + if ( player != null && m.getBounds().withinSameWorld( player.getLocation() ) ) { + + row.addTextComponent( " &3Dist: &7"); + row.addFancy( + new FancyMessage( fFmt.format(m.getBounds().getDistance3d(player.getLocation()))). + tooltip("Distance to the Mine") ); + + } + + builder.add(row.getFancy()); + + + + RowComponent row2 = new RowComponent(); +// row2.addTextComponent( " &3Rem: " ); + + // Right justify the total blocks mined, with 1000's separators: + String blocksMined = " " + dFmt.format( m.getTotalBlocksMined() ); + blocksMined = blocksMined.substring( blocksMined.length() - 10); + + row2.addFancy( + new FancyMessage( String.format(" %s &3Rem: ", blocksMined)). + tooltip( "Blocks mined" ) ); + + row2.addFancy( + new FancyMessage(fFmt.format(m.getPercentRemainingBlockCount())). + tooltip( "Percent Blocks Remaining" ) ); + + row2.addTextComponent( "%% &3RCnt: &7" ); + + row2.addFancy( + new FancyMessage(dFmt.format(m.getResetCount())). + tooltip( "Times the mine was reset." ) ); + + + row2.addTextComponent( " &3 Vol: &7" ); + row2.addFancy( + new FancyMessage(dFmt.format(m.getBounds().getTotalBlockCount())). + tooltip( "Volume in Blocks" ) ); + + +// String noteMode = m.getNotificationMode().name() + +// ( m.getNotificationMode() == MineNotificationMode.radius ? +// " " + dFmt.format( m.getNotificationRadius() ) : "" ); +// row.addFancy( +// new FancyMessage(noteMode).tooltip( "Notification Mode" ) ); +// +// row.addTextComponent( "&7 - &b" ); +// +// row.addFancy( +// new FancyMessage(m.getBounds().getDimensions()).tooltip( "Size of Mine" ) ); +// +// row.addTextComponent( "&7 - &b"); + + builder.add(row2.getFancy()); + + + if ( isMineStatsEnabled ) { + RowComponent rowStats = new RowComponent(); + + rowStats.addTextComponent( " -- &7 Stats :: " ); + + rowStats.addTextComponent( m.statsMessage() ); + + builder.add(rowStats.getFancy()); + } + + } + } + +// display.addComponent(builder.build()); - row.addFancy( - new FancyMessage("&7" + m.getName()).command("/mines info " + m.getName()) - .tooltip("&7Click to view info.")); + return builder.build(); + } - row.addTextComponent( "&r - " ); - - row.addFancy( - new FancyMessage("&eTP").command("/mines tp " + m.getName()) - .tooltip("&7Click to TP to the mine")); - - row.addTextComponent( "&r - " ); + /** + *

The following command will change the mine's time between resets. But it will + * not be applied until after the next reset. + *

+ * + * @param sender + * @param mineName + * @param time + */ + @Command(identifier = "mines set skipReset", permissions = "mines.skipreset", + description = "Set a mine to skip the reset if not enough blocks have been mined.") + public void skipResetCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "enabled", description = "Enable the skip reset processing: 'Enabled' or 'Disable'", + def = "disabled") String enabled, + @Arg(name = "percent", description = "Percent threshold before resetting.", def = "80" ) String percent, + @Arg(name = "bypassLimit", description = "Limit number of skips before bypassing and performing a reset", + def = "50") String bypassLimit + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + if ( enabled == null || !"enabled".equalsIgnoreCase( enabled ) && !"disabled".equalsIgnoreCase( enabled )) { + Output.get().sendWarn( sender,"&7Invalid &benabled&7 value. Must be either &benabled&7 or " + + "&bdisabled&7. Was &b%s&7.", (enabled == null ? "&c-blank-" : enabled) ); + return; + } + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + boolean skipEnabled = "enabled".equalsIgnoreCase( enabled ); + double skipPercent = 80.0d; + int skipBypassLimit = 50; + + try { + skipPercent = Double.parseDouble( percent ); + if ( skipPercent < 0.0d ) { + skipPercent = 0.0d; + } else if ( skipPercent > 100.0d ) { + skipPercent = 100.0d; + } + } + catch ( NumberFormatException e1 ) { + Output.get().sendWarn( sender,"&7Invalid percentage. Not a number. " + + "Was &b%s&7.", (enabled == null ? "&c-blank-" : enabled) ); + return; + } + + try { + skipBypassLimit = Integer.parseInt( bypassLimit ); + if ( skipBypassLimit < 1 ) { + skipBypassLimit = 1; + } + } + catch ( NumberFormatException e1 ) { + Output.get().sendWarn( sender,"&7Invalid bypass limit. Not number. " + + "Was &b%s&7.", (bypassLimit == null ? "-blank-" : bypassLimit) ); + } + + m.setSkipResetEnabled( skipEnabled ); + m.setSkipResetPercent( skipPercent ); + m.setSkipResetBypassLimit( skipBypassLimit ); + + pMines.getMineManager().saveMine( m ); + + // User's message: + String message = String.format( "&7mines skipreset for &b%s&7: &b%s&7 " + + "threshold: &b%.2f&7 percent bypassLimit: &b%d", + m.getName(), (skipEnabled ? "enabled" : "disabled"), + skipPercent, skipBypassLimit ); + Output.get().sendInfo( sender, message ); + + // Server Log message: + Player player = getPlayer( sender ); + Output.get().logInfo( "%s :: Changed by: %s", message, + (player == null ? "console" : player.getDisplayName()) ); + } + } - row.addTextComponent( "&3Size: &7%d&8x&7%d&8x&7%d", Math.round(m.getBounds().getWidth()), - Math.round(m.getBounds().getHeight()), Math.round(m.getBounds().getLength()) ); - row.addTextComponent( "&r - &3Volume: &7%d &3blocks", m.getBounds().getTotalBlockCount() ); + + /** + *

The following command will change the mine's time between resets. But it will + * not be applied until after the next reset. + *

+ * + * @param sender + * @param mineName + * @param time + */ + @Command(identifier = "mines set resetTime", permissions = "mines.resettime", + description = "Set a mine's time to reset.") + public void resetTimeCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "time", description = "Time in seconds for the mine to auto reset." ) String time + + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + try { + int resetTime = MineData.MINE_RESET__TIME_SEC__DEFAULT; + + if ( time != null && time.trim().length() > 0 ) { + resetTime = Integer.parseInt( time ); + } + + if ( resetTime < MineData.MINE_RESET__TIME_SEC__MINIMUM ) { + Output.get().sendWarn( sender, + "&7Invalid resetTime value for &b%s&7. Must be an integer value of &b%d&7 or greater. [&b%d&7]", + mineName, MineData.MINE_RESET__TIME_SEC__MINIMUM, resetTime ); + } else { + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + m.setResetTime( resetTime ); + + pMines.getMineManager().saveMine( m ); + + // User's message: + Output.get().sendInfo( sender, "&7mines set resettime: &b%s &7resetTime set to &b%d", m.getName(), resetTime ); + + // Server Log message: + Player player = getPlayer( sender ); + Output.get().logInfo( "&bmines set resettime&7: &b%s &7set &b%s &7resetTime to &b%d", + (player == null ? "console" : player.getDisplayName()), m.getName(), resetTime ); + } + } + catch ( NumberFormatException e ) { + Output.get().sendWarn( sender, + "&7Invalid resetTime value for &b%s&7. Must be an integer value of &b%d &7or greater. [&b%s&7]", + mineName, MineData.MINE_RESET__TIME_SEC__MINIMUM, time ); + } + } + } + + + + /** + *

When a mine reaches zero blocks, a manual reset will be issued to run. By default + * it will have a 0 second delay before running, but this command controls how long of + * a delay to use. + *

+ * + *

Although the delay is in seconds, it should be known that the value will be multiplied + * by 20 to convert it to ticks. So any value less than 0.05 will be treated as zero and + * effectively will be in 0.05 increments. Give or take a tick should not matter, but + * beware if a player, or owner, complains that 0.17 is the same as 0.15. + *

+ * + * @param sender + * @param mineName + * @param time + */ + @Command(identifier = "mines set zeroBlockResetDelay", permissions = "mines.zeroblockresetdelay", + description = "Set a mine's delay before reset when it reaches zero blocks.") + public void zeroBlockResetDelayCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "time/DISABLE", description = "Delay in seconds before resetting when the mine reaches " + + "zero blocks, or DISABLE." ) String time + + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + try { + double resetTime = + time != null && "disable".equalsIgnoreCase( time ) ? -1.0d : + 0.0d; + + if ( resetTime != -1.0d && time != null && time.trim().length() > 0 ) { + resetTime = Double.parseDouble( time ); + + // Only displaying two decimal positions, since 0.01 is 10 ms. + // Anything less than 0.01 is set to ZERO so it does not mess with anything unseen. + // Also any value less than 0.05 is basically zero since this value has to be + // converted to ticks. + if ( resetTime < 0.01d ) { + resetTime = 0.0d; + } + } + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + m.setZeroBlockResetDelaySec( resetTime ); + + pMines.getMineManager().saveMine( m ); + + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + // User's message: + if ( m.isZeroBlockResetDisabled() ) { + Output.get().sendInfo( sender, "&7Mine &b%s Zero Block Reset Delay: &cDISABLED", + m.getName(), dFmt.format( resetTime ) ); + + } else { + Output.get().sendInfo( sender, "&7Mine &b%s Zero Block Reset Delay: &b%s &7sec", + m.getName(), dFmt.format( resetTime ) ); + + } + + // Server Log message: + Player player = getPlayer( sender ); + Output.get().logInfo( "&7Mine &b%s Zero Block Reset Delay: &b%s &7set it to &b%s &7sec", + (player == null ? "console" : player.getDisplayName()), + m.getName(), dFmt.format( resetTime ) ); + } + catch ( NumberFormatException e ) { + Output.get().sendWarn( sender, + "&7Invalid zeroBlockResetDelay value for &b%s&7. Must be an double value of &b0.00 &7or " + + "greater. [&b%s&7]", + mineName, time ); + } + } + } + + + + /** + *

The following command will change the mine's time between resets. But it will + * not be applied until after the next reset. + *

+ * + * @param sender + * @param mineName + * @param time + */ + @Command(identifier = "mines set resetThreshold", permissions = "mines.resetThreshold", + description = "Triggers a mine reset once this threshold is crossed and the remaining " + + "block percentage is less than or equal to this value") + public void resetThresholdPercentCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "percent", description = "Threshold percent to trigger a reset.(0 is disabled)", + def = "0" ) String percent + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); - builder.add(row.getFancy()); - } - display.addComponent(builder.build()); - display.send(sender); + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + double thresholdPercent = 0.0d; + + try { + thresholdPercent = Double.parseDouble( percent ); + if ( thresholdPercent < 0.0d ) { + thresholdPercent = 0.0d; + } else if ( thresholdPercent > 100.0d ) { + thresholdPercent = 100.0d; + } + } + catch ( NumberFormatException e1 ) { + Output.get().sendWarn( sender,"&7Invalid percentage. Not a number. " + + "Was &b%s&7.", (percent == null ? "&c-blank-" : percent) ); + return; + } + + + if ( thresholdPercent == m.getResetThresholdPercent() ) { + String msg = "The Reset Threshold Percent was not changed."; + Output.get().sendInfo( sender, msg ); + return; + } + + m.setResetThresholdPercent( thresholdPercent ); + + pMines.getMineManager().saveMine( m ); + + double blocks = m.getBounds().getTotalBlockCount() * + m.getResetThresholdPercent() / 100.0d; + + DecimalFormat dFmt = new DecimalFormat("#,##0"); + DecimalFormat fFmt = new DecimalFormat("#,##0.00"); + + // User's message: + String message = String.format( "&7The Reset Threshold Percent for mine &b%s&7 was set to &b%s&7, " + + "which is about &b%s &7blocks.", + m.getName(), + fFmt.format( m.getResetThresholdPercent() ), + dFmt.format( blocks ) ); + Output.get().sendInfo( sender, message ); + + // Server Log message: + Player player = getPlayer( sender ); + Output.get().logInfo( "%s :: Changed by: %s", message, + (player == null ? "console" : player.getDisplayName()) ); + } + } + + + + @Command(identifier = "mines set notification", permissions = "mines.notification", + description = "Set a mine's notification mode.") + public void setNotificationCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "mode", def="displayOptions", description = "The notification mode to use: disabled, within, radius") + String mode, + @Arg(name = "radius", def="0", description = "The distance from the center of the mine to notify players of a reset." ) + String radius + + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + MineNotificationMode noteMode = MineNotificationMode.fromString( mode, MineNotificationMode.displayOptions ); + + if ( noteMode == MineNotificationMode.displayOptions ) { + sender.sendMessage( "&7Select a Mode: &bdisabled&7, &bwithin &7the mine, &bradius " + + "&7from center of mine." ); + } else { + long noteRadius = 0L; + if ( noteMode == MineNotificationMode.radius ) { + if ( radius == null || radius.trim().length() == 0 ) { + noteRadius = MineData.MINE_RESET__BROADCAST_RADIUS_BLOCKS; + } else { + try { + noteRadius = Long.parseLong( radius ); + + if ( noteRadius < 1 ) { + noteRadius = MineData.MINE_RESET__BROADCAST_RADIUS_BLOCKS; + DecimalFormat dFmt = new DecimalFormat("#,##0"); + Output.get().sendWarn( sender, "&7Invalid radius value for &b%s&7. " + + "Must be an positive non-zero integer. Using the default value: &b%s &7[&b%s&7]", + mineName, dFmt.format(MineData.MINE_RESET__BROADCAST_RADIUS_BLOCKS), radius ); + } + } + catch ( NumberFormatException e ) { + e.printStackTrace(); + Output.get().sendWarn( sender, "&7Invalid notification radius for &b%s&7. " + + "Must be an positive non-zero integer. [&b%s&7]", + mineName, radius ); + } + } + } + + if ( m.getNotificationMode() != noteMode || m.getNotificationRadius() != noteRadius ) { + m.setNotificationMode( noteMode ); + m.setNotificationRadius( noteRadius ); + + pMines.getMineManager().saveMine( m ); + + DecimalFormat dFmt = new DecimalFormat("#,##0"); + // message: notification mode changed + Output.get().sendInfo( sender, "&7Notification mode was changed for &b%s&7: &b%s %s", + mineName, m.getNotificationMode().name(), + (m.getNotificationMode() == MineNotificationMode.radius ? + dFmt.format( m.getNotificationRadius() ) : "" )); + + } else { + // message: notification mode did not change + Output.get().sendInfo( sender, "&7Notification mode was not changed for mine &b%s&7.", mineName ); + } + } + } } - @Command(identifier = "mines set area", permissions = "mines.set", description = "Set the area of a mine to your current selection.") + @Command(identifier = "mines set notificationPerm", permissions = "mines.notification", + description = "Enable or disable a mine's notification permission. If enabled, then players " + + "must have the mine's permission to get messages for reset. This filter " + + "can be combined with the other notification settings.") + public void setNotificationPermissionCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "action", def="enable", description = "Enable or disable the permission filtering: [enable, disable]") + String action + + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + if ( !action.equalsIgnoreCase( "enable" ) && !action.equalsIgnoreCase( "disable" )) { + sender.sendMessage( "&7Invalid value for action: [enable, disable]" ); + return; + } + + if ( action.equalsIgnoreCase( "enable" ) && !m.isUseNotificationPermission() ) { + sender.sendMessage( "&7Notification Permission filter has been enabled." ); + m.setUseNotificationPermission( true ); + pMines.getMineManager().saveMine( m ); + } + else if ( action.equalsIgnoreCase( "disable" ) && m.isUseNotificationPermission() ) { + sender.sendMessage( "&7Notification Permission filter has been disabled." ); + m.setUseNotificationPermission( false ); + pMines.getMineManager().saveMine( m ); + } + else { + + sender.sendMessage( "&7Notification Permission filter was not changed. Canceling." ); + } + + + } + } + + + + @Command(identifier = "mines set area", permissions = "mines.set", + description = "Set the area of a mine to your current selection.") public void redefineCommand(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to edit.") String name) { + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName) { - if (!performCheckMineExists(sender, name)) { + if (!performCheckMineExists(sender, mineName)) { return; } - Selection selection = Prison.get().getSelectionManager().getSelection((Player) sender); PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + Selection selection = Prison.get().getSelectionManager().getSelection((Player) sender); + if (!selection.isComplete()) { pMines.getMinesMessages().getLocalizable("select_bounds") .sendTo(sender); @@ -604,32 +1713,122 @@ public void redefineCommand(CommandSender sender, // TODO check to see if they are the same boundaries, if not, don't change... - setLastMineReferenced(name); + setLastMineReferenced(mineName); - Mine m = pMines.getMineManager().getMine(name).get(); m.setBounds(selection.asBounds()); pMines.getMineManager().saveMine( m ); pMines.getMinesMessages().getLocalizable("mine_redefined") .sendTo(sender); - pMines.getMineManager().clearCache(); + + // Delete the selection: + Prison.get().getSelectionManager().clearSelection((Player) sender); + //pMines.getMineManager().clearCache(); + } + + + @Command(identifier = "mines set resetpaging", permissions = "mines.resetpaging", + description = "Enable paging during a mine reset.") + public void setMineResetPagingCommand(CommandSender sender, + @Arg(name = "mineName", description = "The name of the mine to edit.") String mineName, + @Arg(name = "paging", def="disabled", + description = "Enable or disable paging [disable, enable]") + String paging + ) { + + if (performCheckMineExists(sender, mineName)) { + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + if ( paging == null || !"disable".equalsIgnoreCase( paging ) && !"enable".equalsIgnoreCase( paging ) ) { + sender.sendMessage( "&cInvalid paging option&7. Use &adisable&7 or &aenable&7" ); + return; + } + + if ( "disable".equalsIgnoreCase( paging ) && m.isUsePagingOnReset() ) { + m.setUsePagingOnReset( false ); + pMines.getMineManager().saveMine( m ); + sender.sendMessage( String.format( "&7Mine Reset Paging has been disabled for mine %s.", m.getName()) ); + } + else if ( "enable".equalsIgnoreCase( paging ) && !m.isUsePagingOnReset() ) { + m.setUsePagingOnReset( true ); + pMines.getMineManager().saveMine( m ); + sender.sendMessage( String.format( "&7Mine Reset Paging has been enabled for mine %s.", m.getName()) ); + } + + } } - @Command(identifier = "mines tp", permissions = "mines.tp", description = "TP to the mine.") + @Command(identifier = "mines tp", description = "TP to the mine.", + altPermissions = {"mines.tp", "mines.tp.[mineName]"}) public void mineTp(CommandSender sender, - @Arg(name = "mineName", description = "The name of the mine to teleport to.") String name) { + @Arg(name = "mineName", description = "The name of the mine to teleport to.") String mineName, + + @Arg(name = "player", def = "", description = "Player name to TP - " + + "Only console or rank command can include this parameter") String playerName + + ) { - if (!performCheckMineExists(sender, name)) { - return; + Player player = getPlayer( sender ); + + Player playerAlt = getOnlinePlayer( playerName ); + + if (player == null || !player.isOnline()) { + + if ( playerName != null && playerName.trim().length() > 0 && playerAlt == null) { + sender.sendMessage( "&3Specified player is not in the game so they cannot be teleported." ); + } + + // If the sender is console or its being ran as a rank command, and the playerName is + // a valid online player, then use that player as the active player issuing the command: + if ( playerAlt != null && playerAlt.isOnline() ) { + player = playerAlt; + } + else { + sender.sendMessage( "&3You must be a player in the game to run this command." ); + return; + } + + } + else if ( playerAlt != null ) { + sender.sendMessage( "&3You cannot teleport other players to a mine. Ignoring parameter." ); } - setLastMineReferenced(name); + // Load mine information first to confirm the mine exists and the parameter is correct: + if (!performCheckMineExists(sender, mineName)) { + return; + } + setLastMineReferenced(mineName); + PrisonMines pMines = PrisonMines.getInstance(); - Mine m = pMines.getMineManager().getMine(name).get(); + Mine m = pMines.getMine(mineName); + + String minePermission = "mines.tp." + m.getName().toLowerCase(); + if ( !sender.isOp() && + !sender.hasPermission("mines.tp") && + !sender.hasPermission( minePermission ) ) { + Output.get() + .sendError(sender, "You need the permission '%s' or '%s' to tp to this mine.", + "mines.tp", minePermission ); + return; + } + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + if ( sender instanceof Player ) { m.teleportPlayerOut( (Player) sender ); } else { @@ -639,13 +1838,269 @@ public void mineTp(CommandSender sender, } + + @Command(identifier = "mines stats", permissions = "mines.stats", description = "Toggle stats on all mines.") + public void mineStats(CommandSender sender) { + + PrisonMines pMines = PrisonMines.getInstance(); + MineManager mMan = pMines.getMineManager(); + + // toggle the stats: + mMan.setMineStats( !mMan.isMineStats() ); + + if ( mMan.isMineStats() ) { + sender.sendMessage( + "&3Mine Stats are now enabled. Use &7/mines list&3 to view stats on last mine reset. "); + } else { + sender.sendMessage( "&3Mine stats are now disabled." ); + } + } + + + + @Command(identifier = "mines whereami", permissions = "mines.whereami", + description = "Identifies what mines you are in, or are the closest to." ) + public void mineWhereAmI(CommandSender sender) { + + Player player = getPlayer( sender ); + + if (player == null || !player.isOnline()) { + sender.sendMessage( "&3You must be a player in the game to run this command." ); + return; + } + + player.sendMessage( "&3Your coordinates are: &7" + player.getLocation().toBlockCoordinates() ); + + PrisonMines pMines = PrisonMines.getInstance(); + + List inMine = new ArrayList<>(); + TreeMap nearMine = new TreeMap<>(); + for ( Mine mine : pMines.getMineManager().getMines() ) { + if ( mine.getBounds().within( player.getLocation() ) ) { + inMine.add( mine ); + } + + // This is checking for within a certain distance from any mine, so we just need to use + // some arbitrary distance as a max radius. We do not want to use the individual values + // that have been set for each mine. + else if ( mine.getBounds().within( player.getLocation(), MineData.MINE_RESET__BROADCAST_RADIUS_BLOCKS) ) { + Double distance = mine.getBounds().getDistance3d( player.getLocation() ); +// Double distance = new Bounds( mine.getBounds().getCenter(), player.getLocation()).getDistance(); + nearMine.put( distance.intValue(), mine ); + } + } + + if ( inMine.size() > 0 ) { + // You are in the mines: + for ( Mine m : inMine ) { + sender.sendMessage( "&3You are in mine &7" + m.getName() ); + } + } + if ( nearMine.size() > 0 ) { + // You are near the mines: + int cnt = 0; + Set distances = nearMine.keySet(); + for ( Integer dist : distances ) { + Mine m = nearMine.get( dist ); + sender.sendMessage( "&3You are &7" + dist + " &7blocks away from the center of mine &3" + m.getName() ); + if ( ++cnt >= 5 ) { + break; + } + } + + } else { + // you are not near any mines: + sender.sendMessage( "&3Sorry, you are not within " + MineData.MINE_RESET__BROADCAST_RADIUS_BLOCKS + + " blocks from any mine." ); + } + + } + + private Player getPlayer( CommandSender sender ) { + Optional player = Prison.get().getPlatform().getPlayer( sender.getName() ); + return player.isPresent() ? player.get() : null; + } + + private Player getOnlinePlayer( String playerName ) { + Player player = null; + if ( playerName != null ) { + Optional oPlayer = Prison.get().getPlatform().getPlayer( playerName ); + player = oPlayer.isPresent() ? oPlayer.get() : null; + } + return player; + } + + + @Command(identifier = "mines wand", permissions = "mines.wand", description = "Receive a wand to select a mine area.") public void wandCommand(Player sender) { + + Player player = getPlayer( sender ); + + if (player == null || !player.isOnline()) { + sender.sendMessage( "&3You must be a player in the game to run this command." ); + return; + } + Prison.get().getSelectionManager().bestowSelectionTool(sender); sender.sendMessage( "&3Here you go! &7Left click to select the first corner, and right click to select the other."); } + + + @Command(identifier = "mines command list", description = "Lists the commands for a mine.", + onlyPlayers = false, permissions = "mines.command") + public void commandList(CommandSender sender, + @Arg(name = "mineName") String mineName) { + +// if ( 1 < 2 ) { +// sender.sendMessage( "&cThis command is disabled&7. It will be enabled in the near future." ); +// return; +// } + + if (!performCheckMineExists(sender, mineName)) { + return; + } + + + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + + if (m.getResetCommands() == null || m.getResetCommands().size() == 0) { + Output.get().sendInfo(sender, "The mine '%s' contains no commands.", m.getName()); + return; + } + + + ChatDisplay display = new ChatDisplay("ResetCommand for " + m.getName()); + display.text("&8Click a command to remove it."); + BulletedListComponent.BulletedListBuilder builder = + new BulletedListComponent.BulletedListBuilder(); + + for (String command : m.getResetCommands()) { + FancyMessage msg = new FancyMessage( "&a'&7" + command + "&a'" ) + .command("/mines command remove " + mineName + " " + command) + .tooltip("Click to remove."); + builder.add(msg); + } + + display.addComponent(builder.build()); + display.addComponent(new FancyMessageComponent( + new FancyMessage("&7[&a+&7] Add").suggest("/mines command add " + mineName + " /") + .tooltip("&7Add a new command."))); + display.send(sender); + } + + + @Command(identifier = "mines command remove", description = "Removes a command from a mine.", + onlyPlayers = false, permissions = "mines.command") + public void commandRemove(CommandSender sender, + @Arg(name = "mineName") String mineName, + @Arg(name = "command", + description = "Exact command to remove including the 'before: ' and 'after: ' states.") + @Wildcard String command) { + +// if ( 1 < 2 ) { +// sender.sendMessage( "&cThis command is disabled&7. It will be enabled in the near future." ); +// return; +// } + + if (command.startsWith("/")) { + command = command.replaceFirst("/", ""); + } + + if (!performCheckMineExists(sender, mineName)) { + return; + } + + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + if (m.getResetCommands() == null || m.getResetCommands().size() == 0) { + Output.get().sendInfo(sender, "The mine '%s' contains no commands.", m.getName()); + return; + } + + if ( m.getResetCommands().remove(command) ) { + + pMines.getMineManager().saveMine( m ); + + Output.get().sendInfo(sender, "Removed command '%s' from the mine '%s'.", + command, m.getName()); + } else { + Output.get().sendWarn(sender, + String.format("The mine %s doesn't contain that command. Nothing was changed.", + m.getName())); + } + } + + @Command(identifier = "mines command add", description = "Adds a command to a mine with NO placeholders.", + onlyPlayers = false, permissions = "mines.command") + public void commandAdd(CommandSender sender, + @Arg(name = "mineName") String mineName, + @Arg(name = "state", def = "before", description = "State can be either before or after.") String state, + @Arg(name = "command") @Wildcard String command) { + +// if ( 1 < 2 ) { +// sender.sendMessage( "&cThis command is disabled&7. It will be enabled in the near future." ); +// return; +// } + + if (command.startsWith("/")) { + command = command.replaceFirst("/", ""); + } + + if (!performCheckMineExists(sender, mineName)) { + return; + } + + if ( state == null || !state.equalsIgnoreCase( "before" ) && !state.equalsIgnoreCase( "after" )) { + sender.sendMessage( + String.format("&7Please provide a valid state: b&before&7 or &bafter&7. Was state=[&b%s&7]", + state )); + return; + } + + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + + if ( !m.isEnabled() ) { + sender.sendMessage( "&cMine is disabled&7. Use &a/mines info &7for possible cause." ); + return; + } + + if ( command == null || command.trim().length() == 0 ) { + sender.sendMessage( + String.format( "&7Please provide a valid command: command=[%s]", command) ); + return; + + } + + String newComand = state + ": " + command; + m.getResetCommands().add(newComand); + + pMines.getMineManager().saveMine( m ); + + Output.get().sendInfo(sender, "&7Added command '&b%s&7' to the mine '&b%s&7'.", + newComand, m.getName()); + + } + public Long getConfirmTimestamp() { return confirmTimestamp; @@ -666,7 +2121,8 @@ public void setConfirmTimestamp( Long confirmTimestamp ) */ public String getLastMineReferenced() { - if ( System.currentTimeMillis() - getLastMineReferencedTimestamp() > (30 * 60 * 1000)) + if ( getLastMineReferencedTimestamp() != null && + System.currentTimeMillis() - getLastMineReferencedTimestamp() > (30 * 60 * 1000)) { setLastMineReferenced( null ); } @@ -687,4 +2143,5 @@ public void lastMineReferenced( Long lastMineReferencedTimestamp ) this.lastMineReferencedTimestamp = lastMineReferencedTimestamp; } + } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/Mine.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/Mine.java index 0d3280073..cace801bf 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/Mine.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/Mine.java @@ -19,19 +19,20 @@ package tech.mcprison.prison.mines.data; import java.util.ArrayList; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; +import java.util.Set; import tech.mcprison.prison.Prison; -import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.mines.MineException; import tech.mcprison.prison.mines.PrisonMines; -import tech.mcprison.prison.mines.events.MineResetEvent; import tech.mcprison.prison.mines.managers.MineManager; import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.selection.Selection; +import tech.mcprison.prison.sorting.PrisonSortable; import tech.mcprison.prison.store.Document; import tech.mcprison.prison.util.BlockType; import tech.mcprison.prison.util.Bounds; @@ -40,47 +41,166 @@ /** * @author Dylan M. Perks */ -public class Mine { - - private Bounds bounds; - - private Location spawn; - private String worldName, name; - private boolean hasSpawn = false; - - private List blocks; - +public class Mine + extends MineScheduler + implements PrisonSortable { /** * Creates a new, empty mine instance */ public Mine() { - blocks = new ArrayList<>(); + super(); + + // Kick off the initialize: + initialize(); } - private Location getLocation(Document doc, World world, String x, String y, String z) { - return new Location(world, (double) doc.get(x), (double) doc.get(y), (double) doc.get(z)); - } - private Location getLocation(Document doc, World world, String x, String y, String z, String pitch, String yaw) { - Location loc = getLocation(doc, world, x, y, z); - loc.setPitch( ((Double) doc.get(pitch)).floatValue() ); - loc.setYaw( ((Double) doc.get(yaw)).floatValue() ); - return loc; + /** + *

This is called when a mine is first created. + *

+ * + * @param name + * @param selection + */ + public Mine(String name, Selection selection) { + super(); + + setName(name); + setBounds(selection.asBounds()); + + setWorldName( getBounds().getMin().getWorld().getName()); + + setEnabled( true ); + + // Kick off the initialize: + initialize(); } + /** - * Loads a mine from a document. + *

Loads a mine from a document. + *

+ * + *

Note that the location where the loadFromDocument() occurs in the whole + * "create the objects" is in the "middle". All classes that are extended + * from are instantiated first due to the super() function call. So when Mine + * tries to be instantiated, it first drills all the way down to MineData and + * then runs all the initialization code within MineData and then works back + * through all of the classes, instantiating everything, one layer at a time. + *

+ * + *

Then when it gets back up to this class, Mine, all parents have been fully + * instantiated so all collections will have been assigned non-null values + * as an example. Then this class loads the data from the document object. + * This is important, since all parents have been initialized, now the document + * loader is making it a "mine". + *

+ * + *

Then at that point, after the mine data is loaded, it once again drills all the + * way down to the MineData ancestor class, using the initialize() functions, where + * it then starts to initialize all classes from MineData, back up to Mine. + * What this enables and allows, is when a class is initialized, it will have access + * to the fully loaded mine data. This is a perfect example of being able to start + * submitting the mine reset jobs since all data has been loaded, and all lower + * functions have been ran. + *

+ * + *

So the over all design of the Mine objects is that all ancestors instantiate + * first, from MineData to Mine. Then the mine is loaded from the file system. + * Then all ancestors are initialized from MineData to Mine. This gives a high + * degree of control over when actions can be ran over a mine, and have confidence the + * data and conditions will be there. + *

* * @param document The document to load from. * @throws MineException If the mine couldn't be loaded from the document. */ public Mine(Document document) throws MineException { + super(); - Optional worldOptional = Prison.get().getPlatform().getWorld((String) document.get("world")); + loadFromDocument( document ); + + // Kick off the initialize: + // This is critically vital to ensure the workflow is generated with the contents + // from the document and not the defaults as set by the super(). + initialize(); + } + + + /** + *

This initialize function gets called after the classes are + * instantiated, and is initiated from Mine class and propagates + * to the MineData class. Good for kicking off the scheduler. + *

+ */ + @Override + protected void initialize() { + super.initialize(); + + } + + /** + *

The loading of a mine checks to ensure if the world exists. If not, then + * traditionally, it would not load the mine. The problem with this model is + * the world may not exist yet, if running Multiverse-core (or another similar + * plugin) and as such, may falsely cause mine failures. This is the situation if + * the mine exists within a world that must be loaded by Multiverse-core. If it was + * a standard world, then it would be fine. + *

+ * + *

Soft dependencies do not provide a solution. One bad solution for this + * situation, is to manually add a hard dependency to Multiverse-core. This + * should not be used. + *

+ * + *

As a better solution to this problem, mines will be loaded as normal, but + * if the world does not exist, then their initialization, or enablement, will be + * delayed until the world is available. + *

+ * + * @param document + * @throws MineException + */ + @SuppressWarnings( "unchecked" ) + private void loadFromDocument( Document document ) + throws MineException { + + boolean dirty = false; + boolean inconsistancy = false; + + String worldName = (String) document.get("world"); + setWorldName( worldName ); + setName((String) document.get("name")); // Mine name: + + World world = null; + + if ( worldName == null ) { + Output.get().logInfo( "Mines.loadFromDocument: Failure: World does not exist in Mine file. mine= %s " + + "Contact support on how to fix.", + getName()); + } + + Optional worldOptional = Prison.get().getPlatform().getWorld(worldName); if (!worldOptional.isPresent()) { - throw new MineException("world doesn't exist"); + MineManager mineMan = PrisonMines.getInstance().getMineManager(); + + // Store this mine and the world in MineManager's unavailableWorld for later + // processing and hooking up to the world object. Print an error message upon + // the first mine's world not existing. + mineMan.addUnavailableWorld( worldName, this ); + + setEnabled( false ); + } + else { + world = worldOptional.get(); + setEnabled( true ); } - World world = worldOptional.get(); + +// World world = worldOptional.get(); + + + Double resetTimeDouble = (Double) document.get("resetTime"); + setResetTime( resetTimeDouble != null ? resetTimeDouble.intValue() : PrisonMines.getInstance().getConfig().resetTime ); setBounds( new Bounds( getLocation(document, world, "minX", "minY", "minZ"), @@ -91,358 +211,236 @@ public Mine(Document document) throws MineException { setSpawn(getLocation(document, world, "spawnX", "spawnY", "spawnZ", "spawnPitch", "spawnYaw")); } - setWorldName(world.getName()); - setName((String) document.get("name")); + + setNotificationMode( MineNotificationMode.fromString( (String) document.get("notificationMode")) ); + Double noteRadius = (Double) document.get("notificationRadius"); + setNotificationRadius( noteRadius == null ? MINE_RESET__BROADCAST_RADIUS_BLOCKS : noteRadius.longValue() ); - this.blocks = new ArrayList<>(); + Double zeroBlockResetDelaySec = (Double) document.get("zeroBlockResetDelaySec"); + setZeroBlockResetDelaySec( zeroBlockResetDelaySec == null ? 0.0d : zeroBlockResetDelaySec.doubleValue() ); + + Boolean skipResetEnabled = (Boolean) document.get( "skipResetEnabled" ); + setSkipResetEnabled( skipResetEnabled == null ? false : skipResetEnabled.booleanValue() ); + Double skipResetPercent = (Double) document.get( "skipResetPercent" ); + setSkipResetPercent( skipResetPercent == null ? 80.0D : skipResetPercent.doubleValue() ); + Double skipResetBypassLimit = (Double) document.get( "skipResetBypassLimit" ); + setSkipResetBypassLimit( skipResetBypassLimit == null ? 50 : skipResetBypassLimit.intValue() ); + + Double resetThresholdPercent = (Double) document.get( "resetThresholdPercent" ); + setResetThresholdPercent( resetThresholdPercent == null ? 0 : resetThresholdPercent.doubleValue() ); + + // When loading, skipResetBypassCount must be set to zero: + setSkipResetBypassCount( 0 ); - @SuppressWarnings( "unchecked" ) - List docBlocks = (List) document.get("blocks"); - for (String docBlock : docBlocks) { + // This is a validation set to ensure only one block type is loaded file system. + // Must keep the first one loaded. + Set validateBlockNames = new HashSet<>(); + getBlocks().clear(); + + List docBlocks = (List) document.get("blocks"); + for (String docBlock : docBlocks) { String[] split = docBlock.split("-"); - String id = split[0]; + String blockTypeName = split[0]; double chance = Double.parseDouble(split[1]); - Block block = new Block(BlockType.getBlock(id), chance); - blocks.add(block); + if ( blockTypeName != null && !validateBlockNames.contains( blockTypeName )) { + // Use the BlockType.name() load the block type: + BlockType blockType = BlockType.getBlock(blockTypeName); + if ( blockType != null ) { + Block block = new Block(blockType, chance); + getBlocks().add(block); + } + else { + String message = String.format( "Failure in loading block type from %s mine's " + + "save file. Block type %s has no mapping.", getName(), + blockTypeName ); + Output.get().logError( message ); + } + + validateBlockNames.add( blockTypeName ); + } + else if (validateBlockNames.contains( blockTypeName ) ) { + // Detected and fixed a duplication so mark as dirty so fixed block list is saved: + dirty = true; + inconsistancy = true; + } } - } + + + // Reset validation checks: + validateBlockNames.clear(); + getPrisonBlocks().clear(); + + List docPrisonBlocks = (List) document.get("prisonBlocks"); + if ( docPrisonBlocks != null ) { + + for (String docBlock : docPrisonBlocks) { + String[] split = docBlock.split("-"); + String blockTypeName = split[0]; + double chance = Double.parseDouble(split[1]); + + if ( blockTypeName != null ) { + // The new way to get the PrisonBlocks: + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( blockTypeName ); + + if ( prisonBlock != null && !validateBlockNames.contains( blockTypeName )) { + prisonBlock.setChance( chance ); + if ( prisonBlock.isLegacyBlock() ) { + dirty = true; + } + getPrisonBlocks().add( prisonBlock ); + + validateBlockNames.add( blockTypeName ); + } + else if (validateBlockNames.contains( blockTypeName ) ) { + // Detected and fixed a duplication so mark as dirty so fixed block list is saved: + dirty = true; + inconsistancy = true; + } + + } + } + } + + + if ( Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ) && + getPrisonBlocks().size() == 0 && getBlocks().size() > 0 ) { + // Need to perform the initial conversion: + + for ( Block block : getBlocks() ) { + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( block.getType().name() ); + if ( prisonBlock != null ) { + + prisonBlock.setChance( block.getChance() ); + getPrisonBlocks().add( prisonBlock ); + + dirty = true; + } + + } + Output.get().logInfo( "Notice: Mine: " + getName() + ": Existing prison block model has " + + "been converted to the new block model and will be saved." ); + } + + + List commands = (List) document.get("commands"); + setResetCommands( commands == null ? new ArrayList<>() : commands ); + + + Boolean usePagingOnReset = (Boolean) document.get( "usePagingOnReset" ); + setUsePagingOnReset( usePagingOnReset == null ? false : usePagingOnReset.booleanValue() ); + + if ( dirty ) { + + // Resave the mine data since an update to the mine format was detected and + // needs to be saved. Otherwise the bad data will always need to be converted + // every time the mine is loaded which may lead to other issues. + + // This is enabled since the original is not modified. + + PrisonMines.getInstance().getMineManager().saveMine( this ); + + if ( inconsistancy ) { + + Output.get().logInfo( "Notice: Mine: " + getName() + ": During the loading of this mine an " + + "inconsistancy was detected and was fixed then saved." ); + } + else { + Output.get().logInfo( "Notice: Mine: " + getName() + ": Updated mine data was successfully saved." ); + + } + } + } public Document toDocument() { Document ret = new Document(); - ret.put("world", worldName); - ret.put("name", name); + ret.put("world", getWorldName()); + ret.put("name", getName()); ret.put("minX", getBounds().getMin().getX()); ret.put("minY", getBounds().getMin().getY()); ret.put("minZ", getBounds().getMin().getZ()); ret.put("maxX", getBounds().getMax().getX()); ret.put("maxY", getBounds().getMax().getY()); ret.put("maxZ", getBounds().getMax().getZ()); - ret.put("hasSpawn", hasSpawn); + ret.put("hasSpawn", isHasSpawn()); + + ret.put("resetTime", getResetTime() ); + ret.put("notificationMode", getNotificationMode().name() ); + ret.put("notificationRadius", Long.valueOf( getNotificationRadius() )); - if (hasSpawn) { - ret.put("spawnX", spawn.getX()); - ret.put("spawnY", spawn.getY()); - ret.put("spawnZ", spawn.getZ()); - ret.put("spawnPitch", spawn.getPitch()); - ret.put("spawnYaw", spawn.getYaw()); + ret.put( "zeroBlockResetDelaySec", Double.valueOf( getZeroBlockResetDelaySec() ) ); + + ret.put( "skipResetEnabled", isSkipResetEnabled() ); + ret.put( "skipResetPercent", getSkipResetPercent() ); + ret.put( "skipResetBypassLimit", getSkipResetBypassLimit() ); + + ret.put( "resetThresholdPercent", getResetThresholdPercent() ); + + if (isHasSpawn()) { + ret.put("spawnX", getSpawn().getX()); + ret.put("spawnY", getSpawn().getY()); + ret.put("spawnZ", getSpawn().getZ()); + ret.put("spawnPitch", getSpawn().getPitch()); + ret.put("spawnYaw", getSpawn().getYaw()); } + // This is a validation set to ensure only one block is written to file system: + Set validateBlockNames = new HashSet<>(); + List blockStrings = new ArrayList<>(); - for (Block block : blocks) { - blockStrings.add(block.getType().getId() + "-" + block.getChance()); + for (Block block : getBlocks()) { + if ( !validateBlockNames.contains( block.getType().name() )) { + // Use the BlockType.name() to save the block type to the file: + blockStrings.add(block.getType().name() + "-" + block.getChance()); +// blockStrings.add(block.getType().getId() + "-" + block.getChance()); + validateBlockNames.add( block.getType().name() ); + } } + ret.put("blocks", blockStrings); - return ret; - } - - /** - * NOTE: Have no idea WHY this always returns a value of true; why not void then? - * - * @return - */ - public boolean reset() { - // The all-important event - MineResetEvent event = new MineResetEvent(this); - Prison.get().getEventBus().post(event); - if (event.isCanceled()) { - return true; + // reset validation for next block list: + validateBlockNames.clear(); + + List prisonBlockStrings = new ArrayList<>(); + for (PrisonBlock pBlock : getPrisonBlocks() ) { + if ( !validateBlockNames.contains( pBlock.getBlockName()) ) { + prisonBlockStrings.add(pBlock.getBlockName() + "-" + pBlock.getChance()); + validateBlockNames.add( pBlock.getBlockName() ); + } } + + ret.put("prisonBlocks", prisonBlockStrings); - try { - Optional worldOptional = getWorld(); - if (!worldOptional.isPresent()) { - Output.get().logError("Could not reset mine " + name - + " because the world it was created in does not exist."); - return false; - } - World world = worldOptional.get(); - - MineManager manager = PrisonMines.getInstance().getMineManager(); - // Generate new set of randomized blocks each time: - manager.generateBlockList(this); - List blockTypes = manager.getRandomizedBlocks().get(name); - - teleportAllPlayersOut( world, getBounds().getyBlockMax() ); - - int i = 0; - boolean isFillMode = PrisonMines.getInstance().getConfig().fillMode; - for (int y = getBounds().getyBlockMin(); y <= getBounds().getyBlockMax(); y++) { - for (int x = getBounds().getxBlockMin(); x <= getBounds().getxBlockMax(); x++) { - for (int z = getBounds().getzBlockMin(); z <= getBounds().getzBlockMax(); z++) { - Location targetBlock = new Location(world, x, y, z); - - if (!isFillMode || isFillMode && targetBlock.getBlockAt().isEmpty()) { - targetBlock.getBlockAt().setType(blockTypes.get(i++)); - } - } - } - } - - // NOTE: This is not making sense, since block list was generated about 20 lines ago? - // Why generate blocks after resetting the mine, of which they will NOT be used since the - // list will be generated above when the mines reset again? This could be adding to - // lag and sluggishness on mine reset events, which could cause players to fall back in to - // the mine and suffocate? - // I'm about to remove this code, but I'm thinking it is left over from a refactoring - // and was not removed? Comment out for now. -// if (PrisonMines.getInstance().getConfig().asyncReset) { -// asyncGen(); -// } - - // free up memory: - manager.clearCache(); - - // If a player falls back in to the mine before it is fully done being reset, - // such as could happen if there is lag or a lot going on within the server, - // this will TP anyone out who would otherwise suffocate. I hope! lol - teleportAllPlayersOut( world, getBounds().getyBlockMax() ); - - return true; - } catch (Exception e) { - Output.get().logError("&cFailed to reset mine " + name, e); - return false; - } + ret.put("commands", getResetCommands()); + + + ret.put( "usePagingOnReset", isUsePagingOnReset() ); + + return ret; } - /** - *

This function teleports players out of existing mines if they are within - * their boundaries within the world where the Mine exists.

- * - *

Using only players within the existing world of the current mine, each - * player is checked to see if they are within the mine, and if they are they - * are teleported either to the mine's spawn location, or straight up from the - * center of the mine, to the top of the mine (assumes air space will exist there).

- * - *

This function eliminates possible bug of players being teleported from other - * worlds, and also eliminates the possibility that the destination could - * ever be null.

- * - * @param world - world - * @param targetY - */ - private void teleportAllPlayersOut(World world, int targetY) { - List players = (world.getPlayers() != null ? world.getPlayers() : - Prison.get().getPlatform().getOnlinePlayers()); - for (Player player : players) { - if ( isSameWorld(world, getBounds().getMin().getWorld()) && - getBounds().within(player.getLocation())) { - - teleportPlayerOut(player); - -// Location destination = null; -// if (this.hasSpawn && getWorld().isPresent()) { -// destination = this.spawn; -// } else { -// destination = player.getLocation(); -// destination.setY( targetY + 1 ); -// } -// -// player.teleport( destination ); -// PrisonMines.getInstance().getMinesMessages().getLocalizable("teleported") -// .withReplacements(this.name).sendTo(player); - } - } + private Location getLocation(Document doc, World world, String x, String y, String z) { + return new Location(world, (double) doc.get(x), (double) doc.get(y), (double) doc.get(z)); } - /** - *

This function will teleport the player out of a given mine, or to the given - * mine. It will not confirm if the player is within the mine before trying to - * teleport. - *

- * - *

This function will teleport the player to the defined spawn location, or it - * will teleport the player to the center of the mine, but on top of the - * mine's surface.

- * - *

If the player target location has an empty block under its feet, it will - * then spawn in a single glass block so the player will not take fall damage. - * If that block is within the mine, it will be reset at a later time when the - * mine resets and resets that block. If it is part of spawn for the mine, then - * the glass block will become part of the landscape. - *

- * - * @param player - */ - public void teleportPlayerOut(Player player) { - - Location altTp = new Location( getBounds().getCenter() ); - altTp.setY( getBounds().getyBlockMax() + 1 ); - Location target = isHasSpawn() ? getSpawn() : altTp; - - // Player needs to stand on something. If block below feet is air, change it to a - // glass block: - Location targetGround = new Location( target ); - targetGround.setY( target.getBlockY() - 1 ); - if ( targetGround.getBlockAt().isEmpty() ) { - targetGround.getBlockAt().setType( BlockType.GLASS ); - } - - player.teleport( target ); - PrisonMines.getInstance().getMinesMessages().getLocalizable("teleported") - .withReplacements(this.name).sendTo(player); + private Location getLocation(Document doc, World world, String x, String y, String z, String pitch, String yaw) { + Location loc = getLocation(doc, world, x, y, z); + loc.setPitch( ((Double) doc.get(pitch)).floatValue() ); + loc.setYaw( ((Double) doc.get(yaw)).floatValue() ); + return loc; } - /** - *

This is a temporary fix until the Bounds.within() checks for the - * same world. For now, it is assumed that Bounds.min and Bounds.max are - * the same world, but that may not always be the case.

- * - * @param w1 First world to compare to - * @param w2 Second world to compare to - * @return true if they are the same world - */ - private boolean isSameWorld(World w1, World w2) { - // TODO Need to fix Bounds.within() to test for same worlds: - return w1 == null && w2 == null || - w1 != null && w2 != null && - w1.getName().equalsIgnoreCase(w2.getName()); - } - -// private void asyncGen() { -// try { -// Prison.get().getPlatform().getScheduler() -// .runTaskLaterAsync( -// () -> PrisonMines.getInstance().getMineManager().generateBlockList(this), 0L); -// } catch (Exception e) { -// Output.get().logWarn("Couldn't generate blocks for mine " + name -// + " asynchronously. The blocks will be generated synchronously later.", e); -// } -// } - - - /** - * Gets the spawn for this mine - * - * @return the location of the spawn. {@link Optional#empty()} if no spawn is present OR the - * world can't be found - */ - public Location getSpawn() { - return spawn; - } - - /** - * Sets the spawn for this mine. - * - * @param location the new spawn - * @return this instance for chaining - */ - public Mine setSpawn(Location location) { - hasSpawn = (location != null); - spawn = location; - return this; - } - - /** - * Gets the name of this mine - * - * @return the name of this mine - */ - public String getName() { - return name; - } - - /** - * Sets the name of this mine - * - * @param name the new name - * @return this instance for chaining - */ - public Mine setName(String name) { - this.name = name; - return this; - } - - /** - * Gets the world - */ - public Optional getWorld() { - return Prison.get().getPlatform().getWorld(worldName); - } - - public Bounds getBounds() { - return bounds; - } - - /** - * (Re)defines the boundaries for this mine - * - * @param bounds the new boundaries - * @return this instance for chaining - */ - public Mine setBounds(Bounds bounds) { - this.bounds = bounds; - this.worldName = bounds.getMin().getWorld().getName(); - return this; - } - - public List getBlocks() { - return blocks; - } - - /** - * Sets the blocks for this mine - * - * @param blockMap the new blockmap with the {@link BlockType} as the key, and the chance of the - * block appearing as the value. - * @return this instance for chaining - */ - public Mine setBlocks(HashMap blockMap) { - blocks = new ArrayList<>(); - for (Map.Entry entry : blockMap.entrySet()) { - blocks.add(new Block(entry.getKey(), entry.getValue())); - } - return this; - } - - public boolean isInMine(Location location) { - return getBounds().within(location); - } - - public boolean isInMine(BlockType blockType) { - for (Block block : getBlocks()) { - if (blockType == block.getType()) { - return true; - } - } - return false; - } - - public double area() { - return getBounds().getArea(); - } - @Override public boolean equals(Object obj) { - return (obj instanceof Mine) && (((Mine) obj).name).equals(name); + return (obj instanceof Mine) && (((Mine) obj).getName()).equals(getName()); } @Override public int hashCode() { - return name.hashCode(); + return getName().hashCode(); } - - - public String getWorldName() - { - return worldName; - } - public void setWorldName( String worldName ) - { - this.worldName = worldName; - } - - public boolean isHasSpawn() - { - return hasSpawn; - } - public void setHasSpawn( boolean hasSpawn ) - { - this.hasSpawn = hasSpawn; - } - } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineCountAirBlocksAsyncTask.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineCountAirBlocksAsyncTask.java new file mode 100644 index 000000000..a66cf57f9 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineCountAirBlocksAsyncTask.java @@ -0,0 +1,23 @@ +package tech.mcprison.prison.mines.data; + +public class MineCountAirBlocksAsyncTask + implements PrisonRunnable { + + private MineReset mine; + private PrisonRunnable callbackAsync; + + public MineCountAirBlocksAsyncTask(MineReset mine, PrisonRunnable callbackAsync) { + this.mine = mine; + this.callbackAsync = callbackAsync; + } + + @Override + public void run() { + this.mine.refreshAirCountAsyncTask(); + + if ( this.callbackAsync != null ) { + this.mine.submitAsyncTask( callbackAsync ); + } + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineData.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineData.java new file mode 100644 index 000000000..8460de481 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineData.java @@ -0,0 +1,475 @@ +package tech.mcprison.prison.mines.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.util.BlockType; +import tech.mcprison.prison.util.Bounds; +import tech.mcprison.prison.util.Location; + +public abstract class MineData { + + public static final int MINE_RESET__TIME_SEC__DEFAULT = 15 * 60; // 15 minutes + public static final int MINE_RESET__TIME_SEC__MINIMUM = 30; // 30 seconds + public static final long MINE_RESET__BROADCAST_RADIUS_BLOCKS = 150; + + public static final String MINE_NOTIFICATION_PERMISSION_PREFIX = "mines.notification."; + + private String name; + private boolean enabled = false; + + private Bounds bounds; + + private Location spawn; + private String worldName; + private boolean hasSpawn = false; + + private int resetTime; + private MineNotificationMode notificationMode; + private long notificationRadius; + private boolean useNotificationPermission = false; + + private long targetResetTime; + private int resetCount = 0; + + private List blocks; + private List prisonBlocks; + + private long totalBlocksMined = 0; + private double zeroBlockResetDelaySec; + + private double resetThresholdPercent = 0; + + + private boolean skipResetEnabled = false; + private double skipResetPercent; + private int skipResetBypassLimit; + private transient int skipResetBypassCount; + + private List resetCommands; + + private boolean usePagingOnReset = false; + + + public enum MineNotificationMode { + disabled, + within, + radius, + + displayOptions + ; + + public static MineNotificationMode fromString(String mode) { + return fromString(mode, radius); + } + public static MineNotificationMode fromString(String mode, MineNotificationMode defaultValue) { + MineNotificationMode results = defaultValue; + + if ( mode != null && mode.trim().length() > 0 ) { + for ( MineNotificationMode mnm : values() ) { + if ( mnm.name().equalsIgnoreCase( mode )) { + results = mnm; + } + } + } + + return results; + } + } + + public MineData() { + this.blocks = new ArrayList<>(); + this.prisonBlocks = new ArrayList<>(); + + this.enabled = false; + + this.resetTime = MINE_RESET__TIME_SEC__DEFAULT; + this.notificationMode = MineNotificationMode.radius; + this.notificationRadius = MINE_RESET__BROADCAST_RADIUS_BLOCKS; + this.useNotificationPermission = false; + + this.targetResetTime = 0; + this.resetCount = 0; + this.totalBlocksMined = 0; + this.zeroBlockResetDelaySec = 0; + this.resetThresholdPercent = 0; + + this.skipResetEnabled = false; + this.skipResetPercent = 80.0D; + this.skipResetBypassLimit = 50; + this.skipResetBypassCount = 0; + + this.resetCommands = new ArrayList<>(); + + this.usePagingOnReset = false; + } + + /** + *

This initialize function gets called after the classes are + * instantiated, and is initiated from Mine class and propagates + * to the MineData class. Good for kicking off the scheduler. + *

+ */ + protected void initialize() { + + } + + + + + public boolean isEnabled() { + return enabled; + } + public void setEnabled( boolean enabled ) { + this.enabled = enabled; + } + + /** + * Gets the name of this mine + * + * @return the name of this mine + */ + public String getName() { + return name; + } + + /** + * Sets the name of this mine + * + * @param name the new name + */ + public void setName(String name) { + this.name = name; + } + + public String getWorldName() { + return worldName; + } + public void setWorldName( String worldName ) { + this.worldName = worldName; + } + + /** + *

A mine cannot be created without defining the bounds. Therefore, + * the "World" is stored within the bounds and that's where it should come from. + * At least this way, once the world is loaded, it will be set once and for all + * within the bounds and as such, it will help eliminate the overhead incurred by + * trying to get it from the platform object. + *

+ * + *

Until the world is fully loaded and processed by the prison mine manager, + * this function will not be able to return a value for world. + *

+ * + * @return + */ + public Optional getWorld() { + return Optional.ofNullable( getBounds().getMin().getWorld() ); +// return Prison.get().getPlatform().getWorld(worldName); + } + + /** + *

This function sets the world object for this mine, then + * indicates that this mine is enabled. + * This will be needed if the world in which the mine is located + * has not yet been loaded. This may be the situation when using a + * world generated and loaded by Multiverse-core, or similar plugin. + *

+ * + * @param world + */ + public void setWorld( World world ) { + + // Must add world to the two Bounds locations: + if ( world != null ) { + getBounds().setWorld( world ); + + // Add world to Spawn, if it exists: + if ( getSpawn() != null ) { + getSpawn().setWorld( world ); + } + } + + setEnabled( world != null ); + } + + public Bounds getBounds() { + return bounds; + } + + /** + *

(Re)defines the boundaries for this mine. + *

+ * + *

This function used to set the world name every time the bounds would + * change. The world name MUST NEVER be changed. If world is null then it will screw + * up the original location of when the was created. World name is set + * in the document loader under Mine.loadFromDocument as the first field + * that is set when restoring from the file. + *

+ * + * @param bounds the new boundaries + */ + public void setBounds(Bounds bounds) { + this.bounds = bounds; + + // The world name MUST NEVER be changed. If world is null then it will screw + // up the original location of when the was created. World name is set + // in the document loader under Mine.loadFromDocument as the first field + // that is set when restoring from the file. + //this.worldName = bounds.getMin().getWorld().getName(); + } + + public List getBlocks() { + return blocks; + } + + + public List getPrisonBlocks() { + return prisonBlocks; + } + + + /** + * This is only used in an obsolete conversion utility. + * + * Adding the newer PrisonBlocks for compatibility. + * + * Sets the blocks for this mine + * + * @param blockMap the new blockmap with the {@link BlockType} as the key, and the chance of the + * block appearing as the value. + */ + public void setBlocks(HashMap blockMap) { + this.blocks.clear(); + this.prisonBlocks.clear(); + + for (Map.Entry entry : blockMap.entrySet()) { + blocks.add(new Block(entry.getKey(), entry.getValue())); + + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( entry.getKey().name() ); + if ( prisonBlock != null ) { + prisonBlock.setChance( entry.getValue() ); + prisonBlocks.add( prisonBlock ); + } + } + } + + public boolean isInMine(Location location) { + return getBounds().within(location); + } + + public boolean isInMine(BlockType blockType) { + for (Block block : getBlocks()) { + if (blockType == block.getType()) { + return true; + } + } + return false; + } + + public boolean isInMine(PrisonBlock blockType) { + for (PrisonBlock block : getPrisonBlocks()) { + if (blockType.getBlockName().equalsIgnoreCase( block.getBlockName())) { + return true; + } + } + return false; + } + + public double area() { + return getBounds().getArea(); + } + + + /** + * Gets the spawn for this mine + * + * @return the location of the spawn. {@link Optional#empty()} if no spawn is present OR the + * world can't be found + */ + public Location getSpawn() { + return spawn; + } + + /** + * Sets the spawn for this mine. + * + * @param location the new spawn + * @return this instance for chaining + */ + public void setSpawn(Location location) { + hasSpawn = (location != null); + spawn = location; + } + + public boolean isHasSpawn() { + return hasSpawn; + } + public void setHasSpawn( boolean hasSpawn ) { + this.hasSpawn = hasSpawn; + } + + public int getResetTime() { + return resetTime; + } + public void setResetTime( int resetTime ) { + this.resetTime = resetTime; + } + + public MineNotificationMode getNotificationMode() { + return notificationMode; + } + public void setNotificationMode( MineNotificationMode notificationMode ) { + this.notificationMode = notificationMode; + } + + public long getNotificationRadius() { + return notificationRadius; + } + public void setNotificationRadius( long notificationRadius ) { + this.notificationRadius = notificationRadius; + } + + public long getTargetResetTime() { + return targetResetTime; + } + public void setTargetResetTime( long targetResetTime ) { + this.targetResetTime = targetResetTime; + } + + public boolean isUseNotificationPermission() { + return useNotificationPermission; + } + public void setUseNotificationPermission( boolean useNotificationPermission ) { + this.useNotificationPermission = useNotificationPermission; + } + + public String getMineNotificationPermissionName() { + return MINE_NOTIFICATION_PERMISSION_PREFIX + getName(); + } + + /** + *

This is the remaining time until a reset, in seconds. + * This is based upon the getTargetResetTime() in ms and the current + * System.currentTimeMillis(). + *

+ * + *

The actual remaining time can vary greatly and is highly + * dependent upon the server load. Since many jobs within the whole + * mine reset process are scheduled to run in the future, their run + * time is just a request and it can vary if there are demanding jobs + * that are running, or if the bukkit/spigot's TPS starts dropping + * below 20. + *

+ * + * @return + */ + public double getRemainingTimeSec() { + // NOTE: timeleft can vary based upon server loads: + long targetResetTime = getTargetResetTime(); + double remaining = ( targetResetTime <= 0 ? 0d : + (targetResetTime - System.currentTimeMillis()) / 1000d); + return remaining; + } + + public int incrementResetCount() { + return ++resetCount; + } + public int getResetCount() { + return resetCount; + } + public void setResetCount( int resetCount ) { + this.resetCount = resetCount; + } + + /** + * May not be 100% thread safe, but odds of collisions will be minimal and + * if its off by a few blocks its not a big deal since the value resets + * when the server resets. + * + * @return + */ + public long addTotalBlocksMined( int blockCount ) { + return totalBlocksMined += blockCount; + } + public long incrementTotalBlocksMined() { + return ++totalBlocksMined; + } + public long getTotalBlocksMined() { + return totalBlocksMined; + } + public void setTotalBlocksMined( long totalBlocksMined ) { + this.totalBlocksMined = totalBlocksMined; + } + + public boolean isZeroBlockResetDisabled() { + return zeroBlockResetDelaySec == -1.0d; + } + public double getZeroBlockResetDelaySec() { + return zeroBlockResetDelaySec; + } + public void setZeroBlockResetDelaySec( double zeroBlockResetDelaySec ) { + this.zeroBlockResetDelaySec = zeroBlockResetDelaySec; + } + + public double getResetThresholdPercent() { + return resetThresholdPercent; + } + public void setResetThresholdPercent( double resetThresholdPercent ) { + this.resetThresholdPercent = resetThresholdPercent; + } + + public boolean isSkipResetEnabled() { + return skipResetEnabled; + } + public void setSkipResetEnabled( boolean skipResetEnabled ) { + this.skipResetEnabled = skipResetEnabled; + } + + public double getSkipResetPercent() { + return skipResetPercent; + } + public void setSkipResetPercent( double skipResetPercent ) { + this.skipResetPercent = skipResetPercent; + } + + public int getSkipResetBypassLimit() { + return skipResetBypassLimit; + } + public void setSkipResetBypassLimit( int skipResetBypassLimit ) { + this.skipResetBypassLimit = skipResetBypassLimit; + } + + public int incrementSkipResetBypassCount() { + return ++skipResetBypassCount; + } + public int getSkipResetBypassCount() { + return skipResetBypassCount; + } + public void setSkipResetBypassCount( int skipResetBypassCount ) { + this.skipResetBypassCount = skipResetBypassCount; + } + + public List getResetCommands() { + return resetCommands; + } + public void setResetCommands( List resetCommands ) { + this.resetCommands = resetCommands; + } + + public boolean isUsePagingOnReset() { + return usePagingOnReset; + } + + public void setUsePagingOnReset( boolean usePagingOnReset ) { + this.usePagingOnReset = usePagingOnReset; + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineReset.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineReset.java new file mode 100644 index 000000000..f9d00ae5b --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineReset.java @@ -0,0 +1,1443 @@ +package tech.mcprison.prison.mines.data; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.TreeMap; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.internal.block.PrisonBlockTypes.InternalBlockTypes; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.data.MineScheduler.MineJob; +import tech.mcprison.prison.mines.events.MineResetEvent; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.util.BlockType; +import tech.mcprison.prison.util.Location; +import tech.mcprison.prison.util.Text; + +public abstract class MineReset + extends MineData +{ + /** + *

Minecraft ticks have 20 per seconds, which is 50 MS per tick. + * The value for MINE_RESET__MAX_PAGE_ELASPSED_TIME_MS is intended + * to be used as a threshold to determine when to stop placing blocks + * and resubmit in the sync job queue to allow other processes to run. + *

+ * + *

This value, in milliseconds, is not hard-and-fast guaranteed to be + * caught exactly at that time. It is instead used to check to see if + * the current process has exceeded this value, which may be many times + * greater than this value. + *

+ * + *

This value is subject to refinement and tuning to ensure better and + * more accurate responses. + *

+ */ + public static final long MINE_RESET__MAX_PAGE_ELASPSED_TIME_MS = 75; + + /** + *

When placing blocks, this is the block count that is used to check for + * the elapsed time. + *

+ * + *

It does not matter if some of those blocks were placed in a prior + * page, for it is the elapsed time that is important. It's also important + * that there are not too many checks since it will just contribute to lag. + * The value needs to be moderately large. + *

+ * + *

This value is subject to refinement and tuning to ensure better and + * more accurate responses. + *

+ */ + public static final long MINE_RESET__PAGE_TIMEOUT_CHECK__BLOCK_COUNT = 250; + + + public static final long MINE_RESET__AIR_COUNT_BASE_DELAY = 30000L; // 30 seconds + + @Deprecated + private List randomizedBlocks; + + // to replace randomizedBlocks.... + private List mineTargetBlocks; + private TreeMap mineTargetBlocksMap; + + private int resetPage = 0; + private int resetPosition = 0; + + private int airCountOriginal = 0; + private int airCount = 0; + private long airCountTimestamp = 0L; + private long airCountElapsedTimeMs = 0L; + + + private int blockBreakCount = 0; + + +// private boolean[] mineAirBlocksOriginal; +// private boolean[] mineAirBlocksCurrent; + + private long statsResetTimeMS = 0; + private long statsBlockGenTimeMS = 0; + private long statsBlockUpdateTimeMS = 0; + private long statsTeleport1TimeMS = 0; + private long statsTeleport2TimeMS = 0; + private long statsMessageBroadcastTimeMS = 0; + + private int statsResetPages = 0; + private long statsResetPageBlocks = 0; + private long statsResetPageMs = 0; + + + public MineReset() { + super(); + + this.randomizedBlocks = new ArrayList<>(); + + this.mineTargetBlocks = new ArrayList<>(); + this.mineTargetBlocksMap = new TreeMap<>(); + } + + /** + *

This initialize function gets called after the classes are + * instantiated, and is initiated from Mine class and propagates + * to the MineData class. Good for kicking off the scheduler. + *

+ * + *

Once the mine has been loaded in to memory, the number of + * air blocks must be counted to properly set the blockBreakCount. + *

+ * + */ + @Override + protected void initialize() { + super.initialize(); + + // Once the mine has been loaded, MUST get a count of all air blocks. + refreshBlockBreakCountUponStartup(); + } + + /** + *

Optimized the mine reset to focus on itself. Also set the Y plane to refresh at the top and work its + * way down. That way if the play is teleported to the top, it will appear like the whole mine has reset + * instantly and they will not see the delay from the bottom of the mine working up to the top. This will + * also reduce the likelihood of the player falling back in to the mine if there is no spawn set. + *

+ * + *

The ONLY code that could be asynchronous ran is the random generation of the blocks. The other + * lines of code is using bukkit and/or spigot api calls which MUST be ran synchronously. + *

+ */ + protected void resetSynchonously() { + + long start = System.currentTimeMillis(); + + // The all-important event + MineResetEvent event = new MineResetEvent(this); + Prison.get().getEventBus().post(event); + if (!event.isCanceled()) { + resetSynchonouslyInternal(); + } + + long stop = System.currentTimeMillis(); + setStatsResetTimeMS( stop - start ); + + // Tie to the command stats mode so it logs it if stats are enabled: + if ( PrisonMines.getInstance().getMineManager().isMineStats() ) { + DecimalFormat dFmt = new DecimalFormat("#,##0"); + Output.get().logInfo("&cMine reset: &7" + getName() + + "&c Blocks: &7" + dFmt.format( getBounds().getTotalBlockCount() ) + + statsMessage() ); + } + + } + + private void resetSynchonouslyInternal() { + try { + + if ( !isEnabled() ) { + Output.get().logError( + String.format( "MineReset: Reset failure: Mine is not enabled. " + + "Ensure world exists. mine= %s ", + getName() )); + return; + } + + boolean useNewBlockModel = Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ); + + // Output.get().logInfo( "MineRest.resetSynchonouslyInternal() " + getName() ); + + Optional worldOptional = getWorld(); + World world = worldOptional.get(); + + // Generate new set of randomized blocks each time: This is the ONLY thing that can be async!! ;( + //generateBlockList(); + + setStatsTeleport1TimeMS( + teleportAllPlayersOut( getBounds().getyBlockMax() ) ); + + // Before reset commands: + if ( getResetCommands() != null && getResetCommands().size() > 0 ) { + + for (String command : getResetCommands() ) { +// String formatted = cmd.replace("{player}", prisonPlayer.getName()) +// .replace("{player_uid}", player.uid.toString()); + if ( command.startsWith( "before: " )) { + String cmd = command.replace( "before: ", "" ); + + PrisonAPI.dispatchCommand(cmd); + } + } + } + + + + long time2 = System.currentTimeMillis(); + + boolean isFillMode = PrisonMines.getInstance().getConfig().fillMode; + + Location altTp = alternativeTpLocation(); + altTp.setY( altTp.getBlockY() - 1 ); // Set Y one lower to + //boolean replaceGlassBlock = ( isFillMode && altTp.getBlockAt().getType() == BlockType.GLASS ); + + // Reset the block break count before resetting the blocks: + setBlockBreakCount( 0 ); + Random random = new Random(); + + int i = 0; + for (int y = getBounds().getyBlockMax(); y >= getBounds().getyBlockMin(); y--) { +// for (int y = getBounds().getyBlockMin(); y <= getBounds().getyBlockMax(); y++) { + for (int x = getBounds().getxBlockMin(); x <= getBounds().getxBlockMax(); x++) { + for (int z = getBounds().getzBlockMin(); z <= getBounds().getzBlockMax(); z++) { + Location targetBlock = new Location(world, x, y, z); + + if ( useNewBlockModel ) { + + if (!isFillMode || + isFillMode && targetBlock.getBlockAt().isEmpty() || + isFillMode && targetBlock.equals(altTp) && + altTp.getBlockAt().getPrisonBlock().getBlockName().equalsIgnoreCase( "GLASS" ) ) { + + + targetBlock.getBlockAt().setPrisonBlock( randomlySelectPrisonBlock( random )); + i++; +// targetBlock.getBlockAt().setType(getRandomizedBlocks().get(i++)); + } + + if ( targetBlock.getBlockAt().getPrisonBlock().getBlockName().equalsIgnoreCase( "AIR" ) ) { + incrementBlockBreakCount(); + } + } + else { + + if (!isFillMode || + isFillMode && targetBlock.getBlockAt().isEmpty() || + isFillMode && targetBlock.equals(altTp) && altTp.getBlockAt().getType() == BlockType.GLASS ) { + + + targetBlock.getBlockAt().setType(randomlySelectBlock( random )); + i++; +// targetBlock.getBlockAt().setType(getRandomizedBlocks().get(i++)); + } + + if ( targetBlock.getBlockAt().getType() == BlockType.AIR ) { + incrementBlockBreakCount(); + } + } + } + } + } + + time2 = System.currentTimeMillis() - time2; + setStatsBlockUpdateTimeMS( time2 ); + + setStatsResetPages( getStatsResetPages() + 1 ); + setStatsResetPageBlocks( i ); + setStatsResetPageMs( time2 ); + + incrementResetCount(); + + + setSkipResetBypassCount(0); + + + // If a player falls back in to the mine before it is fully done being reset, + // such as could happen if there is lag or a lot going on within the server, + // this will TP anyone out who would otherwise suffocate. I hope! lol + setStatsTeleport2TimeMS( + teleportAllPlayersOut( getBounds().getyBlockMax() ) ); + + // free up memory: + getRandomizedBlocks().clear(); + + + // After reset commands: + if ( getResetCommands() != null && getResetCommands().size() > 0 ) { + + for (String command : getResetCommands() ) { +// String formatted = cmd.replace("{player}", prisonPlayer.getName()) +// .replace("{player_uid}", player.uid.toString()); + if ( command.startsWith( "after: " )) { + String cmd = command.replace( "after: ", "" ); + + PrisonAPI.dispatchCommand(cmd); + } + } + } + + + // Broadcast message to all players within a certain radius of this mine: + broadcastResetMessageToAllPlayersWithRadius(); +// broadcastResetMessageToAllPlayersWithRadius( MINE_RESET__BROADCAST_RADIUS_BLOCKS ); + + } catch (Exception e) { + Output.get().logError("&cFailed to reset mine " + getName(), e); + } + } + + + public String statsMessage() { + StringBuilder sb = new StringBuilder(); + DecimalFormat dFmt = new DecimalFormat("#,##0.000"); + DecimalFormat iFmt = new DecimalFormat("#,##0"); + + sb.append( "&3 Reset: &7" ); + sb.append( dFmt.format(getStatsResetTimeMS() / 1000.0d )); + + sb.append( "&3 BlockGen: &7" ); + sb.append( dFmt.format(getStatsBlockGenTimeMS() / 1000.0d )); + + sb.append( "&3 TP1: &7" ); + sb.append( dFmt.format(getStatsTeleport1TimeMS() / 1000.0d )); + + sb.append( "&3 BlockUpdate: &7" ); + sb.append( dFmt.format(getStatsBlockUpdateTimeMS() / 1000.0d )); + + sb.append( "&3 TP2: &7" ); + sb.append( dFmt.format(getStatsTeleport2TimeMS() / 1000.0d )); + + sb.append( "&3 Msg: &7" ); + sb.append( dFmt.format(getStatsMessageBroadcastTimeMS() / 1000.0d )); + + sb.append( "&3 ResetPages: &7" ); + sb.append( iFmt.format(getStatsResetPages() )); + + double avgBlocks = getStatsResetPageBlocks() / getStatsResetPages(); + double avgMs = getStatsResetPageMs() / getStatsResetPages(); + + sb.append( "&3 avgBlocks: &7" ); + sb.append( dFmt.format(avgBlocks)); + + sb.append( "&3 avgMs: &7" ); + sb.append( dFmt.format(avgMs)); + + return sb.toString(); + } + + private void resetStats() { + setResetPage( 0 ); + + // The reset position is critical in ensuring that all blocks within the mine are reset + // and that when a reset process pages (allows another process to run) then it will be + // used to pick up where it left off. + setResetPosition( 0 ); + + setSkipResetBypassCount( 0 ); + + // NOTE: DO NOT reset blockBreakCount here! Players can break many blocks between + // here and when the mine actually starts to reset. + + setAirCountOriginal( 9 ); + setAirCount( 0 ); + + setStatsResetTimeMS( 0 ); + setStatsBlockGenTimeMS( 0 ); + setStatsBlockUpdateTimeMS( 0 ); + setStatsTeleport1TimeMS( 0 ); + setStatsTeleport2TimeMS( 0 ); + setStatsMessageBroadcastTimeMS( 0 ); + + setStatsResetPages( 0 ); + setStatsResetPageBlocks( 0 ); + setStatsResetPageMs( 0 ); + } + + /** + *

This function teleports players out of existing mines if they are within + * their boundaries within the world where the Mine exists.

+ * + *

Using only players within the existing world of the current mine, each + * player is checked to see if they are within the mine, and if they are they + * are teleported either to the mine's spawn location, or straight up from the + * center of the mine, to the top of the mine (assumes air space will exist there).

+ * + *

This function eliminates possible bug of players being teleported from other + * worlds, and also eliminates the possibility that the destination could + * ever be null.

+ * + * @param world - world + * @param targetY + */ + private long teleportAllPlayersOut(int targetY) { + long start = System.currentTimeMillis(); + + World world = getBounds().getCenter().getWorld(); + + if ( isEnabled() && world != null ) { + List players = (world.getPlayers() != null ? world.getPlayers() : + Prison.get().getPlatform().getOnlinePlayers()); + for (Player player : players) { + if ( getBounds().within(player.getLocation()) ) { + + teleportPlayerOut(player); + } + } + } + + return System.currentTimeMillis() - start; + } + + /** + *

This function will teleport the player out of a given mine, or to the given + * mine. It will not confirm if the player is within the mine before trying to + * teleport. + *

+ * + *

This function will teleport the player to the defined spawn location, or it + * will teleport the player to the center of the mine, but on top of the + * mine's surface.

+ * + *

If the player target location has an empty block under its feet, it will + * then spawn in a single glass block so the player will not take fall damage. + * If that block is within the mine, it will be reset at a later time when the + * mine resets and resets that block. If it is part of spawn for the mine, then + * the glass block will become part of the landscape. + *

+ * + *

Do not show any TP notifications. It will be obvious that the mine + * just reset and that they were teleported out of the mine. Since there is no + * control over this message, like enabling or disabling, then I'm just + * removing it since it just clutters chat and provides no real additional + * value. + *

+ * + * @param player + */ + public void teleportPlayerOut(Player player) { + + if ( !isEnabled() ) { + player.sendMessage( + String.format( "&7MineReset: Teleport failure: Mine is not enabled. " + + "Ensure world exists. mine= &3%s ", + getName() )); + } + else { + Location altTp = alternativeTpLocation(); + Location target = isHasSpawn() ? getSpawn() : altTp; + + // Player needs to stand on something. If block below feet is air, change it to a + // glass block: + Location targetGround = new Location( target ); + targetGround.setY( target.getBlockY() - 1 ); + if ( targetGround.getBlockAt().isEmpty() ) { + targetGround.getBlockAt().setType( BlockType.GLASS ); + } + + player.teleport( target ); + +// PrisonMines.getInstance().getMinesMessages().getLocalizable("teleported") +// .withReplacements(this.getName()).sendTo(player); + } + + } + + + private Location alternativeTpLocation() + { + Location altTp = new Location( getBounds().getCenter() ); + altTp.setY( getBounds().getyBlockMax() + 1 ); + return altTp; + } + + public int getPlayerCount() { + int count = 0; + + if ( isEnabled() ) { + + World world = getWorld().get(); + + List players = (world.getPlayers() != null ? world.getPlayers() : + Prison.get().getPlatform().getOnlinePlayers()); + for (Player player : players) { + if ( getBounds().within(player.getLocation()) ) { + count++; + } + } + } + + return count; + } + +// /** +// *

This is a temporary fix until the Bounds.within() checks for the +// * same world. For now, it is assumed that Bounds.min and Bounds.max are +// * the same world, but that may not always be the case.

+// * +// * @param w1 First world to compare to +// * @param w2 Second world to compare to +// * @return true if they are the same world +// */ +// private boolean isSameWorld(World w1, World w2) { +// // TODO Need to fix Bounds.within() to test for same worlds: +// return w1 == null && w2 == null || +// w1 != null && w2 != null && +// w1.getName().equalsIgnoreCase(w2.getName()); +// } + + /** + *

This generation of a new block list for the mines is designed to run asynchronously. + * It not only generates what each block should be, it also records what the block location + * is. This allows actual block updates to be performed linearly using the mineTargetBlock + * List, or to randomly access each block using the mineTargetBlockMap; two keys in to the + * same collection of target blocks. + *

+ * + *

The major use of the mineTargetBlock List is to allow paging of the updates: where a mine + * can be updated in smaller segments. The actual update must be ran synchronously and in small + * segments. + *

+ * + *

Set the Y plane to refresh at the top and work its way down. That way if the play is + * teleported to the top, it will appear like the whole mine has reset instantly and they will + * not see the delay from the bottom of the mine working up to the top. This will also reduce + * the likelihood of the player falling back in to the mine if there is no spawn set. + *

+ * + */ + protected void generateBlockListAsync() { + + if ( !isEnabled() ) { + Output.get().logError( + String.format( "MineReset: Block count failure: Mine is not enabled. " + + "Ensure world exists. mine= %s ", + getName() )); + return; + } + + long start = System.currentTimeMillis(); + + // Reset stats: + resetStats(); + + Random random = new Random(); + + // Clear the mineTargetBlocks list: + getMineTargetBlocks().clear(); + +// // Reset the mineAirBlocks to all false values: +// boolean[] mAirBlocks = new boolean[ getBounds().getTotalBlockCount() ]; +// // Arrays.fill( mAirBlocks, false ); // redundant but prevents nulls if were Boolean +// setMineAirBlocksOriginal( mAirBlocks ); + + int airCount = 0; + + for (int y = getBounds().getyBlockMax(); y >= getBounds().getyBlockMin(); y--) { + for (int x = getBounds().getxBlockMin(); x <= getBounds().getxBlockMax(); x++) { + for (int z = getBounds().getzBlockMin(); z <= getBounds().getzBlockMax(); z++) { + + BlockType blockType = randomlySelectBlock( random ); + + MineTargetBlock mtb = new MineTargetBlock( blockType, x, y, z); + + getMineTargetBlocks().add( mtb ); + getMineTargetBlocksMap().put( mtb.getBlockKey(), mtb ); + + if ( blockType == BlockType.AIR ) { +// mAirBlocks[i++] = true; + airCount++; + } + } + } + } + + setAirCountOriginal( airCount ); + setAirCount( airCount ); + + // The reset position is critical in ensuring that all blocks within the mine are reset + // and that when a reset process pages (allows another process to run) then it will be + // used to pick up where it left off. + setResetPosition( 0 ); + + long stop = System.currentTimeMillis(); + setStatsBlockGenTimeMS( stop - start ); + + } + + /** + *

Yeah I know, it has async in the name of the function, but it still can only + * be ran synchronously. The async part implies this is the reset "part" for the + * async workflow. + *

+ * + *

Before this part is ran, the generateBlockListAsync() function must be ran + * to regenerate the new block list. + *

+ * + */ + protected void resetAsynchonously() { + boolean canceled = false; + +// Output.get().logInfo( "MineRest.resetAsynchonously() " + getName() ); + + + if ( getResetPage() == 0 ) { + generateBlockListAsync(); + + canceled = resetAsynchonouslyInitiate(); + } + + if ( !canceled ) { + + // First time through... reset the block break count and run the before reset commands: + if ( getResetPosition() == 0 ) { + + // Reset the block break count before resetting the blocks: + // Set it to the original air count, if subtracted from total block count + // in the mine, then the result will be blocks remaining. + setBlockBreakCount( getAirCountOriginal() ); + + + // Before reset commands: + if ( getResetCommands() != null && getResetCommands().size() > 0 ) { + + for (String command : getResetCommands() ) { +// String formatted = cmd.replace("{player}", prisonPlayer.getName()) +// .replace("{player_uid}", player.uid.toString()); + if ( command.startsWith( "before: " )) { + String cmd = command.replace( "before: ", "" ); + + PrisonAPI.dispatchCommand(cmd); + } + } + } + + } + + resetAsynchonouslyUpdate(); + + if ( getResetPosition() == getMineTargetBlocks().size() ) { + // Done resetting the mine... wrap up: + + // If a player falls back in to the mine before it is fully done being reset, + // such as could happen if there is lag or a lot going on within the server, + // this will TP anyone out who would otherwise suffocate. I hope! lol + setStatsTeleport2TimeMS( + teleportAllPlayersOut( getBounds().getyBlockMax() ) ); + + // Reset the paging for the next reset: + setResetPage( 0 ); + + incrementResetCount(); + + // After reset commands: + if ( getResetCommands() != null && getResetCommands().size() > 0 ) { + + for (String command : getResetCommands() ) { +// String formatted = cmd.replace("{player}", prisonPlayer.getName()) +// .replace("{player_uid}", player.uid.toString()); + if ( command.startsWith( "after: " )) { + String cmd = command.replace( "after: ", "" ); + + PrisonAPI.dispatchCommand(cmd); + } + } + } + + + // Broadcast message to all players within a certain radius of this mine: + broadcastResetMessageToAllPlayersWithRadius(); +// broadcastResetMessageToAllPlayersWithRadius( MINE_RESET__BROADCAST_RADIUS_BLOCKS ); + + + // Tie to the command stats mode so it logs it if stats are enabled: + if ( PrisonMines.getInstance().getMineManager().isMineStats() ) { + DecimalFormat dFmt = new DecimalFormat("#,##0"); + Output.get().logInfo("&cMine reset: &7" + getName() + + "&c Blocks: &7" + dFmt.format( getBounds().getTotalBlockCount() ) + + statsMessage() ); + } + } else { + + // Need to continue to reset the mine. Resubmit it to run again. + MineResetAsyncResubmitTask mrAsyncRT = new MineResetAsyncResubmitTask( this, null ); + + // Must run synchronously!! + submitSyncTask( mrAsyncRT ); + } + } + + // NOTE: blocks already generated in generateBlockListAsync(): + +// long start = System.currentTimeMillis(); +// +// // The all-important event +// MineResetEvent event = new MineResetEvent(this); +// Prison.get().getEventBus().post(event); +// if (!event.isCanceled()) { +// +// try { +// Optional worldOptional = getWorld(); +// if (!worldOptional.isPresent()) { +// Output.get().logError("Could not reset mine " + getName() + +// " because the world it was created in does not exist."); +// return; +// } +// //World world = worldOptional.get(); +// +// setStatsTeleport1TimeMS( +// teleportAllPlayersOut( getBounds().getyBlockMax() ) ); +// +// +// resetAsynchonouslyUpdate(); +// +// +// // If a player falls back in to the mine before it is fully done being reset, +// // such as could happen if there is lag or a lot going on within the server, +// // this will TP anyone out who would otherwise suffocate. I hope! lol +// setStatsTeleport2TimeMS( +// teleportAllPlayersOut( getBounds().getyBlockMax() ) ); +// +// +// +// // Broadcast message to all players within a certain radius of this mine: +// broadcastResetMessageToAllPlayersWithRadius( MINE_RESET_BROADCAST_RADIUS_BLOCKS ); +// +// } catch (Exception e) { +// Output.get().logError("&cFailed to reset mine " + getName(), e); +// } +// } +// +// long stop = System.currentTimeMillis(); +// setStatsResetTimeMS( stop - start ); + +// // Tie to the command stats mode so it logs it if stats are enabled: +// if ( PrisonMines.getInstance().getMineManager().isMineStats() ) { +// DecimalFormat dFmt = new DecimalFormat("#,##0"); +// Output.get().logInfo("&cMine reset: &7" + getName() + +// "&c Blocks: &7" + dFmt.format( getBounds().getTotalBlockCount() ) + +// statsMessage() ); +// } + } + + private boolean resetAsynchonouslyInitiate() { + boolean canceled = false; + + if ( !isEnabled() ) { + Output.get().logError( + String.format( "MineReset: resetAsynchonouslyInitiate failure: Mine is not enabled. " + + "Ensure world exists. mine= %s ", + getName() )); + canceled = true; + } + else { + long start = System.currentTimeMillis(); + + // The all-important event + MineResetEvent event = new MineResetEvent(this); + Prison.get().getEventBus().post(event); + + canceled = event.isCanceled(); + if (!canceled) { + + try { + setStatsTeleport1TimeMS( + teleportAllPlayersOut( getBounds().getyBlockMax() ) ); + + } catch (Exception e) { + Output.get().logError("&cMineReset: Failed to TP players out of mine. mine= " + + getName(), e); + canceled = true; + } + } + + long stop = System.currentTimeMillis(); + setStatsResetTimeMS( stop - start ); + } + + return canceled; + } + + + /** + *

This is the synchronous part of the job that actually updates the blocks. + * It will only replace what it can within the given allocated milliseconds, + * then it will terminate and allow this process to re-run, picking up where it + * left off. + *

+ * + *

Paging is what this is doing. Running, and doing what it can within it's + * limited amount of time, then yielding to any other task, then resuming later. + * This is a way of running a massive synchronous task, without hogging all the + * resources and killing the TPS. + *

+ * + *

NOTE: The values for MINE_RESET__PAGE_TIMEOUT_CHECK__BLOCK_COUNT and for + * MINE_RESET__MAX_PAGE_ELASPSED_TIME_MS are set to arbitrary values and may not + * be the correct values. They may be too large and may have to be adjusted to + * smaller values to better tune the process. + *

+ * + */ + private void resetAsynchonouslyUpdate() { + + if ( !isEnabled() ) { + Output.get().logError( + String.format( "MineReset: resetAsynchonouslyUpdate failure: Mine is not enabled. " + + "Ensure world exists. mine= %s ", + getName() )); + } + else { + World world = getBounds().getCenter().getWorld(); + + long start = System.currentTimeMillis(); + + boolean isFillMode = PrisonMines.getInstance().getConfig().fillMode; + + int blocksPlaced = 0; + long elapsed = 0; + + int i = getResetPosition(); + for ( ; i < getMineTargetBlocks().size(); i++ ) + { + MineTargetBlock target = getMineTargetBlocks().get(i); + + Location targetBlock = new Location(world, + target.getBlockKey().getX(), target.getBlockKey().getY(), + target.getBlockKey().getZ()); + + if (!isFillMode || isFillMode && targetBlock.getBlockAt().isEmpty()) { + targetBlock.getBlockAt().setType(target.getBlockType()); + } + + /** + * About every 250 blocks, or so, check to see if the current wall time + * spent is greater than + * the threshold. If it is greater, then end the update and let it resubmit. + * It does not matter how many blocks were actually updated during this "page", + * but what it is more important is the actual elapsed time. This is to allow other + * processes to get processing time and to eliminate possible lagging. + */ + if ( i % MINE_RESET__PAGE_TIMEOUT_CHECK__BLOCK_COUNT == 0 ) { + elapsed = System.currentTimeMillis() - start; + if ( elapsed > MINE_RESET__MAX_PAGE_ELASPSED_TIME_MS ) { + + break; + } + } + } + + blocksPlaced = i - getResetPosition(); + + if ( PrisonMines.getInstance().getMineManager().isMineStats() ) { + + // Only print these details if stats is enabled: + Output.get().logInfo( "MineReset.resetAsynchonouslyUpdate() :" + + " page " + getResetPage() + + " blocks = " + blocksPlaced + " elapsed = " + elapsed ); + } + + setResetPosition( i ); + + setResetPage( getResetPage() + 1 ); + + long time = System.currentTimeMillis() - start; + setStatsBlockUpdateTimeMS( time + getStatsBlockUpdateTimeMS() ); + setStatsResetTimeMS( time + getStatsResetTimeMS() ); + + + setStatsResetPages( getStatsResetPages() + 1 ); + setStatsResetPageBlocks( getStatsResetPageBlocks() + blocksPlaced ); + setStatsResetPageMs( getStatsResetPageMs() + time ); + } + + } + + /** + * This should be used to submit async tasks. + * + * @param callbackAsync + */ + public void submitAsyncTask( PrisonRunnable callbackAsync ) { + Prison.get().getPlatform().getScheduler().runTaskLaterAsync( callbackAsync, 0L ); + } + + public void submitSyncTask( PrisonRunnable callbackSync ) { + Prison.get().getPlatform().getScheduler().runTaskLater( callbackSync, 0L ); + } + +// /** +// *

This function will identify how many air blocks are within a mine. +// *

+// */ +// private void refreshAirCount() { +// refreshAirCount(null); +// } + +// private void refreshAirCount(PrisonRunnable callback) { +// +// long elapsedTarget = getAirCountTimestamp() + MINE_RESET__AIR_COUNT_BASE_DELAY + +// (getAirCountElapsedTimeMs() * MINE_RESET__AIR_COUNT_BASE_DELAY / 100); +// +// if ( getAirCountTimestamp() == 0L || +// (elapsedTarget <= System.currentTimeMillis() )) { +// +// MineCountAirBlocksAsyncTask cabAsyncTask = new MineCountAirBlocksAsyncTask(this, callback); +// +// submitSyncTask( cabAsyncTask ); +// +// // Cannot run this async +// //submitAsyncTask( cabAsyncTask ); +// +//// Prison.get().getPlatform().getScheduler().runTaskLaterAsync( cabAsyncTask, 0L ); +// +// // Do not run this here, it must be ran as an async task... +//// refreshAirCountAsyncTask(); +// } +// } + + /** + * This task should be ran upon loading of the mines upon server start. + * This will calculate how many air blocks there are within the mine, which + * will be what the blockBreakCount should be set to initially. + * + * But as a warning, this may trigger stack traces if there are active + * entities in the unloaded chunks. May have run this synchronously. :( + */ + public void refreshBlockBreakCountUponStartup() { + + // if the mine is being used in a unit test, then it will not have a value for + // bounds and therefore do not run the task. + if ( getBounds() != null ) { + + OnStartupRefreshBlockBreakCountAsyncTask cabAsyncTask = new OnStartupRefreshBlockBreakCountAsyncTask(this); + + // Must run synchronously!! + submitSyncTask( cabAsyncTask ); + //submitAsyncTask( cabAsyncTask ); + } + } + + /** + *

This function performs the air count and should be ran as an async task. + *

+ * + *

WARNING: This generates a ton of async failures upon startup or during other + * times. This MUST be ran synchronously... + *

+ * + */ + protected void refreshAirCountAsyncTask() + { + boolean useNewBlockModel = Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ); + + if ( !isEnabled() ) { + Output.get().logError( + String.format( "MineReset: refreshAirCountAsyncTask failure: Mine is not enabled. " + + "Ensure world exists. mine= %s ", + getName() )); + } + else if ( useNewBlockModel && + getPrisonBlocks().size() == 1 && + getPrisonBlocks().get( 0 ).getBlockName().equalsIgnoreCase( InternalBlockTypes.IGNORE.name() ) && + getPrisonBlocks().get( 0 ).getChance() == 100.0 ) { + + // This mine is set to ignore all blocks when trying to do a reset, + // so for now ignore the types and just set air count to zero. + // Basically, this mine, if using natural spawned landscape, may contain blocks that are + // not registered and tracked within prison, and hence will report incorrect errors. + setAirCount( 0 ); + } + else if ( !useNewBlockModel && + getBlocks().size() == 1 && + getBlocks().get( 0 ).getType() == BlockType.IGNORE && + getBlocks().get( 0 ).getChance() == 100.0 ) { + + // This mine is set to ignore all blocks when trying to do a reset, + // so for now ignore the types and just set air count to zero. + // Basically, this mine, if using natural spawned landscape, may contain blocks that are + // not registered and tracked within prison, and hence will report incorrect errors. + setAirCount( 0 ); + } + else { + long start = System.currentTimeMillis(); + Optional worldOptional = getWorld(); + World world = worldOptional.get(); + + + int airCount = 0; + int errorCount = 0; + StringBuilder sb = new StringBuilder(); + + for (int y = getBounds().getyBlockMax(); y >= getBounds().getyBlockMin(); y--) { + for (int x = getBounds().getxBlockMin(); x <= getBounds().getxBlockMax(); x++) { + for (int z = getBounds().getzBlockMin(); z <= getBounds().getzBlockMax(); z++) { + + try { + Location targetBlock = new Location(world, x, y, z); + + if ( useNewBlockModel ) { + + if ( targetBlock.getBlockAt().getPrisonBlock().getBlockName().equalsIgnoreCase( "AIR" ) ) { + airCount++; + } + } + else { + + if ( targetBlock.getBlockAt().getType() == BlockType.AIR ) { + airCount++; + } + } + } + catch ( Exception e ) { + // Updates to the "world" should never be ran async. Upon review of the above + // that gets the location and block, causes the chunk to load, if it is not loaded, + // and if there is an entity in that loaded chunk it will throw an exception: + // java.lang.IllegalStateException: Asynchronous entity world add! + // If there are no entities, it will be fine, but they could cause issues with async + // access of unloaded chunks. + String coords = String.format( "%d.%d.%d ", x, y, z ); + if ( errorCount ++ == 0 ) { + String message = String.format( + "MineReset.refreshAirCountAsyncTask: Error counting air blocks: " + + "Mine=%s coords=%s Error: %s ", getName(), coords, e.getMessage() ); + if ( e.getMessage().contains( "Asynchronous entity world add" )) { + Output.get().logWarn( message ); + } else { + Output.get().logWarn( message, e ); + } + + } + else if ( errorCount <= 20 ) { + sb.append( coords ); + } + } + } + } + } + + if ( errorCount > 0 ) { + String message = String.format( + "MineReset.refreshAirCountAsyncTask: Error counting air blocks: Mine=%s: " + + "errorCount=%d blocks%s : %s", getName(), errorCount, + (errorCount > 20 ? "(first 20)" : ""), + sb.toString() ); + Output.get().logWarn( message ); + } + + + setAirCount( airCount ); + + long stop = System.currentTimeMillis(); + long elapsed = stop - start; + setAirCountElapsedTimeMs( elapsed ); + setAirCountTimestamp( stop ); + } + + } + + public int getRemainingBlockCount() { + int remainingBlocks = getBounds().getTotalBlockCount() - getBlockBreakCount(); +// int remainingBlocks = getBounds().getTotalBlockCount() - getAirCount(); + return remainingBlocks; + } + + public double getPercentRemainingBlockCount() { + int totalCount = getBounds().getTotalBlockCount(); + int remainingCount = getRemainingBlockCount(); + double percentRemaining = (totalCount == 0d ? 0d : (remainingCount * 100d) / (double) totalCount); +// double remainingBlocksP = (totalCount - getAirCount()) * 100d; +// double originalCount = totalCount - getAirCountOriginal(); +// double percentRemaining = (originalCount == 0d ? 0d : remainingBlocksP / originalCount); + return percentRemaining; + } + + /** + *

This assumes the air block count was just updated. + *

+ * + */ + public void refreshMineAsyncTask() { + if ( isSkipResetEnabled() ) { + + if ( getAirCountOriginal() == getAirCount() ) { + // No blocks mined. Skip and do not increment bypass count since mine is pristine. + // Skip Reset!! + return; + } + else if ( getPercentRemainingBlockCount() > getSkipResetPercent() ) { + // Blocks have been mined, but not enough to reset the mine yet. + // Increment skip bypass count. + setSkipResetBypassCount( getSkipResetBypassCount() + 1 ); + + if ( getSkipResetBypassCount() < getSkipResetBypassLimit() ) { + // Skip Reset!! + return; + } + + // Reached bypass limit. Must reset! + setSkipResetBypassCount( 0 ); + } + + // Must reset. Not bypassing. + } + + // Mine reset here: + // Async if possible... + + resetAsynchonously(); + } + + public void refreshMineAsyncResubmitTask() { + + // Mine reset here: + + resetAsynchonously(); + } + + + /** + * Generates blocks for the specified mine and caches the result. + * + * The random chance is now calculated upon a double instead of integer. + * + * @param mine the mine to randomize + */ + private void generateBlockList() { + long start = System.currentTimeMillis(); + + Random random = new Random(); + + + getRandomizedBlocks().clear(); + + for (int i = 0; i < getBounds().getTotalBlockCount(); i++) { + BlockType blockType = randomlySelectBlock( random ); + getRandomizedBlocks().add(blockType); + } + long stop = System.currentTimeMillis(); + + setStatsBlockGenTimeMS( stop - start ); + +// Output.get().logInfo("&cMine reset: " + getName() + " generated " + getBounds().getTotalBlockCount() + +// " blocks in " + getStatsBlockGenTimeMS() + " ms"); + } + + + private PrisonBlock randomlySelectPrisonBlock( Random random ) + { + double chance = random.nextDouble() * 100.0d; + + PrisonBlock prisonBlock = Prison.get().getPlatform().getPrisonBlock( "AIR" ); + for (PrisonBlock block : getPrisonBlocks()) { + if (chance <= block.getChance()) { + prisonBlock = block; + break; + } else { + chance -= block.getChance(); + } + } + return prisonBlock; + } + + private BlockType randomlySelectBlock( Random random ) + { + double chance = random.nextDouble() * 100.0d; + + BlockType value = BlockType.AIR; + for (Block block : getBlocks()) { + if (chance <= block.getChance()) { + value = block.getType(); + break; + } else { + chance -= block.getChance(); + } + } + return value; + } + + + private void broadcastResetMessageToAllPlayersWithRadius() { + long start = System.currentTimeMillis(); + + if ( getNotificationMode() != MineNotificationMode.disabled ) { + World world = getBounds().getCenter().getWorld(); + + if ( world != null ) { + List players = (world.getPlayers() != null ? world.getPlayers() : + Prison.get().getPlatform().getOnlinePlayers()); + for (Player player : players) { + + // Check for either mode: Within the mine, or by radius from mines center: + if ( getNotificationMode() == MineNotificationMode.within && + getBounds().withinIncludeTopOfMine(player.getLocation() ) || + getNotificationMode() == MineNotificationMode.radius && + getBounds().within(player.getLocation(), getNotificationRadius()) ) { + + if ( !isUseNotificationPermission() || + isUseNotificationPermission() && + player.hasPermission( getMineNotificationPermissionName() ) ) { + + + PrisonMines.getInstance().getMinesMessages() + .getLocalizable("reset_message").withReplacements( getName() ) + .sendTo(player); + +// player.sendMessage( "The mine " + getName() + " has just reset." ); + } + } + } + + } + + } + + long stop = System.currentTimeMillis(); + + setStatsMessageBroadcastTimeMS( stop - start ); + } + + protected void broadcastPendingResetMessageToAllPlayersWithRadius(MineJob mineJob) { + if ( getNotificationMode() != MineNotificationMode.disabled ) { + World world = getBounds().getCenter().getWorld(); + + if ( world != null ) { + List players = (world.getPlayers() != null ? world.getPlayers() : + Prison.get().getPlatform().getOnlinePlayers()); + for (Player player : players) { + // Check for either mode: Within the mine, or by radius from mines center: + if ( getNotificationMode() == MineNotificationMode.within && + getBounds().withinIncludeTopOfMine(player.getLocation() ) || + getNotificationMode() == MineNotificationMode.radius && + getBounds().within(player.getLocation(), getNotificationRadius()) ) { + + if ( !isUseNotificationPermission() || + isUseNotificationPermission() && + player.hasPermission( getMineNotificationPermissionName() ) ) { + + PrisonMines.getInstance().getMinesMessages() + .getLocalizable("reset_warning") + .withReplacements( getName(), + Text.getTimeUntilString(Math.round(mineJob.getResetInSec() * 1000.0d)) ) + .sendTo(player); + +// player.sendMessage( "The mine " + getName() + " will reset in " + +// Text.getTimeUntilString(mineJob.getResetInSec() * 1000) ); + } + + + } + } + + } + } + } + + @Deprecated + public List getRandomizedBlocks() + { + return randomizedBlocks; + } + @Deprecated + public void setRandomizedBlocks( List randomizedBlocks ) + { + this.randomizedBlocks = randomizedBlocks; + } + + public List getMineTargetBlocks() + { + return mineTargetBlocks; + } + public void setMineTargetBlocks( List mineTargetBlocks ) + { + this.mineTargetBlocks = mineTargetBlocks; + } + + public TreeMap getMineTargetBlocksMap() + { + return mineTargetBlocksMap; + } + public void setMineTargetBlocksMap( TreeMap mineTargetBlocksMap ) + { + this.mineTargetBlocksMap = mineTargetBlocksMap; + } + + +// public boolean[] getMineAirBlocksOriginal() +// { +// return mineAirBlocksOriginal; +// } +// public void setMineAirBlocksOriginal( boolean[] mineAirBlocksOriginal ) +// { +// this.mineAirBlocksOriginal = mineAirBlocksOriginal; +// } +// +// public boolean[] getMineAirBlocksCurrent() +// { +// return mineAirBlocksCurrent; +// } +// public void setMineAirBlocksCurrent( boolean[] mineAirBlocksCurrent ) +// { +// this.mineAirBlocksCurrent = mineAirBlocksCurrent; +// } + + public int getResetPage() + { + return resetPage; + } + public void setResetPage( int resetPage ) + { + this.resetPage = resetPage; + } + + public int getResetPosition() + { + return resetPosition; + } + public void setResetPosition( int resetPosition ) + { + this.resetPosition = resetPosition; + } + + public int getAirCountOriginal() + { + return airCountOriginal; + } + public void setAirCountOriginal( int airCountOriginal ) + { + this.airCountOriginal = airCountOriginal; + } + + public int getAirCount() + { + return airCount; + } + public void setAirCount( int airCount ) + { + this.airCount = airCount; + } + + public long getAirCountTimestamp() + { + return airCountTimestamp; + } + public void setAirCountTimestamp( long airCountTimestamp ) + { + this.airCountTimestamp = airCountTimestamp; + } + + public long getAirCountElapsedTimeMs() + { + return airCountElapsedTimeMs; + } + public void setAirCountElapsedTimeMs( long airCountElapsedTimeMs ) + { + this.airCountElapsedTimeMs = airCountElapsedTimeMs; + } + + public int addBlockBreakCount( int blockCount ) { + return blockBreakCount += blockCount; + } + public int incrementBlockBreakCount() { + return ++blockBreakCount; + } + public int getBlockBreakCount() { + return blockBreakCount; + } + public void setBlockBreakCount( int blockBreakCount ) { + this.blockBreakCount = blockBreakCount; + } + + public long getStatsResetTimeMS() + { + return statsResetTimeMS; + } + public void setStatsResetTimeMS( long statsResetTimeMS ) + { + this.statsResetTimeMS = statsResetTimeMS; + } + + public long getStatsBlockGenTimeMS() + { + return statsBlockGenTimeMS; + } + public void setStatsBlockGenTimeMS( long statsBlockGenTimeMS ) + { + this.statsBlockGenTimeMS = statsBlockGenTimeMS; + } + + public long getStatsBlockUpdateTimeMS() + { + return statsBlockUpdateTimeMS; + } + public void setStatsBlockUpdateTimeMS( long statsBlockUpdateTimeMS ) + { + this.statsBlockUpdateTimeMS = statsBlockUpdateTimeMS; + } + + public long getStatsTeleport1TimeMS() + { + return statsTeleport1TimeMS; + } + public void setStatsTeleport1TimeMS( long statsTeleport1TimeMS ) + { + this.statsTeleport1TimeMS = statsTeleport1TimeMS; + } + + public long getStatsTeleport2TimeMS() + { + return statsTeleport2TimeMS; + } + public void setStatsTeleport2TimeMS( long statsTeleport2TimeMS ) + { + this.statsTeleport2TimeMS = statsTeleport2TimeMS; + } + + public long getStatsMessageBroadcastTimeMS() + { + return statsMessageBroadcastTimeMS; + } + public void setStatsMessageBroadcastTimeMS( long statsMessageBroadcastTimeMS ) + { + this.statsMessageBroadcastTimeMS = statsMessageBroadcastTimeMS; + } + + public int getStatsResetPages() { + return statsResetPages; + } + public void setStatsResetPages( int statsResetPages ) { + this.statsResetPages = statsResetPages; + } + + public long getStatsResetPageBlocks() { + return statsResetPageBlocks; + } + public void setStatsResetPageBlocks( long statsResetPageBlocks ) { + this.statsResetPageBlocks = statsResetPageBlocks; + } + + public long getStatsResetPageMs() { + return statsResetPageMs; + } + public void setStatsResetPageMs( long statsResetPageMs ) { + this.statsResetPageMs = statsResetPageMs; + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncResubmitTask.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncResubmitTask.java new file mode 100644 index 000000000..c00e1d0a9 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncResubmitTask.java @@ -0,0 +1,23 @@ +package tech.mcprison.prison.mines.data; + +public class MineResetAsyncResubmitTask + implements PrisonRunnable +{ + + private MineReset mine; + private PrisonRunnable callbackAsync; + + public MineResetAsyncResubmitTask(MineReset mine, PrisonRunnable callbackAsync) { + this.mine = mine; + this.callbackAsync = callbackAsync; + } + + @Override + public void run() { + this.mine.refreshMineAsyncResubmitTask(); + + if ( this.callbackAsync != null ) { + this.mine.submitAsyncTask( callbackAsync ); + } + } +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncTask.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncTask.java new file mode 100644 index 000000000..bdb0c88bb --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineResetAsyncTask.java @@ -0,0 +1,23 @@ +package tech.mcprison.prison.mines.data; + +public class MineResetAsyncTask + implements PrisonRunnable { + + private MineReset mine; + private PrisonRunnable callbackAsync; + + public MineResetAsyncTask(MineReset mine, PrisonRunnable callbackAsync) { + this.mine = mine; + this.callbackAsync = callbackAsync; + } + + @Override + public void run() { + this.mine.refreshMineAsyncTask(); + + if ( this.callbackAsync != null ) { + this.mine.submitAsyncTask( callbackAsync ); + } + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineScheduler.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineScheduler.java new file mode 100644 index 000000000..bbc130403 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineScheduler.java @@ -0,0 +1,529 @@ +package tech.mcprison.prison.mines.data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Stack; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.output.Output; + +public abstract class MineScheduler + extends MineReset + implements PrisonRunnable +{ + + /** + *

The jobWorkflow defines the various steps to the workflow that + * is related to the mine reset process. There will always be a RESET + * action and it will always be the last item in the workflow. If there + * are other steps in the workflow they will be MESSAGEs that are to + * be sent out a different intervals. + *

+ * + *

The jobWorkflow is built only one time and it regenerates the jobStack + * once a workflow cycle has been completed. + *

+ */ + private List jobWorkflow; + private Stack jobStack; + private MineJob currentJob; + private Integer taskId = null; + + public MineScheduler() { + super(); + + this.jobWorkflow = new ArrayList<>(); + this.jobStack = new Stack<>(); + this.currentJob = null; + } + + /** + *

This initialize function gets called after the classes are + * instantiated, and is initiated from Mine class and propagates + * to the MineData class. Good for kicking off the scheduler. + *

+ */ + @Override + protected void initialize() { + super.initialize(); + + // need to rebuild JobWorkflow if reset time ever changes: + setJobWorkflow( initializeJobWorkflow() ); + resetJobStack(); + } + + public enum JobType { + SYNC, + ASYNC + ; + } + + public enum MineJobAction { + MESSAGE_GATHER( JobType.ASYNC ), + MESSAGE( JobType.SYNC ), + + RESET_BUILD_BLOCKS_ASYNC( JobType.ASYNC ), + RESET_ASYNC( JobType.SYNC ), + RESET_SYNC( JobType.SYNC ); + + private final JobType jobType; + private MineJobAction(JobType jobType) { + this.jobType = jobType; + } + + public JobType getJobType() + { + return jobType; + } + } + + public enum MineResetType { + NORMAL, + FORCED; + } + + /** + *

This class represents a workflow action. The action can be one of either MESSAGE, or + * RESET. The delayActionSec is how many seconds the job must wait until taking action. + * When it does take action, such as sending of a message, the resetInSec should be how + * many seconds in to the future will be the reset. + *

+ * + *

Please note that the value of resetInSec is the time from when the job starts to run + * until the mine should reset. To find out when the mine should reset when submitting + * this job, add both resetInSec and delayActionSec to get the estimate reset time. + *

+ * + */ + public class MineJob + { + private MineJobAction action; + private double delayActionSec; + private double resetInSec; + private MineResetType resetType; + + public MineJob( MineJobAction action, double delayActionSec, double resetInSec ) + { + super(); + + this.action = action; + this.delayActionSec = delayActionSec; + this.resetInSec = resetInSec; + + this.resetType = MineResetType.NORMAL; + } + + public double getJobSubmitResetInSec() { + return getResetInSec() + getDelayActionSec(); + } + + @Override + public String toString() { + return "Action: " + getAction().name() + + " Reset at submit: " + getJobSubmitResetInSec() + + " Delay before running: " + getDelayActionSec() + + " Reset at run: " + getResetInSec(); + } + + public MineJobAction getAction() + { + return action; + } + public void setAction( MineJobAction action ) + { + this.action = action; + } + + public double getDelayActionSec() + { + return delayActionSec; + } + public void setDelayActionSec( double delayActionSec ) + { + this.delayActionSec = delayActionSec; + } + + public double getResetInSec() + { + return resetInSec; + } + public void setResetInSec( double resetInSec ) + { + this.resetInSec = resetInSec; + } + + public MineResetType getResetType() + { + return resetType; + } + public void setResetType( MineResetType resetType ) + { + this.resetType = resetType; + } + + + } + + + + + private List initializeJobWorkflow() + { + if ( PrisonMines.getInstance() != null ) { + MinesConfig config = PrisonMines.getInstance().getConfig(); + return initializeJobWorkflow(getResetTime(), config.resetMessages, config.resetWarningTimes ); + } + + // The following is used within jUnit tests: + return new ArrayList<>(); + } + + /** + *

Added parameters so this can be unit tested to ensure it's working correctly. + *

+ * + *

With reset warning times of: 59, 121, and 313. And a resetTime of 10 minutes, + * then the following is expected with all of them adding up to 15 minutes: + *

+ *
    + *
  • MESSAGE, 287 (600 until reset)
  • + *
  • MESSAGE, 192 (313 until reset)
  • + *
  • MESSAGE, 62 (121 until reset)
  • + *
  • RESET, 59 (59 until reset)
  • + *
+ * + * @param resetTime + * @param includeMessages + * @param rwTimes + * @return + */ + protected List initializeJobWorkflow( double resetTime, boolean includeMessages, ArrayList rwTimes ) + { + List workflow = new ArrayList<>(); + + // Determine if the sync or async reset action should be used for this workflow. + MineJobAction resetAction = isUsePagingOnReset() ? MineJobAction.RESET_ASYNC : MineJobAction.RESET_SYNC; + + if ( includeMessages ) { + // Need to ensure that the reset warning times are sorted in ascending order: +// ArrayList rwTimes = PrisonMines.getInstance().getConfig().resetWarningTimes; + Collections.sort( rwTimes ); + + double total = 0; + for ( Integer time : rwTimes ) { + if ( time < resetTime ) { + // if reset time is less than warning time, then skip warning: + double elapsed = time - total; + workflow.add( + new MineJob( workflow.size() == 0 ? resetAction : MineJobAction.MESSAGE, + elapsed, total) ); + total += elapsed; + } + } + workflow.add( + new MineJob( workflow.size() == 0 ? resetAction : MineJobAction.MESSAGE, + (resetTime - total), total) ); + + } else { + // Exclude all messages. Only reset mine: + workflow.add( new MineJob( resetAction, resetTime, 0) ); + } + + return workflow; + } + + + /** + *

Reset the job stack, and if the reset time has changed, then rebuild the + * whole workflow to account for the new reset time. + *

+ */ + private void resetJobStack() { + getJobStack().clear(); + + double oldResetTime = getJobWorkflow() == null || getJobWorkflow().size() == 0 ? 0.0d : + getJobWorkflow().get( 0 ).getJobSubmitResetInSec(); + + if ( oldResetTime == 0.0d || oldResetTime != getResetTime() ) { + // need to rebuild JobWorkflow if reset time ever changes: + setJobWorkflow( initializeJobWorkflow() ); + } + + getJobStack().addAll( getJobWorkflow() ); + } + + /** + * Calculate if the reset should be skipped. If it should, increment the skip count + * at the end, and skip the generation of the next block list and the actual resets. + */ + @Override + public void run() + { + // TODO track how many times the world fails to load? Then terminate the mine job if it + // appears like it will never load? + //checkWorld(); + + boolean forced = getCurrentJob() != null && + getCurrentJob().getResetType() == MineResetType.FORCED; + + boolean skip = !forced && + isSkipResetEnabled() && + getPercentRemainingBlockCount() >= getSkipResetPercent() && + getSkipResetBypassCount() < getSkipResetBypassLimit(); + +// Output.get().logInfo( "Mine Reset: Run: Mine= %s action= %s skip= %s forced= %s ", +// this.getName(), getCurrentJob().getAction().name(), +// Boolean.valueOf( skip ).toString(), Boolean.valueOf( forced ).toString() ); + + switch ( getCurrentJob().getAction() ) + { + case MESSAGE_GATHER: + // Not yet implemented: let MESSAGE process this request: + + // The idea here is to check how far all players are from the mine. If they are + // candidates to get a message, then collect a list of players async. This + // should be safe to run, since the active players would already have the + // chunks where they are already loaded so as to prevent corruption if + // this process "forces" an unloaded chunk to load. + + + case MESSAGE: + // Send reset message: + broadcastPendingResetMessageToAllPlayersWithRadius(getCurrentJob() ); + + break; + + case RESET_BUILD_BLOCKS_ASYNC: + if ( !skip ) { + generateBlockListAsync(); + } + + break; + + case RESET_ASYNC: + if ( !skip ) { + resetAsynchonously(); + } else { + incrementSkipResetBypassCount(); + } + + break; + + case RESET_SYNC: + // synchronous reset. Will be phased out in the future? + if ( !skip ) { + resetSynchonously(); + } else { + incrementSkipResetBypassCount(); + } + + break; + + default: + break; + } + +// if ( getCurrentJob().getAction() == MineJobAction.RESET ) { +// resetSynchonously(); +// } else { +// // Send reset message: +// broadcastPendingResetMessageToAllPlayersWithRadius(getCurrentJob(), MINE_RESET_BROADCAST_RADIUS_BLOCKS ); +// } +// + + submitNextAction(); + } + + /** + *

This checks to see if the world stored for the mine is null, if it is, it + * tries to reload it the platform (spigot?) and then it updates all world instances + * within the Bound object for the mine, including spawn. + *

+ * + */ + @SuppressWarnings( "unused" ) + private void checkWorld() + { + if ( !isEnabled() ) { + // Must try to load world again: + Optional worldOptional = Prison.get().getPlatform().getWorld( getWorldName() ); + if (!worldOptional.isPresent()) { + Output.get().logWarn( "&7MineScheduler.checkWorld: World STILL doesn't exist. " + + "This is serious. &aworldName= " + getWorldName() ); + } + else { + World world = worldOptional.get(); + + setWorld( world ); + } + } + } + + /** + *

This task performs the job submission. If the currentJob is null, then it will generate an + * exception in the console. Manually resetting the mine will resubmit the workflow. + *

+ * + *

At the beginning of every submission, it will update the targetResetTime associated with + * this mine. This is important since heavy work loads could result in delays that will + * push the actual reset back. This is a way to update the estimated target time. + *

+ */ + private void submitTask() { + if ( getCurrentJob() != null ) { + // Need to set the targetRestTime when the job is first submitted since that is the ideal time: + long targetResetTime = System.currentTimeMillis() + + Math.round(getCurrentJob().getJobSubmitResetInSec() * 1000.0d); + setTargetResetTime( targetResetTime ); + + long ticksToWait = Math.round( getCurrentJob().getDelayActionSec() * 20.0d); + + + // Submit currentJob using delay in the job. Must be a one time run, no repeats. + int taskId = Prison.get().getPlatform().getScheduler().runTaskLater(this, ticksToWait); + setTaskId( taskId ); + } else { + Output.get().logError("Mine " + getName() + + " failed to resubmit itself so it will not auto reset. Manually reset " + + "this mine to re-enable the auto reset."); + } + } + + + /** + *

Terminate this job to remove it from running, which will also allow the mine to be + * garbage collected if removing the mine. + *

+ * + */ + public void terminateJob() { + getJobStack().clear(); + + int taskId = getTaskId(); + + Prison.get().getPlatform().getScheduler().cancelTask( taskId ); + } + + public void submit( int offset ) { + submitNextAction(offset); + } + private void submitNextAction() { + submitNextAction(0); + } + private void submitNextAction(int offset) { + if ( getJobStack().size() == 0 ) { + resetJobStack(); + } + + setCurrentJob( getJobStack().pop() ); + + // Offset tries to stagger the mine resets, assuming most will have the same delays: + if ( offset > 0 ) { + getCurrentJob().setDelayActionSec( getCurrentJob().getDelayActionSec() + offset ); + } + + // Submit currentJob using delay in the job. Must be a one time run, no repeats. + submitTask(); + } + + /** + * This is called by the MineCommand.resetCommand() function, which is + * triggered by a player. + * + */ + public void manualReset() { + manualReset( MineResetType.FORCED, 0 ); + } + + + public boolean checkZeroBlockReset() { + boolean reset = false; + + // Reset if the mine runs out of blocks: + + if ( getRemainingBlockCount() == 0 && !isZeroBlockResetDisabled() || + getResetThresholdPercent() > 0 && + getRemainingBlockCount() < (getBounds().getTotalBlockCount() * + getResetThresholdPercent() / 100.0d)) { + + // submit a manual reset since the mine is empty: + manualReset( MineResetType.NORMAL, getZeroBlockResetDelaySec() ); + reset = true; + } + return reset; + } + + /** + *

This function should only be called from the commands to manually force a mine to reset. + * How this should work, is it should cancel (remove) the scheduled reset for this mine, then + * run the actual reset, with the intentions of resubmitting the whole workflow from the + * beginning. A manual reset not only resets the mine, but it resets the workflow schedule + * from the beginning. + *

+ * @param resetType + * @param delayActionSec Delay in seconds before resetting mine. + * + */ + private void manualReset( MineResetType resetType, double delayActionSec ) { + // cancel existing job: + if ( getTaskId() != null ) { + Prison.get().getPlatform().getScheduler().cancelTask( getTaskId() ); + } + + // Clear jobStack and set currentJob to run the RESET with zero delay: + getJobStack().clear(); + + MineJobAction action = isUsePagingOnReset() ? + MineJobAction.RESET_ASYNC : MineJobAction.RESET_SYNC; + + MineJob mineJob = new MineJob( action, delayActionSec, 0); + mineJob.setResetType( resetType ); + setCurrentJob( mineJob ); + + // Force reset even if skip is enabled: + + // Submit to run: + submitTask(); + } + + public List getJobWorkflow() + { + return jobWorkflow; + } + public void setJobWorkflow( List jobWorkflow ) + { + this.jobWorkflow = jobWorkflow; + } + + public Stack getJobStack() + { + return jobStack; + } + public void setJobStack( Stack jobStack ) + { + this.jobStack = jobStack; + } + + public MineJob getCurrentJob() + { + return currentJob; + } + public void setCurrentJob( MineJob currentJob ) + { + this.currentJob = currentJob; + } + + public Integer getTaskId() + { + return taskId; + } + public void setTaskId( Integer taskId ) + { + this.taskId = taskId; + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlock.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlock.java new file mode 100644 index 000000000..6753b79c5 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlock.java @@ -0,0 +1,35 @@ +package tech.mcprison.prison.mines.data; + +import tech.mcprison.prison.util.BlockType; + +public class MineTargetBlock +{ + private MineTargetBlockKey blockKey; + + private BlockType blockType; + + public MineTargetBlock( BlockType blockType, int x, int y, int z ) { + super(); + + this.blockKey = new MineTargetBlockKey( x, y, z ); + this.blockType = blockType; + } + + public BlockType getBlockType() + { + return blockType; + } + public void setBlockType( BlockType blockType ) + { + this.blockType = blockType; + } + + public MineTargetBlockKey getBlockKey() + { + return blockKey; + } + public void setBlockKey( MineTargetBlockKey blockKey ) + { + this.blockKey = blockKey; + } +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlockKey.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlockKey.java new file mode 100644 index 000000000..2c55e1308 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MineTargetBlockKey.java @@ -0,0 +1,50 @@ +package tech.mcprison.prison.mines.data; + +public class MineTargetBlockKey + implements Comparable +{ + private final int x, y, z; + + public MineTargetBlockKey( int x, int y, int z ) { + super(); + + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } + + public int getZ() + { + return z; + } + + @Override + public int compareTo( Object arg0 ) { + if ( !(arg0 instanceof MineTargetBlockKey) ) { + return -1; + } + MineTargetBlockKey key = (MineTargetBlockKey) arg0; + + int result = key.getX() - getX(); + + if ( result == 0 ) { + result = key.getY() - getY(); + + if ( result == 0 ) { + result = key.getZ() - getZ(); + } + } + + return result; + } +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MinesConfig.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MinesConfig.java index 81fd0d18e..1f10b04a5 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MinesConfig.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/MinesConfig.java @@ -29,23 +29,23 @@ */ public class MinesConfig implements FileIOData { - /** - * True if randomized blocks for mines should be cached for faster resets. False otherwise - */ - public boolean asyncReset = true; +// /** +// * True if randomized blocks for mines should be cached for faster resets. False otherwise +// */ +// public boolean asyncReset = true; /** * True if reset warnings an reset broadcasts should be enabled. False otherwise */ public boolean resetMessages = true; - /** - * True if broadcasts should only be enabled in the worlds specified in the worlds list. - * False otherwise. - * - * @see MinesConfig#worlds - */ - public boolean multiworld = false; +// /** +// * True if broadcasts should only be enabled in the worlds specified in the worlds list. +// * False otherwise. +// * +// * @see MinesConfig#worlds +// */ +// public boolean multiworld = false; /** * True if only blocks that are air should be replaced. False otherwise @@ -55,15 +55,15 @@ public class MinesConfig implements FileIOData { /** * The duration between mine resets in seconds. */ - public int resetTime = 600; + public int resetTime = MineData.MINE_RESET__TIME_SEC__DEFAULT; - /** - * The worlds that reset messages should be broadcasted to. Ignored if multiworld is disabled. - * - * @see MinesConfig#multiworld - */ - public ArrayList worlds = - new ArrayList<>(Arrays.asList(new String[]{"plots", "mines"})); +// /** +// * The worlds that reset messages should be broadcasted to. Ignored if multiworld is disabled. +// * +// * @see MinesConfig#multiworld +// */ +// public ArrayList worlds = +// new ArrayList<>(Arrays.asList(new String[]{"plots", "mines"})); /** * The time between mine reset warnings. Ignored if resetMessages is disabled. diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/OnStartupRefreshBlockBreakCountAsyncTask.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/OnStartupRefreshBlockBreakCountAsyncTask.java new file mode 100644 index 000000000..3f725b52e --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/OnStartupRefreshBlockBreakCountAsyncTask.java @@ -0,0 +1,19 @@ +package tech.mcprison.prison.mines.data; + +public class OnStartupRefreshBlockBreakCountAsyncTask + implements PrisonRunnable { + + private MineReset mine; + + public OnStartupRefreshBlockBreakCountAsyncTask(MineReset mine) { + this.mine = mine; + } + + @Override + public void run() { + this.mine.refreshAirCountAsyncTask(); + + int airBlocks = mine.getAirCount(); + mine.setBlockBreakCount( mine.getBlockBreakCount() + airBlocks ); + } +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonRunnable.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonRunnable.java new file mode 100644 index 000000000..de33e22a8 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonRunnable.java @@ -0,0 +1,7 @@ +package tech.mcprison.prison.mines.data; + +public interface PrisonRunnable + extends Runnable +{ + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonSortableMines.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonSortableMines.java new file mode 100644 index 000000000..a2a493948 --- /dev/null +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/data/PrisonSortableMines.java @@ -0,0 +1,48 @@ +package tech.mcprison.prison.mines.data; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.sorting.PrisonSorter; + +public class PrisonSortableMines + extends PrisonSorter { + + public class PrisonSortComparableMines + implements Comparator { + + @Override + public int compare( Mine m1, Mine m2 ) + { + int results = 0; + + if ( m1 == null ) { + results = -1; + } + else if ( m2 == null ) { + results = 1; + } + else { + results = m1.getName().toLowerCase().compareTo( m2.getName().toLowerCase() ); + } + + return results; + } + + } + + + @Override + public Set getSortedSet() { + TreeSet mines = new TreeSet<>( new PrisonSortComparableMines() ); + + List unsortedMines = PrisonMines.getInstance().getMineManager().getMines(); + + mines.addAll( unsortedMines ); + return mines; + } + +} diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/events/MineResetEvent.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/events/MineResetEvent.java index d5ebaa6f5..7f248f954 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/events/MineResetEvent.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/events/MineResetEvent.java @@ -19,17 +19,17 @@ package tech.mcprison.prison.mines.events; import tech.mcprison.prison.internal.events.Cancelable; -import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.mines.data.MineReset; /** * Represents an event called when a mine is being reset */ public class MineResetEvent implements Cancelable { - private Mine mine; + private MineReset mine; private boolean canceled = false; // false by default - public MineResetEvent(Mine mine) { + public MineResetEvent(MineReset mine) { this.mine = mine; } @@ -38,7 +38,7 @@ public MineResetEvent(Mine mine) { * * @return the mine associated with this event */ - public Mine getMine() { + public MineReset getMine() { return mine; } diff --git a/prison-mines/src/main/java/tech/mcprison/prison/mines/managers/MineManager.java b/prison-mines/src/main/java/tech/mcprison/prison/mines/managers/MineManager.java index a9d16be4c..875340b97 100644 --- a/prison-mines/src/main/java/tech/mcprison/prison/mines/managers/MineManager.java +++ b/prison-mines/src/main/java/tech/mcprison/prison/mines/managers/MineManager.java @@ -17,63 +17,117 @@ package tech.mcprison.prison.mines.managers; -import java.io.IOException; +import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Optional; -import java.util.Random; -import java.util.TimerTask; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; import tech.mcprison.prison.Prison; +import tech.mcprison.prison.integration.IntegrationManager; +import tech.mcprison.prison.integration.IntegrationManager.PlaceHolderFlags; +import tech.mcprison.prison.integration.IntegrationManager.PrisonPlaceHolders; +import tech.mcprison.prison.integration.ManagerPlaceholders; +import tech.mcprison.prison.integration.PlaceHolderKey; import tech.mcprison.prison.internal.Player; -import tech.mcprison.prison.localization.Localizable; -import tech.mcprison.prison.mines.MineException; +import tech.mcprison.prison.internal.World; import tech.mcprison.prison.mines.PrisonMines; -import tech.mcprison.prison.mines.data.Block; import tech.mcprison.prison.mines.data.Mine; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.store.Collection; import tech.mcprison.prison.store.Document; -import tech.mcprison.prison.util.BlockType; -import tech.mcprison.prison.util.Text; /** * Manages the creation, removal, and management of mines. * * @author Dylan M. Perks */ -public class MineManager { - +public class MineManager + implements ManagerPlaceholders { + + public static final double TIME_SECOND = 1.0; + public static final double TIME_MINUTE = TIME_SECOND * 60.0; + public static final double TIME_HOUR = TIME_MINUTE * 60.0; + public static final double TIME_DAY = TIME_HOUR * 24.0; + + // Base list - List mines; + private List mines; + private TreeMap minesByName; + + private TreeMap> unavailableWorlds; - tech.mcprison.prison.store.Collection coll; + private Collection coll; - // Declarations - HashMap> randomizedBlocks; - int resetCount = 0; + private List translatedPlaceHolderKeys; + + private boolean mineStats = false; /** - * Initializes a new instance of {@link MineManager} + *

MineManager must be fully instantiated prior to trying to load the mines, + * otherwise if the mines cannot find the world they should be, they will be + * unable to register that the world is unavailable. + *

+ * */ - public MineManager(tech.mcprison.prison.store.Collection collection) { - mines = new ArrayList<>(); - randomizedBlocks = new HashMap<>(); - coll = collection; - mines = new ArrayList<>(); + public MineManager() { + this.mines = new ArrayList<>(); + this.minesByName = new TreeMap<>(); + + this.unavailableWorlds = new TreeMap<>(); + + this.coll = null; + + } + - loadMines(); + public void loadFromDbCollection( PrisonMines pMines ) { + + Optional collOptional = pMines.getDb().getCollection("mines"); - Output.get().logInfo("Loaded " + mines.size() + " mines"); - resetCount = PrisonMines.getInstance().getConfig().resetTime; - } + if (!collOptional.isPresent()) { + Output.get().logError("Could not create 'mines' collection."); + pMines.getStatus().toFailed("Could not create mines collection in storage."); + return; + } - public void loadMine(String mineFile) throws IOException, MineException { - Document document = coll.get(mineFile).orElseThrow(IOException::new); - mines.add(new Mine(document)); + this.coll = collOptional.get(); + + int offsetTiming = 5; + loadMines(offsetTiming); + + + Output.get().logInfo( String.format("Loaded %d mines and submitted with a %d " + + "second offset timing for auto resets.", + getMines().size(), offsetTiming)); + + +// // When finished loading the mines, then if there are any worlds that +// // could not be loaded, dump the details: +// List unavailableWorlds = getUnavailableWorldsListings(); +// for ( String uWorld : unavailableWorlds ) { +// Output.get().logInfo( uWorld ); +// } + + +// // Submit all the loaded mines to run: +// int offset = 0; +// for ( Mine mine : mines ) +// { +// mine.submit(offset); +// offset += 5; +// } +// Output.get().logInfo("Mines are all queued to run auto resets."); } +// public void loadMine(String mineFile) throws IOException, MineException { +// Document document = coll.get(mineFile).orElseThrow(IOException::new); +// Mine m = new Mine(document); +// add(m, false, 0); +// } + /** * Adds a {@link Mine} to this {@link MineManager} instance. * @@ -83,7 +137,7 @@ public void loadMine(String mineFile) throws IOException, MineException { * @return if the add was successful */ public boolean add(Mine mine) { - return add(mine, true); + return add(mine, true, 0); } /** @@ -96,144 +150,63 @@ public boolean add(Mine mine) { * no data has changed. * @return if the add was successful */ - public boolean add(Mine mine, boolean save) { + private boolean add(Mine mine, boolean save, int offsetTiming ) { boolean results = false; - if (!mines.contains(mine)){ + if (!getMines().contains(mine)){ if ( save ) { saveMine( mine ); } - results = mines.add(mine); + results = getMines().add(mine); + getMinesByName().put( mine.getName().toLowerCase(), mine ); + + // Start its scheduling: + mine.submit(offsetTiming); } return results; } - private void selectiveSend(Player x, Localizable localizable) { - if (PrisonMines.getInstance().getWorlds() - .contains(x.getLocation().getWorld().getName().toLowerCase())) { - localizable.sendTo(x); - } - } - - /** - * Gets the {@link TimerTask} for the reset timer of this {@link MineManager} - */ - public TimerTask getTimerTask() { - return new TimerTask() { - @Override - public void run() { - // Perform initial checks - if (PrisonMines.getInstance().getConfig().resetTime == 0) { - return; - } - if (mines.size() == 0) { - return; - } - - // It's time to reset - if (resetCount == 0) { - resetMines(); - } else { - broadcastResetWarnings(); - } - - if (resetCount > 0) { - resetCount--; - } - } - }; - } - - private void resetMines() { - mines.forEach(Mine::reset); - - if (PrisonMines.getInstance().getConfig().resetMessages) { - // Send it to everyone if it's not multi-world - if (!PrisonMines.getInstance().getConfig().multiworld) { - Prison.get().getPlatform().getOnlinePlayers().forEach( - x -> PrisonMines.getInstance().getMinesMessages() - .getLocalizable("reset_message") - .sendTo(x)); - } else { // Or those affected if it's multi-world - Prison.get().getPlatform().getOnlinePlayers().forEach(x -> selectiveSend(x, - PrisonMines.getInstance().getMinesMessages().getLocalizable("reset_message"))); - } - } - // And reset the count - resetCount = PrisonMines.getInstance().getConfig().resetTime; - } - - private void broadcastResetWarnings() { - if (!PrisonMines.getInstance().getConfig().resetMessages) { - return; - } - - for (int i : PrisonMines.getInstance().getConfig().resetWarningTimes) { - if (resetCount == i) { - if (!PrisonMines.getInstance().getConfig().multiworld) { - - Prison.get().getPlatform().getOnlinePlayers().forEach( - x -> PrisonMines.getInstance().getMinesMessages() - .getLocalizable("reset_warning") - .withReplacements(Text.getTimeUntilString(resetCount * 1000)) - .sendTo(x)); - } else { - Prison.get().getPlatform().getOnlinePlayers().forEach(x -> selectiveSend(x, - PrisonMines.getInstance().getMinesMessages().getLocalizable("reset_warning") - .withReplacements(Text.getTimeUntilString(resetCount * 1000)))); - } - } - } - } - - public boolean removeMine(String id){ - if (getMine(id).isPresent()) { - return removeMine(getMine(id).get()); - } - else{ - return false; - } + public boolean removeMine(String mineName){ + boolean results = false; + if ( mineName != null ) { + Mine mine = getMinesByName().get( mineName.toLowerCase() ); + if ( mine != null ) { + results = removeMine(mine); + } + } + + return results; } public boolean removeMine(Mine mine) { boolean success = false; if ( mine != null ) { - mines.remove(mine); - success = coll.delete( mine.getName() ); + coll.delete( mine.getName() ); + getMinesByName().remove(mine.getName()); + success = getMines().remove(mine); } return success; } - public static MineManager fromDb() { - PrisonMines pMines = PrisonMines.getInstance(); - - Optional collOptional = pMines.getDb().getCollection("mines"); - if (!collOptional.isPresent()) { - Output.get().logError("Could not create 'mines' collection."); - pMines.getStatus().toFailed("Could not create mines collection in storage."); - return null; - } - return new MineManager(collOptional.get()); - } - - private void loadMines() { + private void loadMines( int offsetTiming ) { List mineDocuments = coll.getAll(); + int offset = 0; for (Document document : mineDocuments) { try { Mine m = new Mine(document); - add(m, false); - if (PrisonMines.getInstance().getConfig().asyncReset) { - generateBlockList(m); - } + add(m, false, offset); + offset += offsetTiming; + } catch (Exception e) { Output.get() .logError("&cFailed to load mine " + document.getOrDefault("name", "null"), e); } } + } /** @@ -245,68 +218,505 @@ public void saveMine(Mine mine) { } public void saveMines(){ - for (Mine m : mines){ + for (Mine m : getMines()){ saveMine(m); } } + /** - * Generates blocks for the specified mine and caches the result. - * - * The random chance is now calculated upon a double instead of integer. + * Returns the mine with the specified name. * - * @param mine the mine to randomize + * @param name The mine's name, case-sensitive. + * @return An optional containing either the {@link Mine} if it could be found, or empty if it + * does not exist by the specified name. */ - public void generateBlockList(Mine mine) { - Random random = new Random(); - ArrayList blocks = new ArrayList<>(); + public Mine getMine(String mineName) { + return (mineName == null ? null : getMinesByName().get( mineName.toLowerCase() )); + + //return mines.stream().filter(mine -> mine.getName().equals(name)).findFirst(); + } - for (int i = 0; i < mine.getBounds().getTotalBlockCount(); i++) { - double chance = random.nextDouble() * 100.0d; - - BlockType value = BlockType.AIR; - for (Block block : mine.getBlocks()) { - if (chance <= block.getChance()) { - value = block.getType(); - break; - } else { - chance -= block.getChance(); - } - } - blocks.add(value); - } - randomizedBlocks.put(mine.getName(), blocks); + public List getMines() { + return mines; } + public TreeMap getMinesByName() { + return minesByName; + } + + public boolean isMineStats() + { + return mineStats; + } + public void setMineStats( boolean mineStats ) + { + this.mineStats = mineStats; + } + + + + /** + *

Add the missing world and the associated mine to the collection. Create the + * base entries if needed. + *

+ * + *

Upon the first entry in to this collection of worlds and mines, an error + * message will be generated indicating the world does not exist. + *

+ * + * @param worldName + * @param mine + */ + public void addUnavailableWorld( String worldName, Mine mine ) { + if ( worldName != null && worldName.trim().length() > 0 && mine != null ) { + if ( !getUnavailableWorlds().containsKey( worldName )) { + getUnavailableWorlds().put( worldName, new ArrayList<>() ); + + Output.get().logWarn( "&7Mine Loader: &aWorld does not exist! " + + "&7This maybe a temporary " + + "condition until the world can be loaded. " + + " &3worldName= " + worldName ); + } + + if ( !getUnavailableWorlds().get( worldName ).contains( mine )) { + getUnavailableWorlds().get( worldName ).add( mine ); + } + } + } + public TreeMap> getUnavailableWorlds() { + return unavailableWorlds; + } + public void setUnavailableWorlds( TreeMap> unavailableWorlds ) { + this.unavailableWorlds = unavailableWorlds; + } + + public void assignAvailableWorld( String worldName ) { + if ( worldName != null && worldName.trim().length() > 0 ) { + + Optional worldOptional = Prison.get().getPlatform().getWorld(worldName); + + if ( worldOptional.isPresent() && getUnavailableWorlds().containsKey( worldName )) { + World world = worldOptional.get(); + + // Store this mine and the world in MineManager's unavailableWorld for later + // processing and hooking up to the world object. + List unenabledMines = getUnavailableWorlds().get( worldName ); + +// List remove = new ArrayList<>(); + + for ( Mine mine : unenabledMines ) { + if ( !mine.isEnabled() ) { + mine.setWorld( world ); + } +// remove.add( mine ); + } + +// // Purge all removed mines from the unenabledMines list: +// if ( remove.size() > 0 ) { +// unenabledMines.removeAll( remove ); +// } + +// // If no mines remain, then remove this world from unavailableWorlds: +// if ( unenabledMines.size() == 0 ) { +// getUnavailableWorlds().remove( worldName ); +// } + + // Since the world is loaded and all available mines have been hooked up + // with the world, so remove these entries. + unenabledMines.clear(); + getUnavailableWorlds().remove( worldName ); + } + } + } + + public List getUnavailableWorldsListings() { + List results = new ArrayList<>(); + + if ( getUnavailableWorlds().size() > 0 ) { + results.add( "&cUnavailable Worlds: &3Deferred loading of mines." ); + + Set worlds = getUnavailableWorlds().keySet(); + + for ( String worldName : worlds ) { + int enabledCount = 0; + + List mines = getUnavailableWorlds().get( worldName ); + for ( Mine mine : mines ) { + if ( mine.isEnabled() ) { + enabledCount++; + } + } + results.add( + String.format( "&7 world: &3%s &7(&c%s &7of &c%s &7mines enabled) ", + worldName, Integer.toString( enabledCount ), + Integer.toString( mines.size() ))); + } + } + + return results; + } + + public String getTranslateMinesPlaceHolder( String identifier ) { + String results = null; + List placeHolderKeys = getTranslatedPlaceHolderKeys(); + + if ( !identifier.startsWith( IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED )) { + identifier = IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED + identifier; + } + + for ( PlaceHolderKey placeHolderKey : placeHolderKeys ) { + if ( placeHolderKey.getKey().equalsIgnoreCase( identifier )) { + + Mine mine = getMine( placeHolderKey.getData() ); + + results = getTranslateMinesPlaceHolder( placeHolderKey, mine ); + break; + } + } + + return results; + } + + public String getTranslateMinesPlaceHolder( PlaceHolderKey placeHolderKey ) { + Mine mine = getMine( placeHolderKey.getData() ); + return getTranslateMinesPlaceHolder( placeHolderKey, mine ); + } + + private String getTranslateMinesPlaceHolder( PlaceHolderKey placeHolderKey, Mine mine ) { + String results = null; + + if ( placeHolderKey != null && placeHolderKey.getData() != null ) { + + // If the mine is not provided, try to get it from the placeholder data: + if ( mine == null ) { + mine = getMine( placeHolderKey.getData() ); + } + + if ( mine != null ) { + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + DecimalFormat iFmt = new DecimalFormat("#,##0"); + + switch ( placeHolderKey.getPlaceholder() ) { + case prison_mi_minename: + case prison_mines_interval_minename: + case prison_mi_pm: + case prison_mines_interval_playermines: + results = iFmt.format( mine.getResetTime() ); + break; + + case prison_mif_minename: + case prison_mines_interval_formatted_minename: + case prison_mif_pm: + case prison_mines_interval_formatted_playermines: + double timeMif = mine.getResetTime(); + results = formattedTime( timeMif ); + break; + + case prison_mtl_minename: + case prison_mines_timeleft_minename: + case prison_mtl_pm: + case prison_mines_timeleft_playermines: + // NOTE: timeleft can vary based upon server loads: + results = dFmt.format( mine.getRemainingTimeSec() ); + break; + + case prison_mtlb_minename: + case prison_mines_timeleft_bar_minename: + case prison_mtlb_pm: + case prison_mines_timeleft_bar_playermines: + // NOTE: timeleft can vary based upon server loads: + + results = getRemainingTimeBar( mine ); + break; + + case prison_mtlf_minename: + case prison_mines_timeleft_formatted_minename: + case prison_mtlf_pm: + case prison_mines_timeleft_formatted_playermines: + // NOTE: timeleft can vary based upon server loads: + double timeMtlf = mine.getRemainingTimeSec(); + results = formattedTime( timeMtlf ); + break; + + case prison_ms_minename: + case prison_mines_size_minename: + case prison_ms_pm: + case prison_mines_size_playermines: + results = iFmt.format( mine.getBounds().getTotalBlockCount() ); + break; + + case prison_mr_minename: + case prison_mines_remaining_minename: + case prison_mr_pm: + case prison_mines_remaining_playermines: + int remainingBlocks = mine.getRemainingBlockCount(); + results = iFmt.format( remainingBlocks ); + break; + + case prison_mrb_minename: + case prison_mines_remaining_bar_minename: + case prison_mrb_pm: + case prison_mines_remaining_bar_playermines: + int totalBlocks = mine.getBounds().getTotalBlockCount(); + int blocksRemaining = mine.getRemainingBlockCount(); + + results = Prison.get().getIntegrationManager(). + getProgressBar( ((double) blocksRemaining), ((double) totalBlocks), false ); + break; + + case prison_mp_minename: + case prison_mines_percent_minename: + case prison_mp_pm: + case prison_mines_percent_playermines: + // mine.refreshAirCount(); // async & delayed : Very high cost + double percentRemaining = mine.getPercentRemainingBlockCount(); + results = dFmt.format( percentRemaining ); + break; + + case prison_mpc_minename: + case prison_mines_player_count_minename: + case prison_mpc_pm: + case prison_mines_player_count_playermines: + results = iFmt.format( mine.getPlayerCount() ); + break; + + case prison_mbm_minename: + case prison_mines_blocks_mined_minename: + case prison_mbm_pm: + case prison_mines_blocks_mined_playermines: + results = iFmt.format( mine.getTotalBlocksMined() ); + break; + + case prison_mrc_minename: + case prison_mines_reset_count_minename: + case prison_mrc_pm: + case prison_mines_reset_count_playermines: + results = iFmt.format( mine.getResetCount() ); + break; + + default: + break; + } + } + + } + + return results; + } + + public String getTranslatePlayerMinesPlaceHolder( UUID playerUuid, String playerName, String identifier ) { + String results = null; + + if ( playerUuid != null ) { + + List placeHolderKeys = getTranslatedPlaceHolderKeys(); + + if ( !identifier.startsWith( IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED )) { + identifier = IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED + identifier; + } + + for ( PlaceHolderKey placeHolderKey : placeHolderKeys ) { + if ( placeHolderKey.getKey().equalsIgnoreCase( identifier )) { + results = getTranslatePlayerMinesPlaceHolder( playerUuid, playerName, placeHolderKey ); + break; + } + } + } + + return results; + } + /** - * Gets the randomized blocks cache - * - * @return a hashmap with all the randomized blocks + * We must have a player to process these placeholders. + * + * @param playerUuid + * @param playerName + * @param placeHolderKey + * @return */ - public HashMap> getRandomizedBlocks() { - return randomizedBlocks; + public String getTranslatePlayerMinesPlaceHolder( UUID playerUuid, String playerName, PlaceHolderKey placeHolderKey ) { + String results = null; + + if ( playerUuid != null ) { + + // there is no data stored for PLAYERMINES: +// String data = placeHolderKey.getData(); + + Player player = getPlayer(playerUuid, playerName); + + if ( player != null ) { + Mine mine = PrisonMines.getInstance().findMineLocation( player ); + + if ( mine != null ) { + results = getTranslateMinesPlaceHolder( placeHolderKey, mine ); + } + } + } + + return results; } + /** - * Returns the mine with the specified name. - * - * @param name The mine's name, case-sensitive. - * @return An optional containing either the {@link Mine} if it could be found, or empty if it - * does not exist by the specified name. + * Get the Player based first on UUID, then on name. + * + * @param playerUuid + * @param playerName + * @return */ - public Optional getMine(String name) { - return mines.stream().filter(mine -> mine.getName().equals(name)).findFirst(); + private Player getPlayer( UUID playerUuid, String playerName ) { + Player player = null; + Player playerAlt = null; + + // First try to match on UUID + for ( Player p : Prison.get().getPlatform().getOnlinePlayers() ) { + if ( p.getUUID().compareTo( playerUuid ) == 0 ) { + player = p; + break; + } + else if ( p.getName().equalsIgnoreCase( playerName ) ) { + // If we get a hit on the name, save it as an alt... + playerAlt = p; + } + } + + // if player is null, and we have a playerAlt, then use it: + if ( player == null && playerAlt != null ) { + player = playerAlt; + } + return player; + } + + + private String getRemainingTimeBar( Mine mine ) { + + double timeRemaining = mine.getRemainingTimeSec(); + int time = mine.getResetTime(); + + return Prison.get().getIntegrationManager(). + getProgressBar( timeRemaining, ((double) time), true ); + } + + + private String formattedTime( double time ) { + StringBuilder sb = new StringBuilder(); + + long days = (long)(time / TIME_DAY); + time -= (days * TIME_DAY); + if ( days > 0 ) { + sb.append( days ); + sb.append( "d " ); + } + + long hours = (long)(time / TIME_HOUR); + time -= (hours * TIME_HOUR); + if ( sb.length() > 0 || hours > 0 ) { + sb.append( hours ); + sb.append( "h " ); + } + + long mins = (long)(time / TIME_MINUTE); + time -= (mins * TIME_MINUTE); + if ( sb.length() > 0 || mins > 0 ) { + sb.append( mins ); + sb.append( "m " ); + } + + double secs = (double)(time / TIME_SECOND); + time -= (secs * TIME_SECOND); + DecimalFormat dFmt = new DecimalFormat("#0"); + sb.append( dFmt.format( secs )); + sb.append( "s " ); + + return sb.toString(); + } + + + /** + *

Generates a list of all of the placeholder keys, which includes the + * mine name, the placeholder enumeration, and then the actual translated + * placeholder with the mine's name. This is generated only once, but if new + * mines are added or mines removed, then just set the class variable to + * null to auto regenerate with the new values. + *

+ */ + @Override + public List getTranslatedPlaceHolderKeys() { + if ( translatedPlaceHolderKeys == null ) { + translatedPlaceHolderKeys = new ArrayList<>(); + + List placeHolders = + PrisonPlaceHolders.getTypes( PlaceHolderFlags.MINES ); +// PrisonPlaceHolders.excludeTypes( +// PrisonPlaceHolders.getTypes( PlaceHolderFlags.MINES ), +// PlaceHolderFlags.PLAYERMINES); + + for ( Mine mine : getMines() ) { + for ( PrisonPlaceHolders ph : placeHolders ) { + String key = ph.name().replace( + IntegrationManager.PRISON_PLACEHOLDER_MINENAME_SUFFIX, "_" + mine.getName() ). + toLowerCase(); + + PlaceHolderKey placeholder = new PlaceHolderKey(key, ph, mine.getName() ); + if ( ph.getAlias() != null ) { + String aliasName = ph.getAlias().name().replace( + IntegrationManager.PRISON_PLACEHOLDER_MINENAME_SUFFIX, "_" + mine.getName() ). + toLowerCase(); + placeholder.setAliasName( aliasName ); + } + translatedPlaceHolderKeys.add( placeholder ); + + // Getting too many placeholders... add back the extended prefix when looking up: + +// // Now generate a new key based upon the first key, but without the prison_ prefix: +// String key2 = key.replace( +// IntegrationManager.PRISON_PLACEHOLDER_PREFIX + "_", "" ); +// PlaceHolderKey placeholder2 = new PlaceHolderKey(key2, ph, mine.getName(), false ); +// translatedPlaceHolderKeys.add( placeholder2 ); + + } + } + + + // Next we need to register all the PLAYERMINES. The mines are dynamic, based upon which one + // the player is in. So this is just a simple registration. + placeHolders = PrisonPlaceHolders.getTypes( PlaceHolderFlags.PLAYERMINES ); + + for ( PrisonPlaceHolders ph : placeHolders ) { + String key = ph.name(); + + PlaceHolderKey placeholder = new PlaceHolderKey(key, ph ); + if ( ph.getAlias() != null ) { + String aliasName = ph.getAlias().name(); + placeholder.setAliasName( aliasName ); + } + translatedPlaceHolderKeys.add( placeholder ); + } + + } + return translatedPlaceHolderKeys; } /** - * Clears all of the cached randomized blocks + *

If new + * mines are added or mines removed, then just set the class variable to + * null to auto regenerate with the new values. + *

*/ - public void clearCache() { - randomizedBlocks.clear(); + public void resetTranslatedPlaceHolderKeys() { + translatedPlaceHolderKeys = null; } - - public List getMines() { - return mines; + + + @Override + public void reloadPlaceholders() { + + // clear the class variable so they will regenerate: + translatedPlaceHolderKeys = null; + + // Regenerate the translated placeholders: + getTranslatedPlaceHolderKeys(); } } diff --git a/prison-mines/src/test/java/tech/mcprison/prison/mines/data/MineSchedulerTest.java b/prison-mines/src/test/java/tech/mcprison/prison/mines/data/MineSchedulerTest.java new file mode 100644 index 000000000..f15d45d7b --- /dev/null +++ b/prison-mines/src/test/java/tech/mcprison/prison/mines/data/MineSchedulerTest.java @@ -0,0 +1,120 @@ +package tech.mcprison.prison.mines.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import org.junit.Test; + +public class MineSchedulerTest + extends MineScheduler +{ + + /** + *

In this first jUnit test we need to setup a workflow with three entries. + * There should be two messages and one reset. The total time for all workflow + * items must match the resetTime. The first item that is popped off the stack + * should NOT be the reset job, since that should be last. + *

+ */ + @Test + public void testInitializeJobWorkflow01() + { + int resetTime = 301; + boolean includeMessages = true; + ArrayList rwTimes = new ArrayList<>(); + rwTimes.add( 45 ); + rwTimes.add( 120 ); + + int totalTime = 0; + + List jWorkflow = initializeJobWorkflow( resetTime, includeMessages, rwTimes ); + + assertNotNull( jWorkflow ); + assertEquals( 3, jWorkflow.size() ); + + Stack jobStack = new Stack<>(); + jobStack.addAll( jWorkflow ); + + assertEquals( 3, jobStack.size() ); + + { + MineJob job1 = jobStack.pop(); + + assertEquals( 2, jobStack.size() ); + + // Tests that the first job that is popped off the stack is a message and not the reset: + assertTrue( job1.getAction() == MineJobAction.MESSAGE ); + assertEquals( 181.0d, job1.getDelayActionSec(), 0.1d ); + assertEquals( 120.0d, job1.getResetInSec(), 0.1d ); + totalTime += job1.getDelayActionSec(); + } + + { + MineJob job2 = jobStack.pop(); + + assertEquals( 1, jobStack.size() ); + + assertTrue( job2.getAction() == MineJobAction.MESSAGE ); + assertEquals( 75.0d, job2.getDelayActionSec(), 0.1d ); + assertEquals( 45.0d, job2.getResetInSec(), 0.1d ); + totalTime += job2.getDelayActionSec(); + } + + { + MineJob job3 = jobStack.pop(); + + assertEquals( 0, jobStack.size() ); + + assertTrue( job3.getAction() == MineJobAction.RESET_SYNC ); + assertEquals( 45.0d, job3.getDelayActionSec(), 0.1d ); + assertEquals( 0.0d, job3.getResetInSec(), 0.1d ); + totalTime += job3.getDelayActionSec(); + } + + + assertEquals( resetTime, totalTime ); + + } + + + /** + *

This unit test should be similar as the first, but with no message generation + * so there should only be one item in the workflow and that should be the RESET + * for the total time of 301 seconds. + *

+ * + */ + @Test + public void testInitializeJobWorkflow02() + { + int resetTime = 301; + boolean includeMessages = false; + ArrayList rwTimes = new ArrayList<>(); + rwTimes.add( 45 ); + rwTimes.add( 120 ); + + List jWorkflow = initializeJobWorkflow( resetTime, includeMessages, rwTimes ); + + assertEquals( 1L, jWorkflow.size() ); + + Stack jobStack = new Stack<>(); + jobStack.addAll( jWorkflow ); + + assertEquals( 1L, jobStack.size() ); + + MineJob job = jobStack.pop(); + + assertEquals( 0L, jobStack.size() ); + + // Tests that the first job that is popped off the stack is a message and not the reset: + assertTrue( job.getAction() == MineJobAction.RESET_SYNC ); + assertEquals( resetTime, job.getDelayActionSec(), 0.1d ); + + } + +} diff --git a/prison-mines/src/test/java/tech/mcprison/prison/mines/data/PrisonSortableMinesTest.java b/prison-mines/src/test/java/tech/mcprison/prison/mines/data/PrisonSortableMinesTest.java new file mode 100644 index 000000000..dd87e616b --- /dev/null +++ b/prison-mines/src/test/java/tech/mcprison/prison/mines/data/PrisonSortableMinesTest.java @@ -0,0 +1,49 @@ +package tech.mcprison.prison.mines.data; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +import org.junit.Test; + +public class PrisonSortableMinesTest + extends PrisonSortableMines +{ + + /** + * A simple alphabetic test of mines that are case insensitive. + */ + @Test + public void testGetSortedSet() + { + Mine a = new Mine(); + a.setName( "A" ); + Mine b = new Mine(); + b.setName( "b" ); + Mine c = new Mine(); + c.setName( "C" ); + + List unsortedList = new ArrayList<>(); + unsortedList.add( b ); + unsortedList.add( c ); + unsortedList.add( a ); + + assertEquals( "b", unsortedList.get( 0 ).getName() ); + assertEquals( "C", unsortedList.get( 1 ).getName() ); + assertEquals( "A", unsortedList.get( 2 ).getName() ); + + TreeSet mines = new TreeSet<>( new PrisonSortComparableMines() ); + + mines.addAll( unsortedList ); + + List sortedList = new ArrayList<>(mines); + + assertEquals( "A", sortedList.get( 0 ).getName() ); + assertEquals( "b", sortedList.get( 1 ).getName() ); + assertEquals( "C", sortedList.get( 2 ).getName() ); + + } + +} diff --git a/prison-ranks/build.gradle b/prison-ranks/build.gradle index bd2f8aace..6d560c986 100644 --- a/prison-ranks/build.gradle +++ b/prison-ranks/build.gradle @@ -9,6 +9,6 @@ repositories { } dependencies { - compile project(':prison-core') + implementation project(':prison-core') testCompile group: 'junit', name: 'junit', version: '4.12' } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/ChatHandler.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/ChatHandler.java index 2476b3d31..5fe1f4396 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/ChatHandler.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/ChatHandler.java @@ -1,21 +1,16 @@ package tech.mcprison.prison.ranks; +import java.util.List; + import com.google.common.eventbus.Subscribe; + import tech.mcprison.prison.Prison; -import tech.mcprison.prison.PrisonAPI; -import tech.mcprison.prison.integration.Integration; -import tech.mcprison.prison.integration.IntegrationType; -import tech.mcprison.prison.integration.PlaceholderIntegration; +import tech.mcprison.prison.integration.PlaceHolderKey; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.events.player.PlayerChatEvent; -import tech.mcprison.prison.ranks.data.Rank; -import tech.mcprison.prison.ranks.data.RankLadder; -import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.managers.PlayerManager; import tech.mcprison.prison.util.Text; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - /** * Handles replacing chat messages for all players. * @@ -29,12 +24,25 @@ public class ChatHandler { public ChatHandler() { Prison.get().getEventBus().register(this); - Optional placeholderIntegration = Prison.get().getIntegrationManager().getForType(IntegrationType.PLACEHOLDER); - if (placeholderIntegration.isPresent()) { - PlaceholderIntegration integration = ((PlaceholderIntegration) placeholderIntegration.get()); - integration.registerPlaceholder("PRISON_RANK", - player -> Text.translateAmpColorCodes(getPrefix(player.getUUID()))); - } + + // This is pushed back in to the place holder integrations: +// Optional placeholderIntegration = Prison.get().getIntegrationManager().getForType(IntegrationType.PLACEHOLDER); +// if (placeholderIntegration.isPresent()) { +// PlaceholderIntegration integration = ((PlaceholderIntegration) placeholderIntegration.get()); +// +// PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); +// for ( PrisonPlaceHolders placeHolder : PrisonPlaceHolders.values() ) { +// if ( !placeHolder.isSuppressed() ) { +// integration.registerPlaceholder(placeHolder.name(), +// player -> Text.translateAmpColorCodes( +// pm.getTranslatePlayerPlaceHolder( player.getUUID(), placeHolder.name() ) +// )); +// } +// } + +//// integration.registerPlaceholder("PRISON_RANK", +//// player -> Text.translateAmpColorCodes(getPrefix(player.getUUID()))); +// } } /* @@ -43,30 +51,44 @@ public ChatHandler() { @Subscribe public void onPlayerChat(PlayerChatEvent e) { - - String prefix = getPrefix(e.getPlayer().getUUID()); - String newFormat = e.getFormat().replace("{PRISON_RANK}", Text.translateAmpColorCodes(prefix)); + String newFormat = e.getFormat(); + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + + Player player = e.getPlayer(); + + List placeholderKeys = pm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderKeys ) { + String key = "{" + placeHolderKey.getKey() + "}"; + if ( newFormat.contains( key )) { + newFormat = newFormat.replace(key, Text.translateAmpColorCodes( + pm.getTranslatePlayerPlaceHolder( player.getUUID(), player.getName(), placeHolderKey ) )); + } + } + +// String prefix = getPrefix(e.getPlayer().getUUID()); +// String newFormat = e.getFormat().replace("{PRISON_RANK}", Text.translateAmpColorCodes(prefix)); e.setFormat(newFormat); } - /* - * Util - */ - - private String getPrefix(UUID uid) { - Optional player = - PrisonRanks.getInstance().getPlayerManager().getPlayer(uid); - String prefix = ""; - - if (player.isPresent() && !player.get().getRanks().isEmpty()) { - StringBuilder builder = new StringBuilder(); - for (Map.Entry entry : player.get().getRanks().entrySet()) { - builder.append(entry.getValue().tag); - } - prefix = builder.toString(); - } - - return prefix; - } +// /* +// * Util +// */ +// +// private String getPrefix(UUID uid) { +// Optional player = +// PrisonRanks.getInstance().getPlayerManager().getPlayer(uid); +// String prefix = ""; +// +// if (player.isPresent() && !player.get().getRanks().isEmpty()) { +// StringBuilder builder = new StringBuilder(); +// for (Map.Entry entry : player.get().getRanks().entrySet()) { +// builder.append(entry.getValue().tag); +// } +// prefix = builder.toString(); +// } +// +// return prefix; +// } } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/PrisonRanks.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/PrisonRanks.java index 4e1f466b5..7588b0e06 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/PrisonRanks.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/PrisonRanks.java @@ -17,6 +17,9 @@ package tech.mcprison.prison.ranks; +import java.io.IOException; +import java.util.Optional; + import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.convert.ConversionManager; @@ -35,14 +38,11 @@ import tech.mcprison.prison.store.Collection; import tech.mcprison.prison.store.Database; -import java.io.IOException; -import java.util.Optional; - /** * @author Faizaan A. Datoo */ public class PrisonRanks extends Module { - + public static final String MODULE_NAME = "Ranks"; /* * Fields & Constants */ @@ -59,9 +59,15 @@ public class PrisonRanks extends Module { */ public PrisonRanks(String version) { - super("Ranks", version, 3); + super(MODULE_NAME, version, 3); } + + @Override + public String getBaseCommands() { + return "&7/&2ranks &7/&2rankup &7/&2rankupMax &7/&2prestige &7/&2prestiges"; + } + /* * Methods */ @@ -75,7 +81,12 @@ public static PrisonRanks getInstance() { if (!PrisonAPI.getIntegrationManager().hasForType(IntegrationType.ECONOMY)) { getStatus().setStatus(ModuleStatus.Status.FAILED); - getStatus().setMessage("no economy plugin"); + getStatus().setMessage("&cNo economy plugin"); + + String integrationDebug = PrisonAPI.getIntegrationManager() + .getIntegrationDetails(IntegrationType.ECONOMY); + Output.get().logError( "PrisonRanks.enable() - Failed - No Economy Plugin Active - " + + integrationDebug ); return; } @@ -92,20 +103,33 @@ public static PrisonRanks getInstance() { try { rankManager.loadRanks(); } catch (IOException e) { + getStatus().setStatus(ModuleStatus.Status.FAILED); + getStatus().addMessage("&cFailed Loading Ranks: " + e.getMessage()); Output.get().logError("A rank file failed to load.", e); } // Load up the ladders - ladderManager = new LadderManager(initCollection("ladders")); + ladderManager = new LadderManager(initCollection("ladders"), this); try { ladderManager.loadLadders(); } catch (IOException e) { + getStatus().setStatus(ModuleStatus.Status.FAILED); + getStatus().addMessage("&cFailed Loading Loadders: " + e.getMessage()); Output.get().logError("A ladder file failed to load.", e); } createDefaultLadder(); + + // Set the rank relationships: + rankManager.connectRanks(); + + + // Verify that all ranks that use currencies have valid currencies: + rankManager.identifyAllRankCurrencies(); + + // Load up the players @@ -113,6 +137,8 @@ public static PrisonRanks getInstance() { try { playerManager.loadPlayers(); } catch (IOException e) { + getStatus().setStatus(ModuleStatus.Status.FAILED); + getStatus().addMessage("&cFailed Loading Players: " + e.getMessage()); Output.get().logError("A player file failed to load.", e); } @@ -129,6 +155,17 @@ public static PrisonRanks getInstance() { new ChatHandler(); ConversionManager.getInstance().registerConversionAgent(new RankConversionAgent()); + + Output.get().logInfo("Loaded " + getRankCount() + " ranks."); + Output.get().logInfo("Loaded " + getladderCount() + " ladders."); + Output.get().logInfo("Loaded " + getPlayersCount() + " players."); + + + + // Display all Ranks in each ladder: + boolean includeAll = true; + PrisonRanks.getInstance().getRankManager().ranksByLadders( includeAll ); + } private Collection initCollection(String collName) { @@ -142,25 +179,47 @@ private Collection initCollection(String collName) { } /** - * A default ladder is absolutely necessary on the server, so let's create it if it doesn't exist. + * A default ladder is absolutely necessary on the server, so let's create it if it doesn't exist, this also create the prestiges ladder. */ private void createDefaultLadder() { if (!ladderManager.getLadder("default").isPresent()) { Optional rankLadderOptional = ladderManager.createLadder("default"); if (!rankLadderOptional.isPresent()) { - Output.get().logError("Could not create the default ladder."); - super.getStatus().toFailed("&cNo default ladder found."); + String message = "Failed to create a new default ladder, preexisting one not be found."; + Output.get().logError(message); + super.getStatus().toFailed("&c" + message); return; } try { ladderManager.saveLadder(rankLadderOptional.get()); } catch (IOException e) { - Output.get().logError("Could not save the default ladder.", e); - super.getStatus().toFailed("&cNo default ladder found."); + String message = "Failed to save a new default ladder, preexisting one not be found."; + Output.get().logError(message, e); + super.getStatus().toFailed("&c" + message); } } + + if (!ladderManager.getLadder("prestiges").isPresent()) { + Optional rankLadderOptional = ladderManager.createLadder("prestiges"); + + if (!rankLadderOptional.isPresent()) { + String message = "Failed to create a new prestiges ladder, preexisting one not be found."; + Output.get().logError(message); + super.getStatus().toFailed("&c" + message); + return; + } + + try { + ladderManager.saveLadder(rankLadderOptional.get()); + } catch (IOException e) { + String message = "Failed to save a new prestiges ladder, preexisting one not be found."; + Output.get().logError(message, e); + super.getStatus().toFailed("&c" + message); + } + } + } /* @@ -201,4 +260,21 @@ public Database getDatabase() { return database; } + public int getRankCount() { + int rankCount = getRankManager() == null ||getRankManager().getRanks() == null ? 0 : + getRankManager().getRanks().size(); + return rankCount; + } + + public int getladderCount() { + int ladderCount = getLadderManager() == null || getLadderManager().getLadders() == null ? 0 : + getLadderManager().getLadders().size(); + return ladderCount; + } + + public int getPlayersCount() { + int playersCount = getPlayerManager() == null || getPlayerManager().getPlayers() == null ? 0 : + getPlayerManager().getPlayers().size(); + return playersCount; + } } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankUtil.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankUtil.java index bdda58262..3b8fa7187 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankUtil.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankUtil.java @@ -17,8 +17,14 @@ package tech.mcprison.prison.ranks; +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Optional; + import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.EconomyCurrencyIntegration; import tech.mcprison.prison.integration.EconomyIntegration; import tech.mcprison.prison.integration.IntegrationType; import tech.mcprison.prison.internal.Player; @@ -28,10 +34,6 @@ import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.ranks.events.RankUpEvent; -import java.io.IOException; -import java.text.NumberFormat; -import java.util.Optional; - /** * Utilities for changing the ranks of players. * @@ -39,92 +41,476 @@ */ public class RankUtil { - /* - * Fields & Constants - */ - - public static final int RANKUP_SUCCESS = 0, RANKUP_FAILURE = 1, RANKUP_HIGHEST = 2, - RANKUP_CANT_AFFORD = 3, RANKUP_NO_RANKS = 4; + + public enum RankupCommands { + rankup, + promote, + demote, + setrank; + } + + public enum RankupModes { + ONE_RANK, + MAX_RANKS + } + + + public enum RankupStatus { + RANKUP_SUCCESS, + RANKUP_FAILURE, + RANKUP_FAILURE_RANK_DOES_NOT_EXIST, + RANKUP_FAILURE_RANK_IS_NOT_IN_LADDER, + RANKUP_FAILURE_CURRENCY_IS_NOT_SUPPORTED, + + RANKUP_LOWEST, + RANKUP_HIGHEST, + RANKUP_CANT_AFFORD, + RANKUP_NO_RANKS, + + IN_PROGRESS + ; + } + + + public enum RankupTransactions { + + tring_to_rankup, + tring_to_promote, + trying_to_demote, + trying_to_setrank, + + bypassing_cost_for_player, + costs_paid_by_player, + costs_refunded_to_player, + + failed_player, + failed_ladder, + + orginal_rank, + + failed_rank_not_found, + failed_setrank, + + failed_rank_not_in_ladder, + + assigned_default_ladder, + assigned_default_rank, + + next_rank_set, + set_to_default_rank, + set_to_next_higher_rank, + set_to_prior_lower_rank, + + failed_unable_to_assign_rank, + + no_ranks_found_on_ladder, + no_higher_rank_found, + no_lower_rank_found, + + custom_currency, + specified_currency_not_found, + player_cannot_afford, + player_balance_initial, + player_balance_decreased, + player_balance_increased, + player_balance_final, + zero_cost_to_player, + + failure_cannot_save_player_file, + + rankupCommandsStart, + rankupCommandsCompleted, + + fireRankupEvent, + + rankup_successful, + failure_exception_caught_check_server_logs, + + ; + } + + public enum PromoteForceCharge { + no_charge, + charge_player, + refund_player + ; + + public static PromoteForceCharge fromString( String forceCharge ) { + PromoteForceCharge results = null; + + for ( PromoteForceCharge pfc : values() ) { + if ( pfc.name().equalsIgnoreCase( forceCharge )) { + results = pfc; + break; + } + } + + return results; + } + } + + public RankUtil() { + super(); + } - /* - * Constructor + + + public RankupResults rankupPlayer(RankPlayer player, String ladderName, String playerName) { + return rankupPlayer(RankupCommands.rankup, player, ladderName, null, + playerName, null, PromoteForceCharge.charge_player ); + } + + public RankupResults promotePlayer(RankPlayer player, String ladderName, + String playerName, String executorName, PromoteForceCharge pForceCharge) { + return rankupPlayer(RankupCommands.promote, player, ladderName, null, + playerName, executorName, pForceCharge); + } + + public RankupResults demotePlayer(RankPlayer player, String ladderName, + String playerName, String executorName, PromoteForceCharge pForceCharge) { + return rankupPlayer(RankupCommands.demote, player, ladderName, null, + playerName, executorName, pForceCharge); + } + + public RankupResults setRank(RankPlayer player, String ladderName, String rankName, + String playerName, String executorName) { + return rankupPlayer(RankupCommands.setrank, player, ladderName, rankName, + playerName, executorName, PromoteForceCharge.no_charge ); + } + + /** + *

This intermediate function ensures the results are logged before allowing the + * results be return to the calling functions. + *

+ * + * @param command + * @param player + * @param ladderName The ladder to use. If null then uses the "default" ladder. + * @param bypassCost + * @param promote + * @param rank The rank to use. If null with the default ladder, it will use the default rank (the first one). + * @param playerName + * @param executorName + * @return */ - - private RankUtil() { + private RankupResults rankupPlayer(RankupCommands command, RankPlayer player, String ladderName, + String rankName, String playerName, String executorName, + PromoteForceCharge pForceCharge ) { + + RankupResults results = new RankupResults(command, playerName, executorName, ladderName, rankName); + + switch ( command ) { + case rankup: + results.addTransaction(RankupTransactions.tring_to_rankup); + break; + + case promote: + results.addTransaction(RankupTransactions.tring_to_promote); + break; + + case demote: + results.addTransaction(RankupTransactions.trying_to_demote); + break; + + case setrank: + results.addTransaction(RankupTransactions.trying_to_setrank); + break; + + default: + break; + } + + switch ( pForceCharge ) { + case no_charge: + results.addTransaction( RankupTransactions.bypassing_cost_for_player ); + break; + + case charge_player: + results.addTransaction( RankupTransactions.costs_paid_by_player ); + break; + + case refund_player: + results.addTransaction( RankupTransactions.costs_refunded_to_player ); + + break; + + default: + break; + } + + + try { + rankupPlayerInternal(results, command, player, ladderName, + rankName, playerName, executorName, pForceCharge ); + } catch (Exception e ) { + results.addTransaction( RankupTransactions.failure_exception_caught_check_server_logs ); + String message = String.format( + "Failure to perform rankupPlayerInternal check server logs for stack trace: %s", e.getMessage() ); + Output.get().logError( message, e ); + } + + // Log the results: + logTransactionResults(results); + + return results; } - /* - * Method - */ - /** + + /** * Sends the player to the next rank. * * @param player The {@link RankPlayer} to rank up. * @param ladderName The name of the ladder to rank up this player on. */ - public static RankUpResult rankUpPlayer(RankPlayer player, String ladderName) { - + private void rankupPlayerInternal(RankupResults results, + RankupCommands command, RankPlayer player, String ladderName, + String rankName, String playerName, String executorName, + PromoteForceCharge pForceCharge) { + + Player prisonPlayer = PrisonAPI.getPlayer(player.uid).orElse(null); - RankLadder ladder = - PrisonRanks.getInstance().getLadderManager().getLadder(ladderName).orElse(null); - - if(prisonPlayer == null || ladder == null) { - return new RankUpResult(RANKUP_FAILURE, null); + if( prisonPlayer == null ) { + results.addTransaction( RankupStatus.RANKUP_FAILURE, RankupTransactions.failed_player ); + return; + } + + // If ladderName is null, then assign it the default ladder: + if ( ladderName == null ) { + ladderName = "default"; + results.addTransaction(RankupTransactions.assigned_default_ladder); + } + + RankLadder ladder = PrisonRanks.getInstance().getLadderManager().getLadder(ladderName).orElse(null); + if( ladder == null ) { + results.addTransaction( RankupStatus.RANKUP_FAILURE, RankupTransactions.failed_ladder ); + return; } - Optional currentRankOptional = player.getRank(ladder); - Rank nextRank; + - if (!currentRankOptional.isPresent()) { + Optional currentRankOptional = player.getRank(ladder); + results.addTransaction( RankupTransactions.orginal_rank ); + results.setOriginalRank( currentRankOptional.orElse( null ) ); + + + Rank targetRank = null; + + // For all commands except for setrank, if the player does not have a current rank, then + // set it to the default and skip all other rank processing: + + if (!currentRankOptional.isPresent() && + ( command == RankupCommands.rankup || + command == RankupCommands.promote || + command == RankupCommands.demote )) { + // Set the default rank: Optional lowestRank = ladder.getByPosition(0); if (!lowestRank.isPresent()) { - return new RankUpResult(RANKUP_NO_RANKS, null); + results.addTransaction( RankupStatus.RANKUP_NO_RANKS, + RankupTransactions.no_ranks_found_on_ladder ); + return; } - nextRank = lowestRank.get(); - } else { - Optional nextRankOptional = - ladder.getNext(ladder.getPositionOfRank(currentRankOptional.get())); - - if (!nextRankOptional.isPresent()) { - return new RankUpResult(RANKUP_HIGHEST, - currentRankOptional.get()); // We're already at the highest rank. - } - - nextRank = nextRankOptional.get(); + results.addTransaction( RankupTransactions.set_to_default_rank ); + targetRank = lowestRank.get(); + } + + // If default ladder and rank is null at this point, that means use the "default" rank: + if ( command == RankupCommands.setrank ) { + + if ("default".equalsIgnoreCase( ladderName ) && rankName == null ) { + Optional lowestRank = ladder.getLowestRank(); + if ( lowestRank.isPresent() ) { + targetRank = lowestRank.get(); + rankName = targetRank.name; + + results.addTransaction(RankupTransactions.assigned_default_rank); + } + + } + + if ( targetRank == null && rankName != null ) { + Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank( rankName ); + + if ( rankOptional.isPresent() ) { + targetRank = rankOptional.get(); + + if ( !ladder.containsRank( targetRank.id )) { + results.addTransaction( RankupStatus.RANKUP_FAILURE_RANK_IS_NOT_IN_LADDER, + RankupTransactions.failed_rank_not_in_ladder ); + return; + } + } else { + results.addTransaction( RankupStatus.RANKUP_FAILURE_RANK_DOES_NOT_EXIST, + RankupTransactions.failed_rank_not_found ); + return; + } + } else { + results.addTransaction( RankupTransactions.failed_setrank ); + + // Got a problem... if using setrank and no rankName is provided, this is a problem + // But it should never get this far if that is the situation + } + } + + if ( targetRank == null ) { + + Optional nextRankOptional = null; + if ( command == RankupCommands.rankup || command == RankupCommands.promote ) { + // Trying to promote: + nextRankOptional = ladder.getNext(ladder.getPositionOfRank(currentRankOptional.get())); + + if (!nextRankOptional.isPresent()) { + // We're already at the highest rank. + results.addTransaction( RankupStatus.RANKUP_HIGHEST, + RankupTransactions.no_higher_rank_found ); + return; + } + targetRank = nextRankOptional.get(); + results.addTransaction( RankupTransactions.set_to_next_higher_rank ); + + } else if ( command == RankupCommands.demote ) { + // Trying to demote: + nextRankOptional = ladder.getPrevious(ladder.getPositionOfRank(currentRankOptional.get())); + + if (!nextRankOptional.isPresent()) { + // We're already at the lowest rank. + results.addTransaction( RankupStatus.RANKUP_LOWEST, + RankupTransactions.no_lower_rank_found ); + return; + } + targetRank = nextRankOptional.get(); + results.addTransaction( RankupTransactions.set_to_prior_lower_rank ); + } } + + // Target rank is still null, so something failed so terminate: + if ( targetRank == null ) { + results.addTransaction( RankupStatus.RANKUP_FAILURE, RankupTransactions.failed_unable_to_assign_rank ); + return; + } + + + results.setTargetRank( targetRank ); + // We're going to be making a transaction here // We'll check if the player can afford it first, and if so, we'll make the transaction and proceed. - EconomyIntegration economy = (EconomyIntegration) PrisonAPI.getIntegrationManager() - .getForType(IntegrationType.ECONOMY).orElseThrow(IllegalStateException::new); - if (!economy.canAfford(prisonPlayer, nextRank.cost)) { - return new RankUpResult(RANKUP_CANT_AFFORD, nextRank); + double nextRankCost = targetRank.cost; + if (pForceCharge != PromoteForceCharge.no_charge ) { + + + if ( targetRank.currency != null ) { + results.addTransaction( RankupTransactions.custom_currency ); + + EconomyCurrencyIntegration currencyEcon = PrisonAPI.getIntegrationManager() + .getEconomyForCurrency( targetRank.currency ); + if ( currencyEcon == null ) { + results.addTransaction( RankupStatus.RANKUP_FAILURE_CURRENCY_IS_NOT_SUPPORTED, + RankupTransactions.specified_currency_not_found ); + return; + } else { + if ( pForceCharge == PromoteForceCharge.charge_player) { + if (!currencyEcon.canAfford(prisonPlayer, nextRankCost, targetRank.currency)) { + //results.setTargetRank( targetRank ); + results.addTransaction( RankupStatus.RANKUP_CANT_AFFORD, + RankupTransactions.player_cannot_afford ); + return; + } + + results.addTransaction( RankupTransactions.player_balance_initial ); + results.setBalanceInitial( currencyEcon.getBalance( prisonPlayer, targetRank.currency ) ); + results.addTransaction( RankupTransactions.player_balance_decreased ); + currencyEcon.removeBalance(prisonPlayer, nextRankCost, targetRank.currency ); + results.addTransaction( RankupTransactions.player_balance_final ); + results.setBalanceFinal( currencyEcon.getBalance( prisonPlayer, targetRank.currency ) ); + } else + if ( pForceCharge == PromoteForceCharge.refund_player) { + + results.addTransaction( RankupTransactions.player_balance_initial ); + results.setBalanceInitial( currencyEcon.getBalance( prisonPlayer, targetRank.currency ) ); + results.addTransaction( RankupTransactions.player_balance_increased); + currencyEcon.addBalance(prisonPlayer, nextRankCost, targetRank.currency ); + results.addTransaction( RankupTransactions.player_balance_final ); + results.setBalanceFinal( currencyEcon.getBalance( prisonPlayer, targetRank.currency ) ); + } else { + // Should never hit this code!! + } + + } + + } else { + + EconomyIntegration economy = (EconomyIntegration) PrisonAPI.getIntegrationManager() + .getForType(IntegrationType.ECONOMY).orElseThrow(IllegalStateException::new); + if ( pForceCharge == PromoteForceCharge.charge_player) { + if (!economy.canAfford(prisonPlayer, nextRankCost)) { + //results.setTargetRank( targetRank ); + results.addTransaction( RankupStatus.RANKUP_CANT_AFFORD, + RankupTransactions.player_cannot_afford ); + return; + } + + results.addTransaction( RankupTransactions.player_balance_initial ); + results.setBalanceInitial( economy.getBalance( prisonPlayer ) ); + results.addTransaction( RankupTransactions.player_balance_decreased ); + economy.removeBalance(prisonPlayer, nextRankCost); + results.addTransaction( RankupTransactions.player_balance_final ); + results.setBalanceFinal( economy.getBalance( prisonPlayer ) ); + } else + if ( pForceCharge == PromoteForceCharge.refund_player) { + + results.addTransaction( RankupTransactions.player_balance_initial ); + results.setBalanceInitial( economy.getBalance( prisonPlayer ) ); + results.addTransaction( RankupTransactions.player_balance_increased); + economy.addBalance(prisonPlayer, nextRankCost ); + results.addTransaction( RankupTransactions.player_balance_final ); + results.setBalanceFinal( economy.getBalance( prisonPlayer ) ); + } else { + // Should never hit this code!! + } + + } + + } else { + results.addTransaction( RankupTransactions.zero_cost_to_player ); } - economy.removeBalance(prisonPlayer, nextRank.cost); - - player.addRank(ladder, nextRank); + player.addRank(ladder, targetRank); try { PrisonRanks.getInstance().getPlayerManager().savePlayer(player); } catch (IOException e) { Output.get().logError("An error occurred while saving player files.", e); - return new RankUpResult(RANKUP_FAILURE, null); + + results.addTransaction( RankupStatus.RANKUP_FAILURE, + RankupTransactions.failure_cannot_save_player_file ); + return; } // Now, we'll run the rank up commands. - for (String cmd : nextRank.rankUpCommands) { + results.addTransaction( RankupTransactions.rankupCommandsStart ); + results.setRankupCommandsAvailable( targetRank.rankUpCommands.size() ); + + int count = 0; + for (String cmd : targetRank.rankUpCommands) { String formatted = cmd.replace("{player}", prisonPlayer.getName()) .replace("{player_uid}", player.uid.toString()); PrisonAPI.dispatchCommand(formatted); + count++; } + results.setRankupCommandsExecuted( count ); + results.addTransaction( RankupTransactions.rankupCommandsCompleted ); + + results.addTransaction( RankupTransactions.fireRankupEvent ); Prison.get().getEventBus().post( - new RankUpEvent(player, currentRankOptional.orElse(null), nextRank, nextRank.cost)); - return new RankUpResult(RANKUP_SUCCESS, nextRank); + new RankUpEvent(player, currentRankOptional.orElse(null), targetRank, nextRankCost)); + + + results.addTransaction( RankupStatus.RANKUP_SUCCESS, RankupTransactions.rankup_successful ); + } public static String doubleToDollarString(double val) { @@ -134,22 +520,174 @@ public static String doubleToDollarString(double val) { public static int doubleToInt(Object d) { return Math.toIntExact(Math.round((double) d)); } - - /* - * Member Classes - */ - - - public static class RankUpResult { - - public int status; - public Rank rank; - - public RankUpResult(int status, Rank rank) { - this.status = status; - this.rank = rank; - } + + public static long doubleToLong(Object d) { + return Math.round((double) d); } + + + + private void logTransactionResults( RankupResults results ) + { + StringBuilder sb = new StringBuilder(); + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + DecimalFormat iFmt = new DecimalFormat("#,##0"); + + Rank oRank = results.getOriginalRank(); + Rank tRank = results.getTargetRank(); + + for ( RankupTransactions rt : RankupTransactions.values() ) { + + // Log the entry if it exists in the results: + if ( results.getTransactions().contains( rt ) ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + + // Log the transaction: + sb.append( rt.name() ); + + // If the transaction has supporting data, log it too: + switch ( rt ) { + case orginal_rank: + sb.append( "=" ); + sb.append( oRank == null ? "" : oRank.name ); + + break; + + case custom_currency: + sb.append( "=" ); + sb.append( tRank == null || tRank.currency == null ? "" : tRank.currency ); + + break; + + case specified_currency_not_found: + sb.append( "=" ); + sb.append( tRank == null || tRank.currency == null ? "" : tRank.currency ); + + break; + + case player_balance_initial: + sb.append( "=" ); + sb.append( dFmt.format( results.getBalanceInitial() ) ); + + break; + + case player_balance_decreased: + sb.append( "=" ); + sb.append( tRank == null ? "" : dFmt.format( tRank.cost ) ); + + break; + + case player_balance_increased: + sb.append( "=" ); + sb.append( tRank == null ? "" : dFmt.format( tRank.cost ) ); + + break; + + case player_balance_final: + sb.append( "=" ); + sb.append( dFmt.format( results.getBalanceFinal() ) ); + + break; + + case rankupCommandsStart: + sb.append( "=" ); + sb.append( iFmt.format( results.getRankupCommandsAvailable() ) ); + + break; + + case rankupCommandsCompleted: + sb.append( "=" ); + sb.append( iFmt.format( results.getRankupCommandsExecuted() ) ); + + break; + + default: + break; + } + } + } + + + // Add in the prefix for the log entry: + String prefix = String.format( + "Rankup: command=%s player=%s executor=%s status=%s " + + "ladderName=%s rankName=%s " + + "originalRank=(%s%s%s) targetRank=(%s%s%s) " + + "runtime=%s ms message=[%s] ", + + results.getCommand().name(), + results.getPlayer(), + (results.getExecutor() == null ? "(see player)" : results.getExecutor()), + (results.getStatus() == null ? "" : results.getStatus().name()), + + (results.getLadderName() == null ? "" : results.getLadderName() ), + (results.getRankName() == null ? "" : results.getRankName() ), + + + (oRank == null ? "none" : oRank.name), + (oRank == null ? "" : " " + dFmt.format( oRank.cost)), + (oRank == null || oRank.currency == null ? "" : " " + oRank.currency), + + (tRank == null ? "none" : tRank.name), + (tRank == null ? "" : " " + dFmt.format( tRank.cost)), + (tRank == null || tRank.currency == null ? "" : " " + tRank.currency), + + iFmt.format( results.getElapsedTime() ), + (results.getMessage() == null ? "" : results.getMessage()) + ); + + sb.insert( 0, prefix ); + + + Output.get().logInfo( sb.toString() ); + } + + +// @Deprecated +// public static class RankUpResult { +// +// private RankupStatus status; +// private Rank rank; +// private String message; +// +// public RankUpResult(RankupStatus status, Rank rank, String message) { +// this.status = status; +// this.rank = rank; +// this.message = message; +// } +// +// public RankUpResult(RankupStatus status, Rank rank) { +// this(status, rank, null); +// } +// +// public RankUpResult(RankupStatus status) { +// this(status, null, null); +// } +// +// +// public RankupStatus getStatus() { +// return status; +// } +// public void setStatus( RankupStatus status ) { +// this.status = status; +// } +// +// public Rank getRank() { +// return rank; +// } +// public void setRank( Rank rank ) { +// this.rank = rank; +// } +// +// public String getMessage() { +// return message; +// } +// public void setMessage( String message ) { +// this.message = message; +// } +// } } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankupResults.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankupResults.java new file mode 100644 index 000000000..dd2d47c5b --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankupResults.java @@ -0,0 +1,187 @@ +package tech.mcprison.prison.ranks; + +import java.util.ArrayList; +import java.util.List; + +import tech.mcprison.prison.ranks.RankUtil.RankupCommands; +import tech.mcprison.prison.ranks.RankUtil.RankupStatus; +import tech.mcprison.prison.ranks.RankUtil.RankupTransactions; +import tech.mcprison.prison.ranks.data.Rank; + +public class RankupResults { + + private RankupCommands command; + + private String player; + private String executor; + + private RankupStatus status; + private String ladderName; + private String rankName; + private Rank originalRank; + private Rank targetRank; + private String message; + + private List transactions; + + private double balanceInitial; + private double balanceFinal; + + private int rankupCommandsAvailable = 0; + private int rankupCommandsExecuted = 0; + + private long timestampStart = 0; + private long timestampStop = 0; + + public RankupResults(RankupCommands command, String playerName, String executorName, + String ladderName, String rankName) { + super(); + + this.status = RankupStatus.IN_PROGRESS; + + this.transactions = new ArrayList<>(); + + this.command = command; + this.player = playerName; + this.executor = executorName; + + this.ladderName = ladderName; + this.rankName = rankName; + + this.timestampStart = System.currentTimeMillis(); + } + + + public RankupResults addTransaction( RankupStatus status, RankupTransactions transaction ) { + setStatus( status ); + getTransactions().add( transaction ); + this.timestampStop = System.currentTimeMillis(); + return this; + } + public RankupResults addTransaction( RankupTransactions transaction ) { + getTransactions().add( transaction ); + this.timestampStop = System.currentTimeMillis(); + return this; + } + + + public long getElapsedTime() { + long elapsed = getTimestampStart() - getTimestampStop(); + + return ( elapsed < 0 ? 0 : elapsed); + } + + + public RankupCommands getCommand() { + return command; + } + public void setCommand( RankupCommands command ) { + this.command = command; + } + + public String getPlayer() { + return player; + } + public void setPlayer( String player ) { + this.player = player; + } + + public String getExecutor() { + return executor; + } + public void setExecutor( String executor ) { + this.executor = executor; + } + + public String getLadderName() { + return ladderName; + } + public void setLadderName( String ladderName ) { + this.ladderName = ladderName; + } + + public String getRankName() { + return rankName; + } + public void setRankName( String rankName ) { + this.rankName = rankName; + } + + public RankupStatus getStatus() { + return status; + } + public void setStatus( RankupStatus status ) { + this.status = status; + } + + public Rank getOriginalRank() { + return originalRank; + } + public void setOriginalRank( Rank originalRank ) { + this.originalRank = originalRank; + } + + public Rank getTargetRank() { + return targetRank; + } + public void setTargetRank( Rank targetRank ) { + this.targetRank = targetRank; + } + + public String getMessage() { + return message; + } + public void setMessage( String message ) { + this.message = message; + } + + public List getTransactions() { + return transactions; + } + public void setTransactions( List transactions ) { + this.transactions = transactions; + } + + public double getBalanceInitial() { + return balanceInitial; + } + public void setBalanceInitial( double balanceInitial ) { + this.balanceInitial = balanceInitial; + } + + public double getBalanceFinal() { + return balanceFinal; + } + public void setBalanceFinal( double balanceFinal ) { + this.balanceFinal = balanceFinal; + } + + public int getRankupCommandsAvailable() { + return rankupCommandsAvailable; + } + public void setRankupCommandsAvailable( int rankupCommandsAvailable ) { + this.rankupCommandsAvailable = rankupCommandsAvailable; + } + + public int getRankupCommandsExecuted() { + return rankupCommandsExecuted; + } + public void setRankupCommandsExecuted( int rankupCommandsExecuted ) { + this.rankupCommandsExecuted = rankupCommandsExecuted; + } + + public long getTimestampStart() { + return timestampStart; + } + public void setTimestampStart( long timestampStart ) { + this.timestampStart = timestampStart; + } + + public long getTimestampStop() { + return timestampStop; + } + public void setTimestampStop( long timestampStop ) { + this.timestampStop = timestampStop; + } + +} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/CommandCommands.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/CommandCommands.java index 002418c8a..5df2a0f5f 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/CommandCommands.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/CommandCommands.java @@ -5,6 +5,8 @@ import tech.mcprison.prison.commands.Command; import tech.mcprison.prison.commands.Wildcard; import tech.mcprison.prison.internal.CommandSender; +import tech.mcprison.prison.localization.Localizable; +import tech.mcprison.prison.localization.Localizable.Level; import tech.mcprison.prison.output.BulletedListComponent; import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.output.FancyMessageComponent; @@ -20,10 +22,27 @@ * @author Faizaan A. Datoo */ public class CommandCommands { - - @Command(identifier = "ranks command add", description = "Adds a command to a rank.", onlyPlayers = false, permissions = "ranks.command") - public void commandAdd(CommandSender sender, @Arg(name = "rank") String rankName, - @Arg(name = "command") @Wildcard String command) { + + public CommandCommands() { + super(); + + // Now this is slight strange. Once in a while I've been seeing exceptions that the + // following class cannot be resolved. So I don't know why it can't, but it was not + // being used directly within this Ranks module, so it's being added here just to + // allow the compiler to add it, and so hopefully the class loaders at run time can + // finally access it consistently. + @SuppressWarnings( "unused" ) + Level force = Localizable.Level.ERROR; + } + + @Command(identifier = "ranks command add", + description = "Adds a command to a rank using {player} and {player_uid} as placeholders.", + onlyPlayers = false, permissions = "ranks.command") + public void commandAdd(CommandSender sender, + @Arg(name = "rankName", description = "The Rank name that will recieve this command.") String rankName, + @Arg(name = "command", + description = "The command to add without / prefix. Will be ran as a console command.") + @Wildcard String command) { if (command.startsWith("/")) { command = command.replaceFirst("/", ""); } @@ -53,9 +72,13 @@ public void commandAdd(CommandSender sender, @Arg(name = "rank") String rankName } - @Command(identifier = "ranks command remove", description = "Removes a command from a rank.", onlyPlayers = false, permissions = "ranks.command") - public void commandRemove(CommandSender sender, @Arg(name = "rank") String rankName, - @Arg(name = "command") @Wildcard String command) { + @Command(identifier = "ranks command remove", description = "Removes a command from a rank.", + onlyPlayers = false, permissions = "ranks.command") + public void commandRemove(CommandSender sender, + @Arg(name = "rankName") String rankName, + @Arg(name = "command", + description = "The command must be exactly the same as stored in the rank.") + @Wildcard String command) { if (command.startsWith("/")) { command = command.replaceFirst("/", ""); } @@ -89,8 +112,11 @@ public void commandRemove(CommandSender sender, @Arg(name = "rank") String rankN } } - @Command(identifier = "ranks command list", description = "Lists the commands for a rank.", onlyPlayers = false, permissions = "ranks.command") - public void commandList(CommandSender sender, @Arg(name = "rank") String rankName) { + @Command(identifier = "ranks command list", description = "Lists the commands for a rank.", + onlyPlayers = false, permissions = "ranks.command") + public void commandList(CommandSender sender, + @Arg(name = "rankName") String rankName) { + Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); if (!rankOptional.isPresent()) { Output.get().sendError(sender, "The rank '%s' does not exist.", rankName); diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommands.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommands.java index 3b8fbfce6..99aede2eb 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommands.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommands.java @@ -18,7 +18,8 @@ */ public class LadderCommands { - @Command(identifier = "ranks ladder create", description = "Creates a new rank ladder.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder create", description = "Creates a new rank ladder.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderAdd(CommandSender sender, @Arg(name = "ladderName") String ladderName) { Optional ladderOptional = PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); @@ -47,7 +48,8 @@ public void ladderAdd(CommandSender sender, @Arg(name = "ladderName") String lad } } - @Command(identifier = "ranks ladder delete", description = "Deletes a rank ladder.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder delete", description = "Deletes a rank ladder.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderRemove(CommandSender sender, @Arg(name = "ladderName") String ladderName) { Optional ladder = PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); @@ -56,7 +58,17 @@ public void ladderRemove(CommandSender sender, @Arg(name = "ladderName") String Output.get().sendError(sender, "The ladder '%s' doesn't exist.", ladderName); return; } + + if (ladder.get().name.equalsIgnoreCase( "default" )) { + Output.get().sendError(sender, "You cannot delete the default ladder. It's needed." ); + return; + } + if (ladder.get().name.equalsIgnoreCase( "prestiges" )) { + Output.get().sendError(sender, "You cannot delete the prestiges ladder. It's needed." ); + return; + } + if ( PrisonRanks.getInstance().getLadderManager().removeLadder(ladder.get()) ) { Output.get().sendInfo(sender, "The ladder '%s' has been deleted.", ladderName); @@ -66,7 +78,8 @@ public void ladderRemove(CommandSender sender, @Arg(name = "ladderName") String } } - @Command(identifier = "ranks ladder list", description = "Lists all rank ladders.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder list", description = "Lists all rank ladders.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderList(CommandSender sender) { ChatDisplay display = new ChatDisplay("Ladders"); BulletedListComponent.BulletedListBuilder list = @@ -79,7 +92,8 @@ public void ladderList(CommandSender sender) { display.send(sender); } - @Command(identifier = "ranks ladder listranks", description = "Lists the ranks within a ladder.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder listranks", description = "Lists the ranks within a ladder.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderInfo(CommandSender sender, @Arg(name = "ladderName") String ladderName) { Optional ladder = PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); @@ -94,23 +108,33 @@ public void ladderInfo(CommandSender sender, @Arg(name = "ladderName") String la BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); + + boolean first = true; for (RankLadder.PositionRank rank : ladder.get().ranks) { Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rank.getRankId()); if(!rankOptional.isPresent()) { continue; // Skip it } + + boolean defaultRank = ("default".equalsIgnoreCase( ladderName ) && first); - builder.add("&3#%d &8- &3%s", rank.getPosition(), - rankOptional.get().name); + builder.add("&3#%d &8- &3%s %s", rank.getPosition(), + rankOptional.get().name, + (defaultRank ? "&b(&9Default Rank&b) &7-" : "") + ); + first = false; } + builder.add( "&3See &f/ranks list &b[ladderName] &3for more details on ranks." ); + display.addComponent(builder.build()); - + display.send(sender); } - @Command(identifier = "ranks ladder addrank", description = "Adds a rank to a ladder.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder addrank", description = "Adds a rank to a ladder.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderAddRank(CommandSender sender, @Arg(name = "ladderName") String ladderName, @Arg(name = "rankName") String rankName, @Arg(name = "position", def = "0", verifiers = "min[0]") int position) { @@ -152,7 +176,8 @@ public void ladderAddRank(CommandSender sender, @Arg(name = "ladderName") String } } - @Command(identifier = "ranks ladder delrank", description = "Removes a rank from a ladder.", onlyPlayers = false, permissions = "ranks.ladder") + @Command(identifier = "ranks ladder delrank", description = "Removes a rank from a ladder.", + onlyPlayers = false, permissions = "ranks.ladder") public void ladderRemoveRank(CommandSender sender, @Arg(name = "ladderName") String ladderName, @Arg(name = "rankName") String rankName) { Optional ladder = diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommand.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommand.java index cc0c24255..998ecebd6 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommand.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The MC-Prison Team + * Copyright (C) 2017-2020 The MC-Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,93 +17,450 @@ package tech.mcprison.prison.ranks.commands; +import java.util.List; import java.util.Optional; +import java.util.UUID; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; +import tech.mcprison.prison.integration.EconomyIntegration; +import tech.mcprison.prison.integration.IntegrationType; +import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.PrisonRanks; import tech.mcprison.prison.ranks.RankUtil; +import tech.mcprison.prison.ranks.RankUtil.PromoteForceCharge; +import tech.mcprison.prison.ranks.RankUtil.RankupModes; +import tech.mcprison.prison.ranks.RankUtil.RankupStatus; +import tech.mcprison.prison.ranks.RankupResults; +import tech.mcprison.prison.ranks.data.Rank; import tech.mcprison.prison.ranks.data.RankLadder; import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.managers.LadderManager; +import tech.mcprison.prison.util.ChatColor; /** * The commands for this module. * * @author Faizaan A. Datoo + * @author GABRYCA + * @author RoyalBlueRanger */ public class RankUpCommand { /* * /rankup command */ + + @Command(identifier = "rankupMax", + description = "Ranks up to the max rank that the player can afford. If the player has the " + + "perm ranks.rankupmax.prestige it will try to rankup prestige once it maxes out " + + "on the default ladder.", + permissions = "ranks.user", + altPermissions = "ranks.rankupmax.[ladderName] ranks.rankupmax.prestige", + onlyPlayers = false) + public void rankUpMax(Player sender, + @Arg(name = "ladder", description = "The ladder to rank up on.", def = "default") String ladder + ) { + rankUpPrivate(sender, ladder, RankupModes.MAX_RANKS, "ranks.rankupmax." ); + } + + @Command(identifier = "rankup", description = "Ranks up to the next rank.", + permissions = "ranks.user", altPermissions = "ranks.rankup.[ladderName]", onlyPlayers = false) + public void rankUp(Player sender, + @Arg(name = "ladder", description = "The ladder to rank up on.", def = "default") String ladder + ) { + rankUpPrivate(sender, ladder, RankupModes.ONE_RANK, "ranks.rankup." ); + } - @Command(identifier = "rankup", description = "Ranks up to the next rank.", permissions = { - "ranks.user"}) public void rankUp(Player sender, - @Arg(name = "ladder", description = "The ladder to rank up on.", def = "default") - String ladderName) { + private void rankUpPrivate(Player sender, String ladder, RankupModes mode, String permission ) { // RETRIEVE THE LADDER // This player has to have permission to rank up on this ladder. - if (!ladderName.equalsIgnoreCase("default") && !sender - .hasPermission("ranks.rankup." + ladderName.toLowerCase())) { + if (!ladder.equalsIgnoreCase("default") && !sender + .hasPermission(permission + ladder.toLowerCase())) { Output.get() .sendError(sender, "You need the permission '%s' to rank up on this ladder.", - "ranks.rankup." + ladderName.toLowerCase()); + permission + ladder.toLowerCase()); return; } - Optional ladderOptional = + + // + if ( mode == null ) { + + Output.get() + .sendError(sender, "&7Invalid rankup mode. Internal failure. Please report." ); + return; + } + + UUID playerUuid = sender.getUUID(); + + ladder = confirmLadder( sender, ladder ); + + RankPlayer rankPlayer = getPlayer( sender, playerUuid, sender.getName() ); + Rank pRank = rankPlayer.getRank( ladder ); + Rank pRankSecond = rankPlayer.getRank("default"); + Rank pRankAfter = null; + LadderManager lm = PrisonRanks.getInstance().getLadderManager(); + boolean WillPrestige = false; + + // If the ladder's the prestige one, it'll execute all of this + if (ladder.equalsIgnoreCase("prestiges")) { + + if (!(lm.getLadder("default").isPresent())){ + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c[ERROR] There isn't a default ladder! Please report this to an admin!")); + return; + } + if (!(lm.getLadder("default").get().getLowestRank().isPresent())){ + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c[ERROR] Can't get the lowest rank! Please report this to an admin!")); + return; + } + + Rank rank = lm.getLadder("default").get().getLowestRank().get(); + + while (rank.rankNext != null) { + rank = rank.rankNext; + } + + if (!(rank == pRankSecond)) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&cYou aren't at the last rank!")); + return; + } + // IF everything's ready, this will be true and the prestige method will start + WillPrestige = true; + } + + // Get currency if it exists, otherwise it will be null if the Rank has no currency: + String currency = rankPlayer == null || pRank == null ? null : pRank.currency; + + boolean rankupWithSuccess = false; + + if ( ladder != null && rankPlayer != null ) { + RankupResults results = new RankUtil().rankupPlayer(rankPlayer, ladder, sender.getName()); + + processResults( sender, null, results, true, null, ladder, currency ); + + if (results.getStatus() == RankupStatus.RANKUP_SUCCESS && mode == RankupModes.MAX_RANKS && !ladder.equals("prestiges")) { + rankUpPrivate( sender, ladder, mode, permission ); + } + if (results.getStatus() == RankupStatus.RANKUP_SUCCESS){ + rankupWithSuccess = true; + } + + // Get the player rank after + pRankAfter = rankPlayer.getRank(ladder); + + } + + // Prestige method + prestigePlayer(sender, rankPlayer, pRank, pRankAfter, lm, WillPrestige, rankupWithSuccess); + } + + private void prestigePlayer(Player sender, RankPlayer rankPlayer, Rank pRank, Rank pRankAfter, LadderManager lm, boolean willPrestige, boolean rankupWithSuccess) { + // Get the player rank after, just to check if it has success + Rank pRankSecond; + // Conditions + if (willPrestige && rankupWithSuccess && pRankAfter != null && pRank != pRankAfter) { + // Set the player rank to the first one of the default ladder + PrisonAPI.dispatchCommand("ranks set rank " + sender.getName() + " " + lm.getLadder("default").get().getLowestRank().get().name + " default"); + // Get that rank + pRankSecond = rankPlayer.getRank("default"); + // Check if the ranks match + if (pRankSecond == lm.getLadder("default").get().getLowestRank().get()) { + // Get economy + EconomyIntegration economy = (EconomyIntegration) PrisonAPI.getIntegrationManager().getForType(IntegrationType.ECONOMY).orElseThrow(IllegalStateException::new); + // Set the player balance to 0 (reset) + economy.setBalance(sender, 0); + // Send a message to the player because he did prestige! + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&7[&3Congratulations&7] &3You've &6Prestige&3 to " + pRankAfter.tag + "&c!")); + } + } + } + + + @Command(identifier = "ranks promote", description = "Promotes a player to the next rank.", + permissions = "ranks.promote", onlyPlayers = false) + public void promotePlayer(CommandSender sender, + @Arg(name = "playerName", def = "", description = "Player name") String playerName, + @Arg(name = "ladder", description = "The ladder to promote on.", def = "default") String ladder, + @Arg(name = "chargePlayers", description = "Force the player to pay for the rankup (no_charge, charge_player)", + def = "no_charge") String chargePlayer + ) { + + Player player = getPlayer( sender, playerName ); + + if (player == null) { + sender.sendMessage( "&3You must be a player in the game to run this command, " + + "and/or the player must be online." ); + return; + } + + PromoteForceCharge pForceCharge = PromoteForceCharge.fromString( chargePlayer ); + if ( pForceCharge == null|| pForceCharge == PromoteForceCharge.refund_player ) { + sender.sendMessage( + String.format( "&3Invalid value for chargePlayer. Valid values are: %s %s", + PromoteForceCharge.no_charge.name(), PromoteForceCharge.charge_player.name()) ); + return; + } + + UUID playerUuid = player.getUUID(); + + ladder = confirmLadder( sender, ladder ); + + RankPlayer rankPlayer = getPlayer( sender, playerUuid, player.getName() ); + Rank pRank = rankPlayer.getRank( ladder ); + + // Get currency if it exists, otherwise it will be null if the Rank has no currency: + String currency = rankPlayer == null || pRank == null ? null : pRank.currency; + + + + if ( ladder != null && rankPlayer != null ) { + RankupResults results = new RankUtil().promotePlayer(rankPlayer, ladder, + player.getName(), sender.getName(), pForceCharge); + + processResults( sender, player, results, true, null, ladder, currency ); + } + } + + + @Command(identifier = "ranks demote", description = "Demotes a player to the next lower rank.", + permissions = "ranks.demote", onlyPlayers = false) + public void demotePlayer(CommandSender sender, + @Arg(name = "playerName", def = "", description = "Player name") String playerName, + @Arg(name = "ladder", description = "The ladder to demote on.", def = "default") String ladder, + @Arg(name = "chargePlayers", description = "Refund the player for the demotion (no_charge, refund_player)", + def = "no_charge") String refundPlayer + ) { + + Player player = getPlayer( sender, playerName ); + + if (player == null) { + sender.sendMessage( "&3You must be a player in the game to run this command, " + + "and/or the player must be online." ); + return; + } + + PromoteForceCharge pForceCharge = PromoteForceCharge.fromString( refundPlayer ); + if ( pForceCharge == null || pForceCharge == PromoteForceCharge.charge_player ) { + sender.sendMessage( + String.format( "&3Invalid value for refundPlayer. Valid values are: %s %s", + PromoteForceCharge.no_charge.name(), PromoteForceCharge.refund_player.name()) ); + return; + } + + UUID playerUuid = player.getUUID(); + + ladder = confirmLadder( sender, ladder ); + + RankPlayer rankPlayer = getPlayer( sender, playerUuid, player.getName() ); + Rank pRank = rankPlayer.getRank( ladder ); + + // Get currency if it exists, otherwise it will be null if the Rank has no currency: + String currency = rankPlayer == null || pRank == null ? null : pRank.currency; + + if ( ladder != null && rankPlayer != null ) { + RankupResults results = new RankUtil().demotePlayer(rankPlayer, ladder, + player.getName(), sender.getName(), pForceCharge); + + processResults( sender, player, results, false, null, ladder, currency ); + } + } + + + @Command(identifier = "ranks set rank", description = "Sets a play to a specified rank.", + permissions = "ranks.setrank", onlyPlayers = false) + public void setRank(CommandSender sender, + @Arg(name = "playerName", def = "", description = "Player name") String playerName, + @Arg(name = "rankName", description = "The rank to assign to the player") String rank, + @Arg(name = "ladder", description = "The ladder to demote on.", def = "default") String ladder) { + + Player player = getPlayer( sender, playerName ); + + if (player == null) { + sender.sendMessage( "&3You must be a player in the game to run this command, " + + "and/or the player must be online." ); + return; + } + + setPlayerRank( player, rank, ladder, sender ); + } + + + private void setPlayerRank( Player player, String rank, String ladder, CommandSender sender ) + { + UUID playerUuid = player.getUUID(); + + ladder = confirmLadder( sender, ladder ); + + RankPlayer rankPlayer = getPlayer( sender, playerUuid, player.getName() ); + Rank pRank = rankPlayer.getRank( ladder ); + + // Get currency if it exists, otherwise it will be null if the Rank has no currency: + String currency = rankPlayer == null || pRank == null ? null : pRank.currency; + + if ( ladder != null && rankPlayer != null ) { + RankupResults results = new RankUtil().setRank(rankPlayer, ladder, rank, + player.getName(), sender.getName()); + + processResults( sender, player, results, true, rank, ladder, currency ); + } + } + + + + public String confirmLadder( CommandSender sender, String ladderName ) { + Optional ladderOptional = PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); // The ladder doesn't exist if (!ladderOptional.isPresent()) { Output.get().sendError(sender, "The ladder '%s' does not exist.", ladderName); - return; + ladderName = null; } + return ladderName; + } - // RETRIEVE THE PLAYER - Optional playerOptional = - PrisonRanks.getInstance().getPlayerManager().getPlayer(sender.getUUID()); + public RankPlayer getPlayer( CommandSender sender, UUID playerUuid, String playerName ) { + Optional playerOptional = + PrisonRanks.getInstance().getPlayerManager().getPlayer(playerUuid, playerName); // Well, this isn't supposed to happen... if (!playerOptional.isPresent()) { Output.get().sendError(sender, - "You don't exist! The server has no records of you. Try rejoining, or contact a server administrator for help."); - return; + "You don't exist! The server has no records of you. Try rejoining, " + + "or contact a server administrator for help."); } - // RANK-UP THE PLAYER + return playerOptional.isPresent() ? playerOptional.get() : null; + } - RankPlayer player = playerOptional.get(); - RankUtil.RankUpResult result = RankUtil.rankUpPlayer(player, ladderName); - - switch (result.status) { - case RankUtil.RANKUP_SUCCESS: - Output.get().sendInfo(sender, "Congratulations! You have ranked up to rank '%s'.", - result.rank.name); + public void processResults( CommandSender sender, Player player, + RankupResults results, + boolean rankup, String rank, String ladder, String currency ) { + + switch (results.getStatus()) { + case RANKUP_SUCCESS: + if ( rankup ) { + String message = String.format( "Congratulations! %s ranked up to rank '%s'. %s", + (player == null ? "You have" : player.getName()), + (results.getTargetRank() == null ? "" : results.getTargetRank().name), + (results.getMessage() != null ? results.getMessage() : "") ); + Output.get().sendInfo(sender, message); + Output.get().logInfo( "%s initiated rank change: %s", sender.getName(), message ); + + String messageGlobal = String.format( "Congratulations! %s ranked up to rank '%s'.", + (player == null ? "Someone" : player.getName()), + (results.getTargetRank() == null ? "" : results.getTargetRank().name) ); + broadcastToWholeServer( sender, messageGlobal ); + } else { + String message = String.format( "Unfortunately, %s has been demoted to rank '%s'. %s", + (player == null ? "You have" : player.getName()), + (results.getTargetRank() == null ? "" : results.getTargetRank().name), + (results.getMessage() != null ? results.getMessage() : "")); + Output.get().sendInfo(sender, message); + Output.get().logInfo( "%s initiated rank change: %s", sender.getName(), message ); + + String messageGlobal = String.format( "Unfortunately, %s has been demoted to rank '%s'.", + (player == null ? "Someone" : player.getName()), + (results.getTargetRank() == null ? "" : results.getTargetRank().name) ); + broadcastToWholeServer( sender, messageGlobal ); + } break; - case RankUtil.RANKUP_CANT_AFFORD: + case RANKUP_CANT_AFFORD: Output.get().sendError(sender, "You don't have enough money to rank up! The next rank costs %s.", - RankUtil.doubleToDollarString(result.rank.cost)); + RankUtil.doubleToDollarString( + results.getTargetRank() == null ? 0 : results.getTargetRank().cost)); break; - case RankUtil.RANKUP_HIGHEST: - Output.get().sendInfo(sender, "You are already at the highest rank!"); + case RANKUP_LOWEST: + Output.get().sendInfo(sender, "%s already at the lowest rank!", + (player == null ? "You are" : player.getName())); + break; + case RANKUP_HIGHEST: + Output.get().sendInfo(sender, "%s already at the highest rank!", + (player == null ? "You are" : player.getName())); break; - case RankUtil.RANKUP_FAILURE: + case RANKUP_FAILURE: Output.get().sendError(sender, - "Failed to retrieve or write data. Your files may be corrupted. Alert a server administrator."); + "Failed to retrieve or write data. Your files may be corrupted. " + + "Alert a server administrator."); break; - case RankUtil.RANKUP_NO_RANKS: + case RANKUP_NO_RANKS: Output.get().sendError(sender, "There are no ranks in this ladder."); break; + case RANKUP_FAILURE_RANK_DOES_NOT_EXIST: + Output.get().sendError(sender, "The rank %s does not exist on this server.", rank); + break; + case RANKUP_FAILURE_RANK_IS_NOT_IN_LADDER: + Output.get().sendError(sender, "The rank %s does not exist in the ladder %s.", rank, ladder); + break; + + case RANKUP_FAILURE_CURRENCY_IS_NOT_SUPPORTED: + Output.get().sendError(sender, "The currency, %s, is not supported by any " + + "loaded economies.", results.getTargetRank().currency); + break; + + case IN_PROGRESS: + Output.get().sendError(sender, "Rankup failed to complete normally. No status was set."); + break; + default: + break; } + } + /** + *

Gets a player by name. If the player is not online, then try to get them from + * the offline player list. If not one is found, then return a null. + *

+ * + * @param sender + * @param playerName is optional, if not supplied, then sender will be used + * @return Player if found, or null. + */ + private Player getPlayer( CommandSender sender, String playerName ) { + Player result = null; + + playerName = playerName != null ? playerName : sender != null ? sender.getName() : null; + + //Output.get().logInfo("RanksCommands.getPlayer :: playerName = " + playerName ); + + if ( playerName != null ) { + Optional opt = Prison.get().getPlatform().getPlayer( playerName ); + if ( !opt.isPresent() ) { + opt = Prison.get().getPlatform().getOfflinePlayer( playerName ); + } + if ( opt.isPresent() ) { + result = opt.get(); + } + } + return result; + } + + + + private void broadcastToWholeServer( CommandSender sender, String message ) { + + String broadcastRankups = Prison.get().getPlatform().getConfigString( "broadcast-rankups" ); + + if ( broadcastRankups == null || broadcastRankups.equalsIgnoreCase( "true" ) ) { + + Player player = getPlayer( sender, sender.getName() ); + List players = Prison.get().getPlatform().getOnlinePlayers(); + + for ( Player p : players ) { + if ( !p.equals( player ) ) { + p.sendMessage( message ); + } + } + } } - + } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommands.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommands.java index 7ba23a603..bdea7aef7 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommands.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommands.java @@ -1,9 +1,21 @@ package tech.mcprison.prison.ranks.commands; +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.chat.FancyMessage; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; +import tech.mcprison.prison.integration.EconomyCurrencyIntegration; import tech.mcprison.prison.internal.CommandSender; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.output.BulletedListComponent; import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.output.FancyMessageComponent; @@ -11,34 +23,21 @@ import tech.mcprison.prison.ranks.PrisonRanks; import tech.mcprison.prison.ranks.data.Rank; import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.data.RankLadder.PositionRank; import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.data.RankPlayerName; +import tech.mcprison.prison.ranks.managers.PlayerManager; import tech.mcprison.prison.util.Text; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - /** * @author Faizaan A. Datoo */ public class RanksCommands { - - @Command(identifier = "ranks", onlyPlayers = false) - public void baseCommand(CommandSender sender, - @Arg(name = "ladder", def = "default") String ladderName) { - if (!sender.hasPermission("ranks.admin")) { - sender.dispatchCommand("ranks list " + ladderName); - } else { - sender.dispatchCommand("ranks help"); - } - } - - @Command(identifier = "ranks create", description = "Creates a new rank", onlyPlayers = false, permissions = "ranks.create") + @Command(identifier = "ranks create", description = "Creates a new rank", + onlyPlayers = false, permissions = "ranks.create") public void createRank(CommandSender sender, - @Arg(name = "name", description = "The name of this rank.") String name, + @Arg(name = "rankName", description = "The name of this rank.") String name, @Arg(name = "cost", description = "The cost of this rank.") double cost, @Arg(name = "ladder", description = "The ladder to put this rank on.", def = "default") String ladder, @@ -51,6 +50,12 @@ public void createRank(CommandSender sender, .sendWarn(sender, "A rank by this name already exists. Try a different name."); return; } + + // Ensure a rank with the name doesn't already exist + if (name == null || name.trim().length() == 0 || name.contains( "&" )) { + Output.get().sendWarn(sender, "A rank name is required and cannot contain formatting codes."); + return; + } // Fetch the ladder first, so we can see if it exists @@ -105,8 +110,9 @@ public void createRank(CommandSender sender, } - @Command(identifier = "ranks delete", description = "Removes a rank, and deletes its files.", onlyPlayers = false, permissions = "ranks.delete") - public void removeRank(CommandSender sender, @Arg(name = "name") String rankName) { + @Command(identifier = "ranks delete", description = "Removes a rank, and deletes its files.", + onlyPlayers = false, permissions = "ranks.delete") + public void removeRank(CommandSender sender, @Arg(name = "rankName") String rankName) { // Check to ensure the rank exists Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); if (!rankOptional.isPresent()) { @@ -132,38 +138,81 @@ public void removeRank(CommandSender sender, @Arg(name = "name") String rankName } } - @Command(identifier = "ranks list", description = "Lists all the ranks on the server.", onlyPlayers = false, permissions = "ranks.list") + @Command(identifier = "ranks list", description = "Lists all the ranks on the server.", + onlyPlayers = false, permissions = "ranks.list") public void listRanks(CommandSender sender, @Arg(name = "ladderName", def = "default") String ladderName) { - Optional ladder = - PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); + Optional ladderOpt = + PrisonRanks.getInstance().getLadderManager().getLadder(ladderName); - if (!ladder.isPresent()) { + if (!ladderOpt.isPresent()) { Output.get().sendError(sender, "The ladder '%s' doesn't exist.", ladderName); return; } - List ranks = ladder.get().ranks; + RankLadder ladder = ladderOpt.get(); + Rank rank = null; + for (PositionRank pRank : ladder.ranks) { + Optional rankOptional = ladder.getByPosition(pRank.getPosition()); + if (rankOptional.isPresent()) { + rank = rankOptional.get(); + break; + } + } + ChatDisplay display = new ChatDisplay("Ranks in " + ladderName); - display.text("&8Click on a rank's name to view more info."); + display.text("&7Click on a rank's name to view more info."); BulletedListComponent.BulletedListBuilder builder = new BulletedListComponent.BulletedListBuilder(); - for (RankLadder.PositionRank pos : ranks) { - Optional rankOptional = ladder.get().getByPosition(pos.getPosition()); - if (!rankOptional.isPresent()) { - continue; // Skip it - } - Rank rank = rankOptional.get(); - + + boolean first = true; + while ( rank != null ) { + + boolean defaultRank = ("default".equalsIgnoreCase( ladderName ) && first); + String text = - String.format("&3%s&r &8- &7%s", rank.tag, Text.numberToDollars(rank.cost)); - FancyMessage msg = new FancyMessage(text).command("/ranks info " + rank.name) + String.format("&3%s &9[&3%s&9] &7- %s&7%s%s &7- Commands: &3%d", + rank.name, rank.tag, + (defaultRank ? "&b(&9Default&b) &7-" : ""), + Text.numberToDollars(rank.cost), + (rank.currency == null ? "" : " &7Currency: &3" + rank.currency), + rank.rankUpCommands.size()); + + String rankName = rank.name; + if ( rankName.contains( "&" ) ) { + rankName = rankName.replace( "&", "-" ); + } + FancyMessage msg = new FancyMessage(text).command("/ranks info " + rankName) .tooltip("&7Click to view info."); builder.add(msg); + + rank = rank.rankNext; + first = false; } + +// for (RankLadder.PositionRank pos : ranks) { +// Optional rankOptional = ladder.get().getByPosition(pos.getPosition()); +// if (!rankOptional.isPresent()) { +// continue; // Skip it +// } +// Rank rank = rankOptional.get(); +// +// boolean defaultRank = ("default".equalsIgnoreCase( ladderName ) && first); +// +// String text = +// String.format("&3%s &9[&3%s&9] &7- %s&7%s &7- Commands: &3%d", +// rank.name, rank.tag, +// (defaultRank ? "&b(&9Default&b) &7-" : ""), +// Text.numberToDollars(rank.cost), +// rank.rankUpCommands.size()); +// FancyMessage msg = new FancyMessage(text).command("/ranks info " + rank.name) +// .tooltip("&7Click to view info."); +// builder.add(msg); +// first = false; +// } display.addComponent(builder.build()); display.addComponent(new FancyMessageComponent( @@ -200,40 +249,55 @@ public void listRanks(CommandSender sender, } - @Command(identifier = "ranks info", description = "Information about a rank.", onlyPlayers = false, permissions = "ranks.info") + @Command(identifier = "ranks info", description = "Information about a rank.", + onlyPlayers = false, permissions = "ranks.info", + altPermissions = "ranks.admin" ) public void infoCmd(CommandSender sender, @Arg(name = "rankName") String rankName) { - Optional rank = PrisonRanks.getInstance().getRankManager().getRank(rankName); - if (!rank.isPresent()) { - Output.get().sendError(sender, "The rank '%s' doesn't exist.", rankName); - return; + + Optional rankOpt = PrisonRanks.getInstance().getRankManager().getRank(rankName); + if (!rankOpt.isPresent()) { + rankOpt = PrisonRanks.getInstance().getRankManager().getRankEscaped(rankName); + if (!rankOpt.isPresent()) { + Output.get().sendError(sender, "The rank '%s' doesn't exist.", rankName); + return; + } } + Rank rank = rankOpt.get(); + List ladders = - PrisonRanks.getInstance().getLadderManager().getLaddersWithRank(rank.get().id); + PrisonRanks.getInstance().getLadderManager().getLaddersWithRank(rank.id); - ChatDisplay display = new ChatDisplay("Rank " + rank.get().tag); + ChatDisplay display = new ChatDisplay("Rank " + rank.tag); + + display.text("&3Rank Name: &7%s", rank.name); + display.text("&3Rank Tag: &7%s", rank.tag); + // (I know this is confusing) Ex. Ladder(s): default, test, and test2. display.text("&3%s: &7%s", Text.pluralize("Ladder", ladders.size()), Text.implodeCommaAndDot( ladders.stream().map(rankLadder -> rankLadder.name).collect(Collectors.toList()))); - display.text("&3Cost: &7%s", Text.numberToDollars(rank.get().cost)); + display.text("&3Cost: &7%s", Text.numberToDollars(rank.cost)); + + display.text("&3Currency: &7<&a%s&7>", (rank.currency == null ? "&cnone" : rank.currency) ); + + List players = + PrisonRanks.getInstance().getPlayerManager().getPlayers().stream() + .filter(rankPlayer -> rankPlayer.getRanks().values().contains(rank)) + .collect(Collectors.toList()); + display.text("&7There %s &3%s players &7with this rank.", + (players.size() == 1 ? "is": "are"), + players.size() + ""); if (sender.hasPermission("ranks.admin")) { // This is admin-exclusive content display.text("&8[Admin Only]"); - display.text("&6Rank ID: &7%s", rank.get().id); - display.text("&6Rank Name: &7%s", rank.get().name); - - List players = - PrisonRanks.getInstance().getPlayerManager().getPlayers().stream() - .filter(rankPlayer -> rankPlayer.getRanks().values().contains(rank.get())) - .collect(Collectors.toList()); - display.text("&7There are &6%s &7with this rank.", players.size() + " players"); + display.text("&6Rank ID: &7%s", rank.id); FancyMessage del = - new FancyMessage("&7[&c-&7] Delete").command("/ranks delete " + rank.get().name) + new FancyMessage("&7[&c-&7] Delete").command("/ranks delete " + rank.name) .tooltip("&7Click to delete this rank.\n&cYou may not reverse this action."); display.addComponent(new FancyMessageComponent(del)); } @@ -242,8 +306,11 @@ public void infoCmd(CommandSender sender, @Arg(name = "rankName") String rankNam } // set commands - @Command(identifier = "ranks set cost", description = "Modifies a ranks cost", onlyPlayers = false, permissions = "ranks.set") - public void setCost(CommandSender sender, @Arg(name = "name") String rankName, @Arg(name = "cost", description = "The cost of this rank.") double cost){ + @Command(identifier = "ranks set cost", description = "Modifies a ranks cost", + onlyPlayers = false, permissions = "ranks.set") + public void setCost(CommandSender sender, + @Arg(name = "rankName") String rankName, + @Arg(name = "cost", description = "The cost of this rank.") double cost){ Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); if (!rankOptional.isPresent()) { Output.get().sendError(sender, "The rank '%s' doesn't exist.", rankName); @@ -261,12 +328,58 @@ public void setCost(CommandSender sender, @Arg(name = "name") String rankName, @ } catch (IOException e) { Output.get().sendError(sender, "The rank could not be saved to disk. The change in rank cost has not been saved. Check the console for details."); - Output.get().logError("Rank could not be written to disk.", e); + Output.get().logError("Rank could not be written to disk (setCost).", e); } } + + // set commands + @Command(identifier = "ranks set currency", description = "Modifies a ranks currency", + onlyPlayers = false, permissions = "ranks.set") + public void setCurrency(CommandSender sender, + @Arg(name = "rankName") String rankName, + @Arg(name = "currency", description = "The currency to use with this rank.") String currency){ + + Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); + if (!rankOptional.isPresent()) { + Output.get().sendError(sender, "The rank '%s' doesn't exist.", rankName); + return; + } + + + if ( currency == null || currency.trim().length() == 0 ) { + Output.get().sendError(sender, "A currency name must be specified. '%s' is invalid.", currency); + return; + } + + + EconomyCurrencyIntegration currencyEcon = PrisonAPI.getIntegrationManager() + .getEconomyForCurrency( currency ); + if ( currencyEcon == null ) { + Output.get().sendError(sender, "No active economy supports the currency named '%s'.", currency); + return; + } + + + Rank rank = rankOptional.get(); + rank.currency = currency; + + // Save the rank + try { + PrisonRanks.getInstance().getRankManager().saveRank(rank); + + Output.get().sendInfo(sender,"Successfully set the currency for the rank '%s' to %s", rankName, currency); + } catch (IOException e) { + Output.get().sendError(sender, + "The rank could not be saved to disk. The change in rank currency has not been saved. Check the console for details."); + Output.get().logError("Rank could not be written to disk (setCurrency).", e); + } + } - @Command(identifier = "ranks set tag", description = "Modifies a ranks tag", onlyPlayers = false, permissions = "ranks.set") - public void setTag(CommandSender sender, @Arg(name = "name") String rankName, @Arg(name = "tag", description = "The desired tag.") String tag){ + @Command(identifier = "ranks set tag", description = "Modifies a ranks tag", + onlyPlayers = false, permissions = "ranks.set") + public void setTag(CommandSender sender, + @Arg(name = "rankName") String rankName, + @Arg(name = "tag", description = "The desired tag.") String tag){ Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); if (!rankOptional.isPresent()) { Output.get().sendError(sender, "The rank '%s' doesn't exist.", rankName); @@ -288,4 +401,238 @@ public void setTag(CommandSender sender, @Arg(name = "name") String rankName, @A } } + + @Command(identifier = "ranks player", description = "Shows a player their rank", onlyPlayers = false) + public void rankPlayer(CommandSender sender, + @Arg(name = "player", def = "", description = "Player name") String playerName){ + + Player player = getPlayer( sender, playerName ); + + if (player == null) { + sender.sendMessage( "&3You must be a player in the game to run this command, and/or the player must be online." ); + return; + } + + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + Optional oPlayer = pm.getPlayer(player.getUUID(), player.getName()); + + if ( oPlayer.isPresent() ) { + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + + RankPlayer rankPlayer = oPlayer.get(); + Map rankLadders = rankPlayer.getRanks(); + + for ( RankLadder rankLadder : rankLadders.keySet() ) + { + Rank rank = rankLadders.get( rankLadder ); + Rank nextRank = rank.rankNext; + + String messageRank = String.format("&c%s&7: Ladder: &b%s &7Current Rank: &b%s", + player.getDisplayName(), + rankLadder.name, + rank.name ); + + if ( nextRank == null ) { + messageRank += " It's the highest rank!"; + } else { + messageRank += String.format(" &7Next rank: &b%s&7 &c$&b%s", + nextRank.name, + dFmt.format( nextRank.cost )); + + if ( nextRank.currency != null ) { + messageRank += String.format(" &7Currency: &b%s", + nextRank.currency); + } + } + + sendToPlayerAndConsole( sender, messageRank ); + } + + if (sender.hasPermission("ranks.admin") && rankPlayer.names.size() > 1) { + // This is admin-exclusive content + + sendToPlayerAndConsole( sender, "&8[Admin Only]" ); + sendToPlayerAndConsole( sender, " &7Past Player Names and Date Changed:" ); + + for ( RankPlayerName rpn : rankPlayer.names ) { + + sendToPlayerAndConsole( sender, " &b" + rpn.toString() ); + } + + + } + +// String nextRank = pm.getPlayerNextRankName( rankPlayer ); +// String nextRankCost = pm.getPlayerNextRankCost( rankPlayer ); +// +// String message = String.format("&c%s&7: Current Rank: &b%s&7", +// player.getDisplayName(), pm.getPlayerRankName( rankPlayer )); +// +// if ( nextRank.trim().length() == 0 ) { +// message += " It's the highest rank!"; +// } else { +// message += String.format(" Next rank: &b%s&7 &c$&b%s &7s%", +// nextRank, nextRankCost, currency ); +// } +// sender.sendMessage( message ); + + } else { + sender.sendMessage( "&3No ranks found for &c" + player.getDisplayName() ); + } + } + + private void sendToPlayerAndConsole( CommandSender sender, String messageRank ) + { + // If not a console user then send the message to the sender, other wise if a console + // user then they will see duplicate messages: + if ( sender.getName() != null && !sender.getName().equalsIgnoreCase( "console" ) ) { + sender.sendMessage( messageRank ); + } + + // log the rank. There was one issue with the ranks suddenly being changed so this + // will help document what ranks were. + Output.get().logInfo( messageRank ); + } + + + @Command(identifier = "ranks players", description = "Shows all ranks with player counts", onlyPlayers = false) + public void rankPlayers(CommandSender sender, + @Arg(name = "ladderName", def = "all", description = "Ladder Name [all, none, LadderName]") String ladderName, + @Arg(name = "action", def = "players", description = "List type [players, all]") String action){ + + + if ( !ladderName.equalsIgnoreCase( "all" ) && + !PrisonRanks.getInstance().getLadderManager().getLadder( ladderName ).isPresent() ) { + Output.get().sendError(sender, "The ladder '%s' doesn't exist, or was not ALL.", ladderName); + return; + } + + + if ( !action.equalsIgnoreCase( "players" ) && !action.equalsIgnoreCase( "all" ) ) { + Output.get().sendError(sender, "The action '%s' is invalid. [players, all]", action); + + return; + } + + boolean includeAll = action.equalsIgnoreCase( "all" ); + PrisonRanks.getInstance().getRankManager().ranksByLadders( sender, ladderName, includeAll ); + +// Output.get().logInfo( "Ranks by ladders:" ); +// +// for ( RankLadder ladder : PrisonRanks.getInstance().getLadderManager().getLadders() ) { +// if ( ladderName.equalsIgnoreCase( "all" ) || ladderName.equalsIgnoreCase( ladder.name ) ) { +// +// boolean includeAll = action.equalsIgnoreCase( "all" ); +// String ladderRanks = ladder.listAllRanks( includeAll ); +// +// sender.sendMessage( ladderRanks ); +// } +// +// } + + } + + + +// /** +// * This function is just an arbitrary test to access the various components. +// * +// * @param sender +// * @param playerName +// */ +// @Command( identifier = "ranks test", onlyPlayers = false, permissions = "prison.admin" ) +// public void prisonModuleTest(CommandSender sender, +// @Arg(name = "player", def = "", description = "Player name") String playerName){ +// +// ModuleManager modMan = Prison.get().getModuleManager(); +// Module module = modMan == null ? null : modMan.getModule( PrisonRanks.MODULE_NAME ).orElse( null ); +// +// int moduleCount = (modMan == null ? 0 : modMan.getModules().size()); +// sender.sendMessage(String.format( "prisonModuleTest: prison=%s moduleManager=%s " + +// "registeredModules=%s PrisonRanks=%s", +// (Prison.get() == null ? "null" : "active"), +// (Prison.get().getModuleManager() == null ? "null" : "active"), +// Integer.toString( moduleCount ), +// (modMan.getModule( PrisonRanks.MODULE_NAME ) == null ? "null" : "active") +// ) ); +// +// if ( module == null || !(module instanceof PrisonRanks) ) { +// +// sender.sendMessage( "prisonModuleTest: Cannot get PrisonRanks. Terminating" ); +// return; +// } +// +// +// PrisonRanks rankPlugin = (PrisonRanks) module; +// +// if ( rankPlugin == null || rankPlugin.getPlayerManager() == null ) { +// sender.sendMessage( "prisonModuleTest: PrisonRanks could not be created. Terminating" ); +// return; +// } +// +// +// PlayerManager playerManager = rankPlugin.getPlayerManager(); +// Player player = getPlayer( sender, playerName ); +// +// sender.sendMessage( String.format( "prisonModuleTest: PlayerManager=%s player=%s sender=%s playerName=%s", +// (playerManager == null ? "null" : "active"), (player == null ? "null" : player.getName()), +// (sender == null ? "null" : sender.getName()), (playerName == null ? "null" : playerName) +// )); +// +// +// if ( player == null ) { +// sender.sendMessage( "prisonModuleTest: Cannot get a valid player. " + +// "If console, must supply a valid name. Terminating" ); +// return; +// } +// +// RankPlayer rPlayer = playerManager.getPlayer( player.getUUID() ).orElse( null ); +// LadderManager lm = rankPlugin.getLadderManager(); +// +// for ( RankLadder ladderData : lm.getLadders() ) { +// Rank playerRank = rPlayer == null ? null : rPlayer.getRank( ladderData ).orElse( null ); +// Rank rank = ladderData.getLowestRank().orElse( null ); +// +// while ( rank != null ) { +// boolean playerHasThisRank = playerRank != null && playerRank.equals( rank ); +// +// sender.sendMessage(String.format( "prisonModuleTest: ladder=%s rank=%s playerRank=%s hasRank=%s", +// ladderData.name, rank.name, (playerRank == null ? "null" : playerRank.name ), +// Boolean.valueOf( playerHasThisRank ).toString() +// )); +// +// rank = rank.rankNext; +// } +// } +// } + + + /** + *

Gets a player by name. If the player is not online, then try to get them from + * the offline player list. If not one is found, then return a null. + *

+ * + * @param sender + * @param playerName is optional, if not supplied, then sender will be used + * @return Player if found, or null. + */ + private Player getPlayer( CommandSender sender, String playerName ) { + Player result = null; + + playerName = playerName != null ? playerName : sender != null ? sender.getName() : null; + + //Output.get().logInfo("RanksCommands.getPlayer :: playerName = " + playerName ); + + if ( playerName != null ) { + Optional opt = Prison.get().getPlatform().getPlayer( playerName ); + if ( !opt.isPresent() ) { + opt = Prison.get().getPlatform().getOfflinePlayer( playerName ); + } + if ( opt.isPresent() ) { + result = opt.get(); + } + } + return result; + } + } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLadders.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLadders.java new file mode 100644 index 000000000..13b6865f0 --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLadders.java @@ -0,0 +1,55 @@ +package tech.mcprison.prison.ranks.data; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.sorting.PrisonSorter; + +public class PrisonSortableLadders + extends PrisonSorter +{ + + protected class PrisonSortComparableLadders + implements Comparator + { + + @Override + public int compare( RankLadder l1, RankLadder l2 ) { + int results = 0; + + if ( l1 == null ) { + results = -1; + } + else if ( l2 == null ) { + results = 1; + } + else if ( "default".equalsIgnoreCase( l1.name ) ) { + results = -999999; + } + else if ( "prestige".equalsIgnoreCase( l1.name )) { + results = 999999; + } + else { + results = l1.name.toLowerCase().compareTo( l2.name.toLowerCase() ); + } + + return results; + } + + } + + @Override + public Set getSortedSet() + { + TreeSet ladders = new TreeSet<>( new PrisonSortComparableLadders() ); + + List unsortedLadders = PrisonRanks.getInstance().getLadderManager().getLadders(); + + ladders.addAll( unsortedLadders ); + + return ladders; + } +} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersRanks.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersRanks.java new file mode 100644 index 000000000..249b17093 --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersRanks.java @@ -0,0 +1,65 @@ +package tech.mcprison.prison.ranks.data; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.sorting.PrisonSorter; + +public class PrisonSortableLaddersRanks + extends PrisonSorter { + + + @Override + public Set getSortedSet() + { + Set results = new LinkedHashSet<>(); + + // Get a sorted ladder set: + PrisonSortableLadders psl = new PrisonSortableLadders(); + TreeSet ladders = new TreeSet<>( psl.new PrisonSortComparableLadders() ); + List unsortedLadders = PrisonRanks.getInstance().getLadderManager().getLadders(); + ladders.addAll( unsortedLadders ); + + + List unsortedRanks = PrisonRanks.getInstance().getRankManager().getRanks(); + + + // We may not want to sort the ranks within the ladders by alpha. + // the rank list we get from the ladders is in rank-order so just use that. + // The following code for PrisonSortableRanks is shown here and commented out + // to serve as a guide on how you "could" perform multi-tiered sorting + // through the use of multiple comparators and collections. + PrisonSortableRanks psr = new PrisonSortableRanks(); + TreeSet ranksSorted = new TreeSet<>( psr.new PrisonSortComparableRanks() ); + + + for ( RankLadder rankLadder : ladders ) { + List rankList = rankLadder.getRanks(); + + // Perform the sort of the ranks: +// ranksSorted.addAll( rankList ); + + // Add the ranks to the result set in sorted order: + results.addAll( rankList ); +// results.addAll( ranksSorted ); + + // Remove from the unsortedRanks list, all ranks that were used: + unsortedRanks.removeAll( rankList ); +// unsortedRanks.removeAll( ranksSorted ); + } + + + // Finally if there are any leftover ranks that have not yet been added + // then sort there before adding to the results. + + ranksSorted.addAll( unsortedRanks ); + results.addAll( ranksSorted ); + + + return results; + } + +} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableRanks.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableRanks.java new file mode 100644 index 000000000..fc0dc903d --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/PrisonSortableRanks.java @@ -0,0 +1,57 @@ +package tech.mcprison.prison.ranks.data; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.sorting.PrisonSorter; + +/** + *

This class sorts all ranks by alphabetical order. Ladders are not + * considered in this sorting. + *

+ * + */ +public class PrisonSortableRanks + extends PrisonSorter { + + + protected class PrisonSortComparableRanks + implements Comparator + { + + @Override + public int compare( Rank r1, Rank r2 ) { + int results = 0; + + if ( r1 == null ) { + results = -1; + } + else if ( r2 == null ) { + results = 1; + } + else { + results = r1.name.toLowerCase().compareTo( r2.name.toLowerCase() ); + } + + return results; + } + + } + + + @Override + public Set getSortedSet() + { + TreeSet ranks = new TreeSet<>( new PrisonSortComparableRanks() ); + + List unsortedRanks = PrisonRanks.getInstance().getRankManager().getRanks(); + + ranks.addAll( unsortedRanks ); + + return ranks; + } + +} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/Rank.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/Rank.java index a8e7fe0ef..1ab0ec6b9 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/Rank.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/Rank.java @@ -17,7 +17,9 @@ package tech.mcprison.prison.ranks.data; +import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.RankUtil; +import tech.mcprison.prison.sorting.PrisonSortable; import tech.mcprison.prison.store.Document; import java.util.List; @@ -27,7 +29,8 @@ * * @author Faizaan A. Datoo */ -public class Rank { +public class Rank + implements PrisonSortable { /* * Fields & Constants @@ -45,23 +48,64 @@ public class Rank { // The general cost of this rank, unit-independent. This value holds true for both XP and cost. public double cost; + /** + *

Special currency to use. If null, then will use the standard currencies. + * If currency is not null, then it must exist in a an economy that is + * supported in the EconomyCurrencyIntegration. + *

+ * + */ + public String currency; + // The commands that are run when this rank is attained. public List rankUpCommands; + + public transient Rank rankPrior; + public transient Rank rankNext; + /* * Document-related */ public Rank() { } + + /** + *

This is strictly used for testing only! + * Never use this function outside of a jUnit test case! + *

+ * + * @param id + * @param name + */ + protected Rank( String name ) { + this.id = 0; + this.name = name; + } @SuppressWarnings( "unchecked" ) public Rank(Document document) { - this.id = RankUtil.doubleToInt(document.get("id")); - this.name = (String) document.get("name"); - this.tag = (String) document.get("tag"); - this.cost = (double) document.get("cost"); - this.rankUpCommands = (List) document.get("commands"); + try + { + this.id = RankUtil.doubleToInt(document.get("id")); + this.name = (String) document.get("name"); + this.tag = (String) document.get("tag"); + this.cost = (double) document.get("cost"); + String currency = (String) document.get("currency"); + this.currency = (currency == null || + "null".equalsIgnoreCase( currency ) ? null : currency); + this.rankUpCommands = (List) document.get("commands"); + } + catch ( Exception e ) + { + Output.get().logError( + String.format( "&aFailure: Loading Ranks! &7Exception parsing rank documents. " + + "Rank id= %s name= %s [%s]", + Integer.toString( this.id ), (this.name == null ? "null" : this.name ), + e.getMessage()) + ); + } } public Document toDocument() { @@ -70,11 +114,17 @@ public Document toDocument() { ret.put("name", this.name); ret.put("tag", this.tag); ret.put("cost", this.cost); + ret.put("currency", this.currency); ret.put("commands", this.rankUpCommands); return ret; } + @Override + public String toString() { + return "Rank: " + id + " " + name; + } + public String filename() { return "rank_" + id; } @@ -100,6 +150,13 @@ public String filename() { if (Double.compare(rank.cost, cost) != 0) { return false; } + + if ( currency != null && rank.currency == null || + currency != null && rank.currency != null && + !currency.equals( rank.currency ) ) { + return false; + } + if (!name.equals(rank.name)) { return false; } @@ -114,6 +171,7 @@ public String filename() { result = 31 * result + (tag != null ? tag.hashCode() : 0); temp = Double.doubleToLongBits(cost); result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + currency.hashCode(); return result; } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java index 9aecb31c9..dfe95e6e7 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java @@ -17,17 +17,19 @@ package tech.mcprison.prison.ranks.data; -import com.google.gson.internal.LinkedTreeMap; - import java.util.ArrayList; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import com.google.gson.internal.LinkedTreeMap; + +import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.PrisonRanks; import tech.mcprison.prison.ranks.RankUtil; +import tech.mcprison.prison.ranks.managers.RankManager; +import tech.mcprison.prison.sorting.PrisonSortable; import tech.mcprison.prison.store.Document; /** @@ -36,7 +38,8 @@ * * @author Faizaan A. Datoo */ -public class RankLadder { +public class RankLadder + implements PrisonSortable { /* * Fields & Constants @@ -46,6 +49,8 @@ public class RankLadder { public String name; public List ranks; public int maxPrestige; + + private boolean dirty = false; /* * Document-related @@ -55,7 +60,10 @@ public RankLadder() { } @SuppressWarnings( "unchecked" ) - public RankLadder(Document document) { + public RankLadder(Document document, PrisonRanks prisonRanks) { + + RankManager rankManager = prisonRanks.getRankManager(); + this.id = RankUtil.doubleToInt(document.get("id")); this.name = (String) document.get("name"); List> ranksLocal = @@ -63,9 +71,29 @@ public RankLadder(Document document) { this.ranks = new ArrayList<>(); for (LinkedTreeMap rank : ranksLocal) { - ranks.add(new PositionRank(RankUtil.doubleToInt(rank.get("position")), - RankUtil.doubleToInt((rank.get("rankId"))))); + + int rPos = RankUtil.doubleToInt(rank.get("position")); + int rRankId = RankUtil.doubleToInt((rank.get("rankId"))); + String rRankName = (String) rank.get( "rankName" ); + Rank rankPrison = null; + + if ( rankManager != null && + rankManager.getRank( rRankId ).isPresent() ) { + + rankPrison = rankManager.getRank( rRankId ).get(); + + // if null look it up from loaded ranks: + if ( rRankName == null ) { + rRankName = rankPrison.name; + dirty = true; + } + } + + ranks.add(new PositionRank( rPos, rRankId, rRankName, rankPrison )); } + + this.maxPrestige = RankUtil.doubleToInt(document.get("maxPrestige")); + } public Document toDocument() { @@ -77,10 +105,35 @@ public Document toDocument() { return ret; } - /* - * Methods - */ - + @Override + public String toString() { + return "Ladder: " + name + " ranks: " + (ranks == null ? 0 : ranks.size()); + } + + public List getRanks() { + + List rankz = new ArrayList<>(); + + RankManager rankManager = PrisonRanks.getInstance().getRankManager(); + + for ( PositionRank rank : ranks ) { + + if ( rank.rank == null ) { + + Rank rnk = rankManager.getRank( rank.rankId ).get(); + if ( rnk != null ) { + rank.rank = rnk; + } + else { + Output.get().logWarn( "RankLadder.listAllRanks(): " + + "Could not get Rank from rankId: " + rank.rankId ); + } + } + rankz.add( rank.rank ); + } + + return rankz; + } /** * Add a rank to this ladder. @@ -96,17 +149,27 @@ public void addRank(int position, Rank rank) { ranks.stream().filter(positionRank -> positionRank.getPosition() >= finalPosition) .forEach(positionRank -> positionRank.setPosition(positionRank.getPosition() + 1)); - ranks.add(new PositionRank(position, rank.id)); + ranks.add(new PositionRank(position, rank.id, rank.name, rank)); + + // Ranks will be reordered within connectRanks() so don't sort here: + + // Reset the rank relationships: + PrisonRanks.getInstance().getRankManager().connectRanks(); } /** * Add a rank to this ladder. The rank's position will be set to the next available position * (i.e. at the end of the ladder). + * + * The sort the ladder based upon the * * @param rank The {@link Rank} to add. */ public void addRank(Rank rank) { - ranks.add(new PositionRank(getNextAvailablePosition(), rank.id)); + ranks.add(new PositionRank(getNextAvailablePosition(), rank.id, rank.name, rank)); + + // Reset the rank relationships: + PrisonRanks.getInstance().getRankManager().connectRanks(); } /** @@ -128,14 +191,23 @@ public void removeRank(int position) { break; } } + + // Reset the rank relationships: + PrisonRanks.getInstance().getRankManager().connectRanks(); } - /** - * Orders the ranks in the rank list of this ladder by their position, in ascending order. - */ - public void orderRanksByPosition() { - ranks.sort(Comparator.comparingInt(PositionRank::getPosition)); - } +// /** +// * Orders the ranks in the rank list of this ladder by their position, in ascending order. +// */ +// public void orderRanksByPosition() { +// +// // Do not sort here: +// //The ranks within a ladder will be sorted within the function connectRanks(): +// //ranks.sort(Comparator.comparingInt(PositionRank::getPosition)); +// +// // Reset the rank relationships: +// PrisonRanks.getInstance().getRankManager().connectRanks(); +// } /* * Getters & Setters @@ -199,7 +271,7 @@ public Optional getPrevious(int before) { ranks.stream().map(PositionRank::getPosition).sorted().collect(Collectors.toList()); int newIndex = positions.indexOf(before) - 1; - if (newIndex >= positions.size()) { + if (newIndex < 0) { return Optional.empty(); } @@ -255,7 +327,10 @@ private int getNextAvailablePosition() { return 0; // obviously, if it's empty, we want to start at the bottom } - orderRanksByPosition(); + //orderRanksByPosition(); + // Reset the rank relationships: + PrisonRanks.getInstance().getRankManager().connectRanks(); + return ranks.get(ranks.size() - 1).getPosition() + 1; } @@ -288,10 +363,24 @@ public class PositionRank { private int position; private int rankId; - - public PositionRank(int position, int rankId) { + private String rankName; + + /** + * Adds a link to the actual Rank. This will save a lot of busy + * work and can reduce the complexity of a lot of code. + */ + private transient Rank rank; + + /** + * + * @param position + * @param rankId + * @param rankName rankName is never used but makes it easier to read the saved files + */ + public PositionRank(int position, int rankId, String rankName, Rank rank ) { this.position = position; this.rankId = rankId; + this.rankName = rankName; } public int getPosition() { @@ -309,6 +398,33 @@ public int getRankId() { public void setRankId(int rankId) { this.rankId = rankId; } + + public String getRankName() + { + return rankName; + } + + public void setRankName( String rankName ) + { + this.rankName = rankName; + } + + public Rank getRank() + { + return rank; + } + + public void setRank( Rank rank ) + { + this.rank = rank; + } } + public boolean isDirty() { + return dirty; + } + public void setDirty( boolean dirty ) { + this.dirty = dirty; + } + } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java index 9da08696b..49b5385cf 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java @@ -17,16 +17,19 @@ package tech.mcprison.prison.ranks.data; -import com.google.gson.internal.LinkedTreeMap; -import tech.mcprison.prison.ranks.PrisonRanks; -import tech.mcprison.prison.ranks.RankUtil; -import tech.mcprison.prison.store.Document; - +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; +import com.google.gson.internal.LinkedTreeMap; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.RankUtil; +import tech.mcprison.prison.store.Document; + /** * Represents a player with ranks. * @@ -41,30 +44,69 @@ public class RankPlayer { public UUID uid; public HashMap ranks; // public HashMap prestige; // + + public List names; + + // Block name, count + public HashMap blocksMined; /* * Document-related */ public RankPlayer() { + super(); } @SuppressWarnings( "unchecked" ) public RankPlayer(Document document) { + this.uid = UUID.fromString((String) document.get("uid")); LinkedTreeMap ranksLocal = (LinkedTreeMap) document.get("ranks"); LinkedTreeMap prestigeLocal = (LinkedTreeMap) document.get("prestige"); + + LinkedTreeMap blocksMinedLocal = + (LinkedTreeMap) document.get("blocksMined"); + + Object namesListObject = document.get( "names" ); + this.ranks = new HashMap<>(); for (String key : ranksLocal.keySet()) { ranks.put(key, RankUtil.doubleToInt(ranksLocal.get(key))); } + this.prestige = new HashMap<>(); for (String key : prestigeLocal.keySet()) { prestige.put(key, RankUtil.doubleToInt(prestigeLocal.get(key))); } + + this.blocksMined = new HashMap<>(); + if ( blocksMinedLocal != null ) { + for (String key : blocksMinedLocal.keySet()) { + blocksMined.put(key, RankUtil.doubleToInt(blocksMinedLocal.get(key))); + } + } + + if ( namesListObject != null ) { + + for ( Object rankPlayerNameMap : (ArrayList) namesListObject ) { + LinkedTreeMap rpnMap = (LinkedTreeMap) rankPlayerNameMap; + + if ( rpnMap.size() > 0 ) { + String name = (String) rpnMap.get( "name" ); + long date = RankUtil.doubleToLong( rpnMap.get( "date" ) ); + + RankPlayerName rankPlayerName = new RankPlayerName( name, date ); + getNames().add( rankPlayerName ); +// Output.get().logInfo( "RankPlayer: uuid: " + uid + " RankPlayerName: " + rankPlayerName.toString() ); + } + + } + } + } public Document toDocument() { @@ -72,14 +114,54 @@ public Document toDocument() { ret.put("uid", this.uid); ret.put("ranks", this.ranks); ret.put("prestige", this.prestige); + + ret.put("names", this.names); + + ret.put("blocksMined", this.blocksMined); return ret; } /* * Methods */ + + + + public boolean checkName( String playerName ) { + boolean added = false; + + // Check if the last name in the list is not the same as the name passed: + if ( getNames().size() == 0 || + !getNames().get( getNames().size() - 1 ).getName().equalsIgnoreCase( playerName ) ) { + + RankPlayerName rpn = new RankPlayerName( playerName, System.currentTimeMillis() ); + getNames().add( rpn ); + + added = true; + } + + return added; + } + - /** + public List getNames() { + if ( names == null ) { + names = new ArrayList<>(); + } + return names; + } + public void setNames( List names ) { + this.names = names; + } + + public HashMap getBlocksMined() { + return blocksMined; + } + public void setBlocksMined( HashMap blocksMined ) { + this.blocksMined = blocksMined; + } + + /** *

This is a helper function to ensure that the given file name is * always generated correctly and consistently. *

@@ -163,6 +245,24 @@ public Optional getRank(RankLadder ladder) { int id = ranks.get(ladder.name); return PrisonRanks.getInstance().getRankManager().getRank(id); } + + /** + * Retrieves the rank that this player has the specified ladder. + * + * @param ladder The ladder name to check. + * @return The {@link Rank} if found, otherwise null; + */ + public Rank getRank(String ladder) { + Rank results = null; + if (ladder != null && ranks.containsKey(ladder)) { + int id = ranks.get(ladder); + Optional ladderOpt = PrisonRanks.getInstance().getRankManager().getRank(id); + if ( ladderOpt.isPresent() ) { + results = ladderOpt.get(); + } + } + return results; + } /** * Returns all ladders this player is a part of, along with each rank the player has in that ladder. diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerName.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerName.java new file mode 100644 index 000000000..2ae83f11c --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerName.java @@ -0,0 +1,39 @@ +package tech.mcprison.prison.ranks.data; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class RankPlayerName { + + private String name; + private long date; + + public RankPlayerName( String name, long date ) { + super(); + + this.name = name; + this.date = date; + } + + @Override + public String toString() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + return name + " " + sdf.format( new Date(date) ); + } + + public String getName() { + return name; + } + public void setName( String name ) { + this.name = name; + } + + public long getDate() { + return date; + } + public void setDate( long date ) { + this.date = date; + } + +} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/LadderManager.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/LadderManager.java index 0b74dc789..fcbb5f314 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/LadderManager.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/LadderManager.java @@ -42,6 +42,8 @@ public class LadderManager { private Collection collection; private List loadedLadders; + + private PrisonRanks prisonRanks; /* * Constructor @@ -49,10 +51,13 @@ public class LadderManager { /** * Instantiate this {@link LadderManager}. + * @param prisonRanks */ - public LadderManager(Collection collection) { + public LadderManager(Collection collection, PrisonRanks prisonRanks) { this.collection = collection; this.loadedLadders = new ArrayList<>(); + + this.prisonRanks = prisonRanks; } /* @@ -68,8 +73,13 @@ public LadderManager(Collection collection) { */ public void loadLadder(String fileKey) throws IOException { Document doc = collection.get(fileKey).orElseThrow(IOException::new); - RankLadder ladder = new RankLadder(doc); + RankLadder ladder = new RankLadder(doc, prisonRanks); loadedLadders.add(ladder); + + // Will be dirty if load a ladder and the rank name does not exist and it adds them: + if ( ladder.isDirty() ) { + saveLadder(ladder); + } } /** @@ -79,7 +89,14 @@ public void loadLadder(String fileKey) throws IOException { */ public void loadLadders() throws IOException { List documents = collection.getAll(); - documents.forEach(document -> loadedLadders.add(new RankLadder(document))); + documents.forEach(document -> loadedLadders.add(new RankLadder(document, prisonRanks))); + + for ( RankLadder ladder : loadedLadders ) { + // Will be dirty if load a ladder and the rank name does not exist and it adds them: + if ( ladder.isDirty() ) { + saveLadder(ladder); + } + } } /** diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/PlayerManager.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/PlayerManager.java index 4e7174bca..c7083010b 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/PlayerManager.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/PlayerManager.java @@ -17,35 +17,54 @@ package tech.mcprison.prison.ranks.managers; +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + import com.google.common.eventbus.Subscribe; + import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.EconomyCurrencyIntegration; +import tech.mcprison.prison.integration.EconomyIntegration; +import tech.mcprison.prison.integration.IntegrationManager; +import tech.mcprison.prison.integration.IntegrationManager.PlaceHolderFlags; +import tech.mcprison.prison.integration.IntegrationManager.PrisonPlaceHolders; +import tech.mcprison.prison.integration.IntegrationType; +import tech.mcprison.prison.integration.ManagerPlaceholders; +import tech.mcprison.prison.integration.PlaceHolderKey; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.events.player.PlayerJoinEvent; import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.RankUtil; +import tech.mcprison.prison.ranks.RankupResults; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.ranks.data.RankLadder; import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.ranks.events.FirstJoinEvent; import tech.mcprison.prison.store.Collection; import tech.mcprison.prison.store.Document; -import java.io.IOException; -import java.util.*; - /** * Manages all the players in the records. * * @author Faizaan A. Datoo */ -public class PlayerManager { +public class PlayerManager + implements ManagerPlaceholders { - /* - * Fields & Constants - */ private Collection collection; private List players; - /* - * Constructor - */ + private List translatedPlaceHolderKeys; + public PlayerManager(Collection collection) { this.collection = collection; @@ -127,35 +146,545 @@ public List getPlayers() { return players; } - public Optional getPlayer(UUID uid) { - return players.stream().filter(player -> player.uid.equals(uid)).findFirst(); + /** + *

Get the player, if they don't exist, add them. + *

+ * + * @param uid + * @return + */ + public Optional getPlayer(UUID uid, String playerName) { + Optional results = players.stream().filter(player -> player.uid.equals(uid)).findFirst(); + + if ( !results.isPresent() ) { + results = Optional.ofNullable( addPlayer(uid, playerName) ); + } + + // check to see if the name has changed, if so, then save because the new name was added: + if ( playerName != null && playerName.trim().length() > 0 && + results.get().checkName( playerName ) ) { + try { + savePlayer( results.get() ); + } + catch ( IOException e ) { + Output.get().logWarn( + String.format( "PlayerManager.getPlayer(): Failed to add new player name: %s. %s", + playerName, e.getMessage()) ); + } + } + + + return results; } + + + private RankPlayer addPlayer( UUID uid, String playerName ) { + // We need to create a new player data file. + RankPlayer newPlayer = new RankPlayer(); + newPlayer.uid = uid; + newPlayer.ranks = new HashMap<>(); + newPlayer.prestige = new HashMap<>(); + + players.add(newPlayer); + try { + savePlayer(newPlayer); + + // Assign the player to the default rank: + String ladder = null; // will set to the "default" ladder + String rank = null; // will set to the "default" rank + + // Set the rank to the default ladder and the default rank. The results are logged + // before the results are returned, so can ignore the results: + @SuppressWarnings( "unused" ) + RankupResults results = new RankUtil().setRank(newPlayer, ladder, rank, + playerName, "FirstJoinEvent"); + + + Prison.get().getEventBus().post(new FirstJoinEvent(newPlayer)); + } + catch (IOException e) { + Output.get().logError( + "Failed to create new player data file for player " + + (playerName == null ? "" : playerName) + + " target filename: " + newPlayer.filename(), e); + } + + return newPlayer; + } + /* * Listeners */ @Subscribe public void onPlayerJoin(PlayerJoinEvent event) { - if (!getPlayer(event.getPlayer().getUUID()).isPresent()) { - // We need to create a new player data file. - RankPlayer newPlayer = new RankPlayer(); - newPlayer.uid = event.getPlayer().getUUID(); - newPlayer.ranks = new HashMap<>(); - newPlayer.prestige = new HashMap<>(); + + Player player = event.getPlayer(); + + if (!getPlayer(player.getUUID(), player.getName()).isPresent()) { + addPlayer( player.getUUID(), player.getName() ); + } + } - players.add(newPlayer); + - try { - savePlayer(newPlayer); - - Prison.get().getEventBus().post(new FirstJoinEvent(newPlayer)); - } - catch (IOException e) { - Output.get().logError( - "Failed to create new player data file for player " + - event.getPlayer().getName() + " target filename: " + newPlayer.filename(), e); - } + public String getPlayerRankName( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + if ( ladderName == null || + ladderName != null && entry.getKey().name.equalsIgnoreCase( ladderName )) { + + if ( sb.length() > 0 ) { + sb.append(" "); + } + sb.append(entry.getValue().name); + } + } + } + + return sb.toString(); + } + + public String getPlayerRankTag( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + if ( ladderName == null || + ladderName != null && entry.getKey().name.equalsIgnoreCase( ladderName )) { + +// if ( sb.length() > 0 ) { +// sb.append(" "); +// } + sb.append(entry.getValue().tag); + } + } + } + + return sb.toString(); + } + + public List getPlayerRanks( RankPlayer rankPlayer ) { + List results = new ArrayList<>(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + results.add( entry.getValue() ); + } + } + + return results; + } + + public List getPlayerNextRanks( RankPlayer rankPlayer ) { + List results = new ArrayList<>(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + + RankLadder key = entry.getKey(); + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + + Rank nextRank = key.getNext(key.getPositionOfRank(entry.getValue())).get(); + results.add( nextRank ); + } + } + } + + return results; + } + + public String getPlayerNextRankCost( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + if ( !rankPlayer.getRanks().isEmpty()) { + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + if ( sb.length() > 0 ) { + sb.append(", "); + } + + double cost = key.getNext(key.getPositionOfRank(entry.getValue())).get().cost; + sb.append( dFmt.format( cost )); + } + } + } + } + + return sb.toString(); + } + + public String getPlayerNextRankCostPercent( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + Player prisonPlayer = PrisonAPI.getPlayer(rankPlayer.uid).orElse(null); + if( prisonPlayer == null ) { + Output.get().logError( String.format( "getPlayerNextRankCostPercent: " + + "Could not load player: %s", rankPlayer.uid) ); + return "0"; } + + if ( !rankPlayer.getRanks().isEmpty()) { + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + if ( sb.length() > 0 ) { + sb.append(", "); + } + + Rank rank = key.getNext(key.getPositionOfRank(entry.getValue())).get(); + double cost = rank.cost; + double balance = getPlayerBalance(prisonPlayer,rank); + + double percent = (balance < 0 ? 0 : + (cost == 0.0d || balance > cost ? 100.0 : + balance / cost * 100.0 ) + ); + sb.append( dFmt.format( percent )); + } + } + } + } + + return sb.toString(); + } + + public String getPlayerNextRankCostBar( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + Player prisonPlayer = PrisonAPI.getPlayer(rankPlayer.uid).orElse(null); + if( prisonPlayer == null ) { + Output.get().logError( String.format( "getPlayerNextRankCostBar: " + + "Could not load player: %s", rankPlayer.uid) ); + return "0"; + } + + if ( !rankPlayer.getRanks().isEmpty()) { + +// DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + if ( sb.length() > 0 ) { + sb.append(", "); + } + + Rank rank = key.getNext(key.getPositionOfRank(entry.getValue())).get(); + double cost = rank.cost; + double balance = getPlayerBalance(prisonPlayer,rank); + + + sb.append( Prison.get().getIntegrationManager(). + getProgressBar( balance, cost, false )); + + } + } + } + } + + return sb.toString(); + } + + public String getPlayerNextRankCostRemaining( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + Player prisonPlayer = PrisonAPI.getPlayer(rankPlayer.uid).orElse(null); + if( prisonPlayer == null ) { + Output.get().logError( String.format( "getPlayerNextRankCostRemaining: " + + "Could not load player: %s", rankPlayer.uid) ); + return "0"; + } + + if ( !rankPlayer.getRanks().isEmpty()) { + DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + if ( sb.length() > 0 ) { + sb.append(", "); + } + + Rank rank = key.getNext(key.getPositionOfRank(entry.getValue())).get(); + double cost = rank.cost; + double balance = getPlayerBalance(prisonPlayer,rank); + + double remaining = cost - balance; + + if ( remaining < 0 ) { + remaining = 0; + } + + sb.append( dFmt.format( remaining )); + } + } + } + } + + return sb.toString(); + } + + private double getPlayerBalance(Player player, Rank rank) { + double playerBalance = 0; + + if ( rank.currency != null ) { + EconomyCurrencyIntegration currencyEcon = PrisonAPI.getIntegrationManager() + .getEconomyForCurrency( rank.currency ); + if ( currencyEcon != null ) { + playerBalance = currencyEcon.getBalance( player, rank.currency ); + } else { + Output.get().logError( + String.format( "Failed to load Economy to get the balance for " + + "player %s with a currency of %s.", + player.getName(), rank.currency )); + } + + } else { + + EconomyIntegration economy = (EconomyIntegration) PrisonAPI.getIntegrationManager() + .getForType(IntegrationType.ECONOMY).orElse(null); + if ( economy != null ) { + playerBalance = economy.getBalance( player ); + } else { + Output.get().logError( + String.format( "Failed to load Economy to get the balance for player %s.", + player.getName() )); + } + } + + return playerBalance; } + + public String getPlayerNextRankName( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { + if ( sb.length() > 0 ) { + sb.append(" "); + } + sb.append(key.getNext(key.getPositionOfRank(entry.getValue())).get().name); + } + } + } + } + + return sb.toString(); + } + + public String getPlayerNextRankTag( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + if ( !rankPlayer.getRanks().isEmpty()) { + for (Map.Entry entry : rankPlayer.getRanks().entrySet()) { + RankLadder key = entry.getKey(); + if ( ladderName == null || + ladderName != null && key.name.equalsIgnoreCase( ladderName )) { + + if(key.getNext(key.getPositionOfRank(entry.getValue())).isPresent()) { +// if ( sb.length() > 0 ) { +// sb.append(", "); +// } + sb.append(key.getNext(key.getPositionOfRank(entry.getValue())).get().tag); + } + } + } + } + + return sb.toString(); + } + + public String getTranslatePlayerPlaceHolder( UUID playerUuid, String playerName, String identifier ) { + String results = null; + + if ( playerUuid != null ) { + + List placeHolderKeys = getTranslatedPlaceHolderKeys(); + + if ( !identifier.startsWith( IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED )) { + identifier = IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED + identifier; + } + + for ( PlaceHolderKey placeHolderKey : placeHolderKeys ) { + if ( placeHolderKey.getKey().equalsIgnoreCase( identifier )) { + results = getTranslatePlayerPlaceHolder( playerUuid, playerName, placeHolderKey ); + break; + } + } + } + + return results; + } + + public String getTranslatePlayerPlaceHolder( UUID playerUuid, String playerName, PlaceHolderKey placeHolderKey ) { + String results = null; + + if ( playerUuid != null ) { + + PrisonPlaceHolders placeHolder = placeHolderKey.getPlaceholder(); + + String ladderName = placeHolderKey.getData(); + + Optional oPlayer = getPlayer(playerUuid, playerName); + + if ( oPlayer.isPresent() ) { + RankPlayer rankPlayer = oPlayer.get(); + + switch ( placeHolder ) { + case prison_r: + case prison_rank: + case prison_r_laddername: + case prison_rank_laddername: + results = getPlayerRankName( rankPlayer, ladderName ); + break; + + case prison_rt: + case prison_rank_tag: + case prison_rt_laddername: + case prison_rank_tag_laddername: + results = getPlayerRankTag( rankPlayer, ladderName ); + break; + + case prison_rc: + case prison_rankup_cost: + case prison_rc_laddername: + case prison_rankup_cost_laddername: + results = getPlayerNextRankCost( rankPlayer, ladderName ); + break; + + case prison_rcp: + case prison_rankup_cost_percent: + case prison_rcp_laddername: + case prison_rankup_cost_percent_laddername: + results = getPlayerNextRankCostPercent( rankPlayer, ladderName ); + break; + + case prison_rcb: + case prison_rankup_cost_bar: + case prison_rcb_laddername: + case prison_rankup_cost_bar_laddername: + results = getPlayerNextRankCostBar( rankPlayer, ladderName ); + break; + + case prison_rcr: + case prison_rankup_cost_remaining: + case prison_rcr_laddername: + case prison_rankup_cost_remaining_laddername: + results = getPlayerNextRankCostRemaining( rankPlayer, ladderName ); + break; + + case prison_rr: + case prison_rankup_rank: + case prison_rr_laddername: + case prison_rankup_rank_laddername: + results = getPlayerNextRankName( rankPlayer, ladderName ); + break; + + case prison_rrt: + case prison_rankup_rank_tag: + case prison_rrt_laddername: + case prison_rankup_rank_tag_laddername: + results = getPlayerNextRankTag( rankPlayer, ladderName ); + break; + + default: + break; + } + } + } + + return results; + } + + @Override + public List getTranslatedPlaceHolderKeys() { + if ( translatedPlaceHolderKeys == null ) { + translatedPlaceHolderKeys = new ArrayList<>(); + + // This generates all of the placeholders for the player ranks: + List placeHolders = PrisonPlaceHolders.getTypes( PlaceHolderFlags.PLAYER ); + for ( PrisonPlaceHolders ph : placeHolders ) { + PlaceHolderKey placeholder = new PlaceHolderKey(ph.name(), ph ); + if ( ph.getAlias() != null ) { + String aliasName = ph.getAlias().name(); + placeholder.setAliasName( aliasName ); + } + + translatedPlaceHolderKeys.add( placeholder ); + + // Getting too many placeholders... add back the extended prefix when looking up: + +// // Now generate a new key based upon the first key, but without the prison_ prefix: +// String key2 = ph.name().replace( +// IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED, "" ); +// PlaceHolderKey placeholder2 = new PlaceHolderKey(key2, ph, false ); +// translatedPlaceHolderKeys.add( placeholder2 ); + } + + + // This generates all of the placeholders for the ladders: + placeHolders = PrisonPlaceHolders.getTypes( PlaceHolderFlags.LADDERS ); + + List ladders = PrisonRanks.getInstance().getLadderManager().getLadders(); + for ( RankLadder ladder : ladders ) { + for ( PrisonPlaceHolders ph : placeHolders ) { + String key = ph.name().replace( + IntegrationManager.PRISON_PLACEHOLDER_LADDERNAME_SUFFIX, "_" + ladder.name ). + toLowerCase(); + + PlaceHolderKey placeholder = new PlaceHolderKey(key, ph, ladder.name ); + if ( ph.getAlias() != null ) { + String aliasName = ph.getAlias().name().replace( + IntegrationManager.PRISON_PLACEHOLDER_LADDERNAME_SUFFIX, "_" + ladder.name ). + toLowerCase(); + placeholder.setAliasName( aliasName ); + } + translatedPlaceHolderKeys.add( placeholder ); + + // Getting too many placeholders... add back the extended prefix when looking up: + +// // Now generate a new key based upon the first key, but without the prison_ prefix: +// String key2 = key.replace( +// IntegrationManager.PRISON_PLACEHOLDER_PREFIX_EXTENDED, "" ); +// PlaceHolderKey placeholder2 = new PlaceHolderKey(key2, ph, ladder.name, false ); +// translatedPlaceHolderKeys.add( placeholder2 ); + + } + + } + } + + return translatedPlaceHolderKeys; + } + + @Override + public void reloadPlaceholders() { + + // clear the class variable so they will regenerate: + translatedPlaceHolderKeys = null; + + // Regenerate the translated placeholders: + getTranslatedPlaceHolderKeys(); + } } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankManager.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankManager.java index 403fc8d9e..12b1df02b 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankManager.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankManager.java @@ -17,19 +17,25 @@ package tech.mcprison.prison.ranks.managers; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.EconomyCurrencyIntegration; +import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.PrisonRanks; import tech.mcprison.prison.ranks.data.Rank; import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.data.RankLadder.PositionRank; import tech.mcprison.prison.store.Collection; import tech.mcprison.prison.store.Document; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - /** * Manages the creation, removal, and management of ranks. * @@ -136,6 +142,9 @@ public Optional createRank(String name, String tag, double cost) { // ... add it to the list... loadedRanks.add(newRank); + + // Reset the rank relationships: + connectRanks(); // ...and return it. return Optional.of(newRank); @@ -170,6 +179,14 @@ private int getNextAvailableId() { public Optional getRank(String name) { return loadedRanks.stream().filter(rank -> rank.name.equals(name)).findFirst(); } + + /** + * Returns the first rank that has an escaped name that has the & replaced with -. + */ + public Optional getRankEscaped(String name) { + return loadedRanks.stream().filter(rank -> + rank.name.replace( "&", "-" ).equals(name)).findFirst(); + } /** * Removes the provided rank. This will go through the process of removing the rank from the loaded @@ -227,6 +244,9 @@ public boolean removeRank(Rank rank) { // Remove it from the list... loadedRanks.remove(rank); + + // Reset the rank relationships: + connectRanks(); // ... and remove the rank's save files. collection.delete(rank.filename()); @@ -252,4 +272,178 @@ public List getRanks() { return loadedRanks; } + /** + *

This should be ran after the RanksManager and LadderManger are loaded. Or any + * time a rank is add, removed, or position changed within a ladder. + *

+ * + *

This function will set the temporal rankPrior and rankNext value in + * each rank based upon each ladder. This will greatly simplify walking the + * ladder by using the linked ranks without having to perform any expensive + * calculations. + *

+ */ + public void connectRanks() { + LadderManager lman = PrisonRanks.getInstance().getLadderManager(); + + for ( RankLadder rLadder : lman.getLadders() ) { + + rLadder.ranks.sort(Comparator.comparingInt(PositionRank::getPosition)); + + Rank rankLast = null; + for ( PositionRank pRank : rLadder.ranks ) { + if ( pRank != null && pRank.getPosition() >= 0 ) { + Optional opRank = rLadder.getByPosition(pRank.getPosition()); + if ( opRank.isPresent() ) { + Rank rank = opRank.get(); + + // reset the rankPrior and rankNext in case there are no hookups: + // Important if ranks are removed, or inserted, or moved: + rank.rankPrior = null; + rank.rankNext = null; + + if ( rankLast != null ) { + rank.rankPrior = rankLast; + rankLast.rankNext = rank; + } + rankLast = rank; + } + } + } + } + } + + /* + *

This function will go through ranks and find ranks that have defined currencies. + * When a currency is assigned to a rank, it is verified to be valid. So in theory + * all currencies "should" be valid. But plugins and setting can change. + *

+ * + *

By hitting all currencies up front when prison first loads, it "registers" + * all currencies with all economies that support currencies. This also allows + * error reporting to happen upon prison start up to report lost currencies. + * And it allows each economy plugin the chance to list all supported currencies. + *

+ * + *

This almost has to be proactive when prison loads, since there is no way to + * poll the economy plugins to find out what currencies it supports. At least its + * not a feature for GemsEconomy. + *

+ * + */ + public void identifyAllRankCurrencies() { + for ( Rank rank : loadedRanks ) { + if ( rank.currency != null ) { + EconomyCurrencyIntegration currencyEcon = PrisonAPI.getIntegrationManager() + .getEconomyForCurrency( rank.currency ); + if ( currencyEcon == null ) { + Output.get().logError( + String.format( "Economy Failure: &7The currency &a%s&7 was registered with " + + "rank &a%s&7, but it isn't supported by any Economy integration.", + rank.currency, rank.name) ); + } + } + } + + } + + + + public String listAllRanks( String ladderName, List ranks, boolean includeAll ) { + StringBuilder sb = new StringBuilder(); + + PlayerManager playerManager = PrisonRanks.getInstance().getPlayerManager(); + + for (Rank rank : ranks ) { + + // Get the players per rank!! + List playersList = + playerManager.getPlayers().stream() + .filter(rankPlayer -> rankPlayer.getRanks().values().contains(rank)) + .collect(Collectors.toList()); + int players = playersList.size(); + + if ( includeAll || !includeAll && players > 0 ) { + if ( sb.length() > 0 ) { + sb.append( "&2, " ); + } + + + sb.append( " &3" ); + sb.append( rank.name ); + + if ( players > 0 ) { + + sb.append( " &7" ); + sb.append( players ); + } + } + } + + sb.insert( 0, "&b: " ); + sb.insert( 0, ladderName ); + sb.insert( 0, " &7" ); + + return sb.toString(); + } + + + /** + *

Sends the output of ranksByLadders to the prison Output (console and logs). + *

+ * + * @param includeAll If true then includes all ranks, otherwise just ranks within one more players + */ + public void ranksByLadders( boolean includeAll ) { + ranksByLadders( null, "all", includeAll ); + } + + public void ranksByLadders( CommandSender sender, boolean includeAll ) { + ranksByLadders( sender, "all", includeAll ); + } + + public void ranksByLadders( CommandSender sender, String ladderName, boolean includeAll ) { + + rankByLadderOutput( sender, "&7Ranks by ladders:" ); + + // Track which ranks were included in the ladders listed: + List ranksIncluded = new ArrayList<>(); + + for ( RankLadder ladder : PrisonRanks.getInstance().getLadderManager().getLadders() ) { + if ( ladderName.equalsIgnoreCase( "all" ) || ladderName.equalsIgnoreCase( ladder.name ) ) { + + List ladderRanks = ladder.getRanks(); + ranksIncluded.addAll( ladderRanks ); + + String ranksByLadder = listAllRanks( ladder.name, ladderRanks, includeAll ); + + rankByLadderOutput( sender, ranksByLadder ); + } + } + + if ( ladderName.equalsIgnoreCase( "all" ) || ladderName.equalsIgnoreCase( "none" ) ) { + + // Next we need to get a list of all ranks that were not included. Use set + List ranksExcluded = new ArrayList<>( loadedRanks ); + ranksExcluded.removeAll( ranksIncluded ); + + // Next generate a list of ranks that are not associated with any ladder: + // NOTE: No players should be associated with ranks that are not tied to a ladder, + // so enable "true" for includeAll to list all ranks that are not tied to ladders + // since player count will always be zero. + String ranksByLadder = listAllRanks( "none", ranksExcluded, true ); + + rankByLadderOutput( sender, ranksByLadder ); + } + } + + private void rankByLadderOutput( CommandSender sender, String ranksByLadder ) { + if ( sender == null ) { + Output.get().logInfo( ranksByLadder ); + } + else { + sender.sendMessage( ranksByLadder ); + } + } + } diff --git a/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersTest.java b/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersTest.java new file mode 100644 index 000000000..17d7a0eeb --- /dev/null +++ b/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableLaddersTest.java @@ -0,0 +1,70 @@ +package tech.mcprison.prison.ranks.data; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +import org.junit.Test; + +public class PrisonSortableLaddersTest + extends PrisonSortableLadders +{ + + @Test + public void testGetSortedSet() + { + RankLadder ladderDefault = new RankLadder(); + ladderDefault.name = "default"; + RankLadder ladderPrestige = new RankLadder(); + ladderPrestige.name = "prestige"; + + RankLadder ladderMods = new RankLadder(); + ladderMods.name = "mods"; + RankLadder ladderAnimals = new RankLadder(); + ladderAnimals.name = "Animals"; + RankLadder ladderDonors = new RankLadder(); + ladderDonors.name = "Donors"; + RankLadder ladderZapps = new RankLadder(); + ladderZapps.name = "ZaPpS"; + + + List unsortedList = new ArrayList<>(); + unsortedList.add( ladderZapps ); + unsortedList.add( ladderDonors ); + unsortedList.add( ladderPrestige ); + unsortedList.add( ladderMods ); + unsortedList.add( ladderAnimals ); + unsortedList.add( ladderDefault ); + + + assertEquals( "ZaPpS", unsortedList.get( 0 ).name ); + assertEquals( "Donors", unsortedList.get( 1 ).name ); + assertEquals( "prestige", unsortedList.get( 2 ).name ); + assertEquals( "mods", unsortedList.get( 3 ).name ); + assertEquals( "Animals", unsortedList.get( 4 ).name ); + assertEquals( "default", unsortedList.get( 5 ).name ); + + TreeSet ladders = new TreeSet<>( new PrisonSortComparableLadders() ); + + ladders.addAll( unsortedList ); + + List sortedList = new ArrayList<>(ladders); + +// for ( RankLadder l : sortedList ) +// { +// System.out.println(l.toString()); +// } + + assertEquals( "default", sortedList.get( 0 ).name ); + assertEquals( "Animals", sortedList.get( 1 ).name ); + assertEquals( "Donors", sortedList.get( 2 ).name ); + assertEquals( "mods", sortedList.get( 3 ).name ); + assertEquals( "ZaPpS", sortedList.get( 4 ).name ); + assertEquals( "prestige", sortedList.get( 5 ).name ); + + + } + +} diff --git a/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableRanksTest.java b/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableRanksTest.java new file mode 100644 index 000000000..96080654d --- /dev/null +++ b/prison-ranks/src/test/java/tech/mcprison/prison/ranks/data/PrisonSortableRanksTest.java @@ -0,0 +1,57 @@ +package tech.mcprison.prison.ranks.data; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +import org.junit.Test; + +public class PrisonSortableRanksTest + extends + PrisonSortableRanks +{ + + @Test + public void test() + { + Rank rA = new Rank("A"); + Rank rB = new Rank("b"); + Rank rC = new Rank("c"); + Rank rD = new Rank("D"); + + + + List unsortedList = new ArrayList<>(); + unsortedList.add( rD ); + unsortedList.add( rC ); + unsortedList.add( rB ); + unsortedList.add( rA ); + + + assertEquals( "D", unsortedList.get( 0 ).name ); + assertEquals( "c", unsortedList.get( 1 ).name ); + assertEquals( "b", unsortedList.get( 2 ).name ); + assertEquals( "A", unsortedList.get( 3 ).name ); + + TreeSet ranks = new TreeSet<>( new PrisonSortComparableRanks() ); + + ranks.addAll( unsortedList ); + + List sortedList = new ArrayList<>(ranks); + + for ( Rank r : sortedList ) + { + System.out.println(r.toString()); + } + + assertEquals( "A", sortedList.get( 0 ).name ); + assertEquals( "b", sortedList.get( 1 ).name ); + assertEquals( "c", sortedList.get( 2 ).name ); + assertEquals( "D", sortedList.get( 3 ).name ); + + + } + +} diff --git a/prison-spigot/build.gradle b/prison-spigot/build.gradle index 2f57a1580..09b87d968 100644 --- a/prison-spigot/build.gradle +++ b/prison-spigot/build.gradle @@ -29,26 +29,55 @@ repositories { maven { url = "http://repo.mvdw-software.be/content/groups/public/" } maven { url = 'http://repo.extendedclip.com/content/repositories/placeholderapi/' } maven { url = 'https://mvnrepository.com/artifact/org.apache.commons/commons-lang3' } + + maven { url = 'https://mvnrepository.com/artifact/com.github.cryptomorin' } + // maven { url = 'https://maven.enginehub.org/repo/' } jcenter() } + + dependencies { - compile project(':prison-core') - compile project(':prison-mines') - compile project(':prison-ranks') - compile 'org.bstats:bstats-bukkit:1.5' - compile 'org.inventivetalent.spiget-update:bukkit:1.4.2-SNAPSHOT' - compile 'org.apache.commons:commons-lang3:3.9' - compile 'me.clip:placeholderapi:2.9.1' - - compileOnly 'org.spigotmc:spigot-api:1.9.4-R0.1-SNAPSHOT' + implementation project(':prison-core') + implementation project(':prison-mines') + implementation project(':prison-ranks') + implementation 'org.bstats:bstats-bukkit:1.5' + implementation 'org.inventivetalent.spiget-update:bukkit:1.4.2-SNAPSHOT' + implementation 'org.apache.commons:commons-lang3:3.9' + implementation 'me.clip:placeholderapi:2.9.1' + + implementation 'net.luckperms:api:5.0' + implementation 'me.lucko.luckperms:luckperms-api:4.0' + + + implementation 'com.github.cryptomorin:XSeries:7.2.1' +// implementation 'com.github.cryptomorin:XSeries:6.3.2.1' + + + // 1.9.4-R0.1-SNAPSHOT has been the version used for a long time: +// compileOnly 'org.spigotmc:spigot-api:1.9.4-R0.1-SNAPSHOT' + // 1.12.2-R0.1-SNAPSHOT works well: +// compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT' + // 1.13.2 fails since deprecated functions have been removed. + compileOnly 'org.spigotmc:spigot-api:1.13.2-R0.1-SNAPSHOT' + + compileOnly 'net.milkbowl.vault:VaultAPI:1.6' - compileOnly 'me.lucko.luckperms:luckperms-api:4.0' compileOnly('be.maximvdw:MVdWPlaceholderAPI:2.4.0-SNAPSHOT'){ exclude group: 'org.spigotmc' } - compileOnly fileTree(dir: 'lib', include: ['*.jar']) + + // compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.1' + compileOnly fileTree(dir: 'lib', include: ['*.jar'], exclude: ['TokenEnchantAPI-15.3.2.jar']) + compile fileTree(dir: 'lib', include: ['TokenEnchantAPI-15.3.2.jar']) + +} + +/* +clean { + delete "${rootProject.name}.jar" } +*/ processResources { from(sourceSets.main.resources.srcDirs) { @@ -63,20 +92,25 @@ processResources { shadowJar { dependencies { - include(dependency('org.apache.commons:commons-lang3:3.9')) + include(dependency('org.apache.commons:commons-lang3:3.10')) include(dependency('com.google.code.gson:gson:2.8.6')) include(dependency('org.bstats:bstats-bukkit:1.5')) include(dependency('me.clip:placeholderapi:2.9.1')) include(dependency('org.inventivetalent.spiget-update:bukkit:1.4.2-SNAPSHOT')) + include(dependency('com.github.cryptomorin:XSeries:7.2.1')) include(project(':prison-core')) include(project(':prison-mines')) include(project(':prison-ranks')) } relocate 'org.bstats.bukkit', 'tech.mcprison.prison.spigot' + relocate 'org.inventivetalent.update.spiget', 'tech.mcprison.prison.spiget' + relocate 'com.cryptomorin', 'tech.mcprison.prison.cryptomorin' classifier '' - version = null + archiveVersion = '' + +// minimize() } build.dependsOn(shadowJar) diff --git a/prison-spigot/lib/EssentialsX-2.17.1.0.jar b/prison-spigot/lib/EssentialsX-2.17.1.0.jar new file mode 100644 index 000000000..cf171570f Binary files /dev/null and b/prison-spigot/lib/EssentialsX-2.17.1.0.jar differ diff --git a/prison-spigot/lib/EssentialsX.jar b/prison-spigot/lib/EssentialsX.jar deleted file mode 100644 index 3aa857255..000000000 Binary files a/prison-spigot/lib/EssentialsX.jar and /dev/null differ diff --git a/prison-spigot/lib/GemsEconomy-4.8.3.jar b/prison-spigot/lib/GemsEconomy-4.8.3.jar new file mode 100644 index 000000000..62411fd49 Binary files /dev/null and b/prison-spigot/lib/GemsEconomy-4.8.3.jar differ diff --git a/prison-spigot/lib/SaneEconomyCore-0.13.1-SNAPSHOT.jar b/prison-spigot/lib/SaneEconomyCore-0.15.0-SNAPSHOT.jar similarity index 89% rename from prison-spigot/lib/SaneEconomyCore-0.13.1-SNAPSHOT.jar rename to prison-spigot/lib/SaneEconomyCore-0.15.0-SNAPSHOT.jar index 5b2704e66..899f197d0 100644 Binary files a/prison-spigot/lib/SaneEconomyCore-0.13.1-SNAPSHOT.jar and b/prison-spigot/lib/SaneEconomyCore-0.15.0-SNAPSHOT.jar differ diff --git a/prison-spigot/lib/TokenEnchantAPI-15.3.2.jar b/prison-spigot/lib/TokenEnchantAPI-15.3.2.jar new file mode 100644 index 000000000..1f8141c9a Binary files /dev/null and b/prison-spigot/lib/TokenEnchantAPI-15.3.2.jar differ diff --git a/prison-spigot/out/production/resources/plugin.yml b/prison-spigot/out/production/resources/plugin.yml index 36306670c..17a66bccf 100644 --- a/prison-spigot/out/production/resources/plugin.yml +++ b/prison-spigot/out/production/resources/plugin.yml @@ -3,8 +3,105 @@ main: tech.mcprison.prison.spigot.SpigotPrison version: "${version}" description: Prison is an all-in-one plugin for the Minecraft prison game mode. website: https://mc-prison.tech -softdepend: [Essentials, Vault, LuckPerms] - +softdepend: [Essentials, Vault, LuckPerms, Multiverse-Core, Multiworld, MVdWPlaceholderAPI, PlaceholderAPI] + +commands: + # =========== + # Commands from module: prison-mines MinesCommands: + # =========== + mines: + description: Root mines command. + + mines create: + description: Creates a new mine. + usage: | + / + / A -- Creates a mine named 'A' + + mines set spawn: + description: Set the mine's spawn to where you're standing and looking. + usage: | + / -- Sets the mine's spawn to where you're standing and looking. + / A -- Sets mine 'A's spawn location to where your standing and looking. + + mines block add: + description: Adds a block to a mine. + usage: | + / -- Adds a block to a mine. + / A GOLD_ORE 14.13 -- Adds a GOLD_ORE block to mine 'A' with a 14.13% chance of occurrance. + + mines block set: + description: Changes the percentage of a block in a mine. + usage: | + / -- Changes the percentage of a block in a mine. + / A GOLD_ORE 21.83 -- Changes the GOLD_ORE block occurrance rate to 21.83% for mine 'A' + + mines block remove: + description: Deletes a block from a mine. + usage: | + / -- Deletes a block from a mine + / A GOLD_ORE -- Deletes GOLD_ORE from mine 'A's list of blocks. + + mines block search: + description: Searches for a block to add to a mine. + usage: | + / -- Searches for a block based upon the search term and displays the given page number. + / cobble 2 -- Shows page two of the cobble results. + + mines delete: + description: Deletes a mine. + usage: | + / -- Deletes a specified mine. Have to enter command twice with confirmation the second time. + / A -- Request to delete mine 'A' -- Step 1 + / A confirm -- confirmation to delete mine 'A'. The mine will be soft deleted. Can recover by renaming save file on server harddrive. + / A cancel -- Cancels a mine delete. Same as not submitting a confirmation. + + mines info: + description: Lists information about a mine. + usage: | + / -- Lists information about a mine. + / A - Lists information about mine 'A' + + mines reset: + description: Resets a mine. + usage: | + / -- Resets a mine. + / A - Resets mine A. This also resets the count down timer for the next reset. + + mines list: + description: Lists all mines. + usage: | + / -- Lists all mines. + + mines set area: + description: Sets the area of a mine to the current selection. + usage: | + / -- Sets the area of the given mine to the current selected area. + / A - Sets the area of mine A to the given current selected area. + + mines tp: + description: Allows an admin to TP to a mine. + usage: | + / -- TP to the mine's spawn point, or to the top-center of the mine. + / A -- TP to mine 'A's spawn point. + + mines stats: + description: Toggles stat details on all mines. + usage: | + / -- Toggles stat detail on all mines. Once enabled, after a mine resets, it will display stats for the reset under /mines list and /mines info. + + mines whereami: + description: Identifies what mines you are in, or are the closest to. + usage: | + / -- Identifies what mines you are in, or the closest mines from where you are standing. + + mines wand: + description: Receive a wand to select a mine area. + usage: | + / -- Receive a wand to select a mine area. + + + permissions: # ================== @@ -65,6 +162,15 @@ permissions: mines.reset: description: Access to the /mines reset command. + mines.resettime: + description: Access to the /mines resettime command. + + mines.notification: + description: Access to the /mines notification command. + + mines.tp: + description: Access to the /mines tp command. + mines.admin: description: Contains all the commands for managing mines. default: op @@ -77,6 +183,9 @@ permissions: mines.wand: true mines.list: true mines.reset: true + mines.resettime: true + mines.notification: true + mines.tp: true # ================== # Ranks permissions @@ -104,6 +213,13 @@ permissions: ranks.delete: description: Access to the /ranks delete command. + ranks.promote: + description: Access to the /ranks promote command. + + ranks.demote: + description: Access to the /ranks demote command. + + ranks.admin: description: Contains all the commands reserved for admins. default: op @@ -115,3 +231,5 @@ permissions: ranks.list: true ranks.create: true ranks.delete: true + ranks.promote: true + ranks.demote: true diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotListener.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotListener.java index 9b5a99ab2..625ff9f21 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotListener.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotListener.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; @@ -31,11 +32,13 @@ import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.WorldLoadEvent; import tech.mcprison.prison.Prison; import tech.mcprison.prison.internal.events.Cancelable; import tech.mcprison.prison.internal.events.player.PlayerChatEvent; import tech.mcprison.prison.internal.events.player.PlayerPickUpItemEvent; +import tech.mcprison.prison.internal.events.world.PrisonWorldLoadEvent; import tech.mcprison.prison.spigot.compat.Compatibility; import tech.mcprison.prison.spigot.game.SpigotPlayer; import tech.mcprison.prison.spigot.game.SpigotWorld; @@ -46,18 +49,22 @@ /** * Posts Prison's internal events. * + *

getTypeId() was deprecated with 1.9.4. + *

+ * + *

PlayerPickupItemEvent is deprecated with spigot v1.12.2. + *

+ * * @author Faizaan A. Datoo */ +@SuppressWarnings( "deprecation" ) public class SpigotListener implements Listener { - private SpigotPrison spigotPrison; - - public SpigotListener(SpigotPrison spigotPrison) { - this.spigotPrison = spigotPrison; + public SpigotListener() { } public void init() { - Bukkit.getServer().getPluginManager().registerEvents(this, this.spigotPrison); + Bukkit.getServer().getPluginManager().registerEvents(this, SpigotPrison.getInstance()); } @EventHandler public void onPlayerJoin(PlayerJoinEvent e) { @@ -78,29 +85,46 @@ public void init() { new SpigotPlayer(e.getPlayer()), e.getReason())); } - @SuppressWarnings( "deprecation" ) @EventHandler public void onBlockPlace(BlockPlaceEvent e) { org.bukkit.Location block = e.getBlockPlaced().getLocation(); + BlockType blockType = SpigotUtil.blockToBlockType( e.getBlock() ); + tech.mcprison.prison.internal.events.block.BlockPlaceEvent event = new tech.mcprison.prison.internal.events.block.BlockPlaceEvent( - BlockType.getBlock(e.getBlock().getTypeId()), + blockType, new Location(new SpigotWorld(block.getWorld()), block.getX(), block.getY(), block.getZ()), (new SpigotPlayer(e.getPlayer()))); Prison.get().getEventBus().post(event); doCancelIfShould(event, e); } - @SuppressWarnings( "deprecation" ) @EventHandler public void onBlockBreak(BlockBreakEvent e) { org.bukkit.Location block = e.getBlock().getLocation(); + BlockType blockType = SpigotUtil.blockToBlockType( e.getBlock() ); + tech.mcprison.prison.internal.events.block.BlockBreakEvent event = new tech.mcprison.prison.internal.events.block.BlockBreakEvent( - BlockType.getBlock(e.getBlock().getTypeId()), + blockType, new Location(new SpigotWorld(block.getWorld()), block.getX(), block.getY(), block.getZ()), (new SpigotPlayer(e.getPlayer())),e.getExpToDrop()); Prison.get().getEventBus().post(event); doCancelIfShould(event, e); } + + /** + *

Monitors when new worlds are loaded, then it fires off a Prison's version of the + * same event type. This is used to initialize mines that are waiting for world to be + * loaded through plugins such as Multiverse-core. + *

+ * + * @param e The world event + */ + @EventHandler + public void onWorldLoadEvent( WorldLoadEvent e ) { + PrisonWorldLoadEvent pwlEvent = new PrisonWorldLoadEvent(e.getWorld().getName()); + + Prison.get().getEventBus().post(pwlEvent); + } @EventHandler public void onPlayerInteract(PlayerInteractEvent e) { // TODO Accept air events (block is null when air is clicked...) @@ -115,7 +139,8 @@ public void init() { // This one's a workaround for the double-interact event glitch. // The wand can only be used in the main hand - if (spigotPrison.compatibility.getHand(e) != Compatibility.EquipmentSlot.HAND) { + if ( SpigotPrison.getInstance().getCompatibility().getHand(e) != + Compatibility.EquipmentSlot.HAND) { return; } @@ -123,7 +148,8 @@ public void init() { tech.mcprison.prison.internal.events.player.PlayerInteractEvent event = new tech.mcprison.prison.internal.events.player.PlayerInteractEvent( new SpigotPlayer(e.getPlayer()), - SpigotUtil.bukkitItemStackToPrison(spigotPrison.compatibility.getItemInMainHand(e)), + SpigotUtil.bukkitItemStackToPrison( + SpigotPrison.getInstance().getCompatibility().getItemInMainHand(e)), tech.mcprison.prison.internal.events.player.PlayerInteractEvent.Action .valueOf(e.getAction().name()), new Location(new SpigotWorld(block.getWorld()), block.getX(), block.getY(), @@ -141,14 +167,19 @@ public void init() { doCancelIfShould(event, e); } - @EventHandler public void onPlayerPickUpItem(PlayerPickupItemEvent e) { + // TODO major potential problems with this function and newer releases. + // PlayerPickupItemEvent was deprecated and may not exist in newer releases. + // Need to research this and find an alternative and push this back in to + // the compatibility classes. + @EventHandler public void onPlayerPickUpItem(PlayerPickupItemEvent e) { PlayerPickUpItemEvent event = new PlayerPickUpItemEvent(new SpigotPlayer(e.getPlayer()), SpigotUtil.bukkitItemStackToPrison(e.getItem().getItemStack())); Prison.get().getEventBus().post(event); doCancelIfShould(event, e); } - @EventHandler public void onPlayerChat(AsyncPlayerChatEvent e) { + @EventHandler(priority=EventPriority.LOW) + public void onPlayerChat(AsyncPlayerChatEvent e) { PlayerChatEvent event = new PlayerChatEvent(new SpigotPlayer(e.getPlayer()), e.getMessage(), e.getFormat()); Prison.get().getEventBus().post(event); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPlatform.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPlatform.java index c6dbba916..ec8e45dce 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPlatform.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPlatform.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,37 +31,50 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.SimpleCommandMap; -import org.bukkit.material.MaterialData; -import org.bukkit.material.Openable; +import org.bukkit.plugin.Plugin; + +import com.cryptomorin.xseries.XBlock; +import com.cryptomorin.xseries.messages.Titles; import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonCommand; import tech.mcprison.prison.commands.PluginCommand; import tech.mcprison.prison.convert.ConversionManager; import tech.mcprison.prison.convert.ConversionResult; import tech.mcprison.prison.file.FileStorage; +import tech.mcprison.prison.file.YamlFileIO; import tech.mcprison.prison.gui.GUI; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.Scheduler; import tech.mcprison.prison.internal.World; +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.internal.platform.Capability; import tech.mcprison.prison.internal.platform.Platform; import tech.mcprison.prison.internal.scoreboard.ScoreboardManager; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.managers.MineManager; +import tech.mcprison.prison.modules.Module; import tech.mcprison.prison.output.BulletedListComponent; +import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.output.LogLevel; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.spigot.game.SpigotCommandSender; +import tech.mcprison.prison.spigot.game.SpigotOfflinePlayer; import tech.mcprison.prison.spigot.game.SpigotPlayer; import tech.mcprison.prison.spigot.game.SpigotWorld; import tech.mcprison.prison.spigot.gui.SpigotGUI; +import tech.mcprison.prison.spigot.placeholder.SpigotPlaceholders; import tech.mcprison.prison.spigot.scoreboard.SpigotScoreboardManager; import tech.mcprison.prison.spigot.util.ActionBarUtil; +import tech.mcprison.prison.spigot.util.SpigotYamlFileIO; import tech.mcprison.prison.store.Storage; import tech.mcprison.prison.util.Location; import tech.mcprison.prison.util.Text; @@ -78,11 +91,16 @@ class SpigotPlatform implements Platform { private ScoreboardManager scoreboardManager; private Storage storage; + + private SpigotPlaceholders placeholders; SpigotPlatform(SpigotPrison plugin) { this.plugin = plugin; this.scoreboardManager = new SpigotScoreboardManager(); this.storage = initStorage(); + + this.placeholders = new SpigotPlaceholders(); + ActionBarUtil.init(plugin); } @@ -100,12 +118,25 @@ private Storage initStorage() { return storage; } - @Override public Optional getWorld(String name) { - if (worlds.containsKey(name)) { + @Override + public Optional getWorld(String name) { + if (name != null && worlds.containsKey(name)) { return Optional.of(worlds.get(name)); } - if (Bukkit.getWorld(name) == null) { + if (name == null || name.trim().length() == 0 || + Bukkit.getWorld(name) == null) { + StringBuilder sb = new StringBuilder(); + for ( org.bukkit.World bukkitWorld : Bukkit.getWorlds() ) { + if ( sb.length() > 0 ) { + sb.append( " " ); + } + sb.append( bukkitWorld.getName() ); + } + + Output.get().logWarn( "&cWorld does not exist: &a" + name + + " &7Available worlds: &a" + sb.toString() ); + return Optional.empty(); // Avoid NPE } SpigotWorld newWorld = new SpigotWorld(Bukkit.getWorld(name)); @@ -113,14 +144,35 @@ private Storage initStorage() { return Optional.of(newWorld); } + @Override + public void getWorldLoadErrors( ChatDisplay display ) { + + Optional prisonMinesOpt = Prison.get().getModuleManager().getModule( PrisonMines.MODULE_NAME ); + + if ( prisonMinesOpt.isPresent() ) { + MineManager mineManager = ((PrisonMines) prisonMinesOpt.get()).getMineManager(); + + // When finished loading the mines, then if there are any worlds that + // could not be loaded, dump the details: + List unavailableWorlds = mineManager.getUnavailableWorldsListings(); + for ( String uWorld : unavailableWorlds ) { + + display.text( uWorld ); + } + + } + + } + @Override public Optional getPlayer(String name) { return Optional.ofNullable( - players.stream().filter(player -> player.getName().equals(name)).findFirst() + players.stream().filter(player -> player.getName().equalsIgnoreCase( name)).findFirst() .orElseGet(() -> { - if (Bukkit.getPlayer(name) == null) { + org.bukkit.entity.Player playerBukkit = Bukkit.getPlayer(name); + if (playerBukkit == null) { return null; } - SpigotPlayer player = new SpigotPlayer(Bukkit.getPlayer(name)); + SpigotPlayer player = new SpigotPlayer(playerBukkit); players.add(player); return player; })); @@ -130,10 +182,11 @@ private Storage initStorage() { return Optional.ofNullable( players.stream().filter(player -> player.getUUID().equals(uuid)).findFirst() .orElseGet(() -> { - if (Bukkit.getPlayer(uuid) == null) { + org.bukkit.entity.Player playerBukkit = Bukkit.getPlayer(uuid); + if (playerBukkit == null) { return null; } - SpigotPlayer player = new SpigotPlayer(Bukkit.getPlayer(uuid)); + SpigotPlayer player = new SpigotPlayer(playerBukkit); players.add(player); return player; })); @@ -144,6 +197,40 @@ private Storage initStorage() { .map(player -> getPlayer(player.getUniqueId()).get()).collect(Collectors.toList()); } + @Override + public Optional getOfflinePlayer(String name) { + return getOfflinePlayer(name, null); + } + + @Override + public Optional getOfflinePlayer(UUID uuid) { + return getOfflinePlayer(null, uuid); + } + private Optional getOfflinePlayer(String name, UUID uuid) { + SpigotOfflinePlayer player = null; + + for ( OfflinePlayer offP : Bukkit.getOfflinePlayers() ) { + if ( name != null && offP.getName().equalsIgnoreCase( name) || + uuid != null && offP.getUniqueId().equals(uuid) ) { + player = new SpigotOfflinePlayer( offP ); + + players.add(player); + break; + } + } + +// List olPlayers = Arrays.asList( Bukkit.getOfflinePlayers() ); +// for ( OfflinePlayer offlinePlayer : olPlayers ) { +// if ( name != null && offlinePlayer.getName().equals(name) || +// uuid != null && offlinePlayer.getUniqueId().equals(uuid) ) { +// player = new SpigotPlayer(offlinePlayer.getPlayer()); +// players.add(player); +// break; +// } +// } + return Optional.ofNullable( player ); + } + @Override public String getPluginVersion() { return plugin.getDescription().getVersion(); } @@ -154,25 +241,59 @@ private Storage initStorage() { @Override public void registerCommand(PluginCommand command) { try { - ((SimpleCommandMap) plugin.commandMap.get(Bukkit.getServer())) - .register(command.getLabel(), "prison", - new Command(command.getLabel(), command.getDescription(), command.getUsage(), - Collections.emptyList()) { - - @Override public boolean execute(CommandSender sender, String commandLabel, - String[] args) { - if (sender instanceof org.bukkit.entity.Player) { - return Prison.get().getCommandHandler() - .onCommand(new SpigotPlayer((org.bukkit.entity.Player) sender), - command, commandLabel, args); - } + Command cmd = new Command(command.getLabel(), command.getDescription(), command.getUsage(), + Collections.emptyList()) { + + @Override public boolean execute(CommandSender sender, String commandLabel, + String[] args) { + if (sender instanceof org.bukkit.entity.Player) { return Prison.get().getCommandHandler() - .onCommand(new SpigotCommandSender(sender), command, commandLabel, - args); + .onCommand(new SpigotPlayer((org.bukkit.entity.Player) sender), + command, commandLabel, args); } - - }); + return Prison.get().getCommandHandler() + .onCommand(new SpigotCommandSender(sender), command, commandLabel, + args); + + /* + * ###Tab-Complete### + * + * Disabled for now until a full solution can be implemented for tab complete. + * + // Output.get().logInfo( "SpigotPlatform.registerCommand: Command: %s :: %s", + // command.getLabel(), command.getUsage() ); + @Override + public List tabComplete( CommandSender sender, String[] args ) + { + Output.get().logInfo( "SpigotPlatform.registerCommand: Command.tabComplete 1" ); + // TODO Auto-generated method stub + return super.tabComplete( sender, args ); + } + + @Override + public List tabComplete( CommandSender sender, String alias, String[] args ) + throws IllegalArgumentException + { + Output.get().logInfo( "SpigotPlatform.registerCommand: Command.tabComplete 2" ); + // TODO Auto-generated method stub + return super.tabComplete( sender, alias, args ); + } + */ + } + }; + + @SuppressWarnings( "unused" ) + boolean success = + ((SimpleCommandMap) plugin.commandMap.get(Bukkit.getServer())) + .register(command.getLabel(), "prison", cmd ); + commands.add(command); + +// if ( !success ) { +// Output.get().logInfo( "SpigotPlatform.registerCommand: %s " + +// "Duplicate command. Fall back to Prison: [%s] ", command.getLabel(), +// cmd.getLabel() ); +// } } catch (IllegalAccessException e) { e.printStackTrace(); } @@ -195,6 +316,10 @@ private Storage initStorage() { @Override public void dispatchCommand(String cmd) { Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), cmd); } + + @Override public void dispatchCommand(tech.mcprison.prison.internal.CommandSender sender, String cmd) { + Bukkit.getServer().dispatchCommand( ((SpigotCommandSender) sender).getWrapper(), cmd); + } @Override public Scheduler getScheduler() { return plugin.scheduler; @@ -204,7 +329,8 @@ private Storage initStorage() { return new SpigotGUI(title, numRows); } - public void toggleDoor(Location doorLocation) { +// @SuppressWarnings( "deprecation" ) + public void toggleDoor(Location doorLocation) { org.bukkit.Location bLoc = new org.bukkit.Location(Bukkit.getWorld(doorLocation.getWorld().getName()), doorLocation.getX(), doorLocation.getY(), doorLocation.getZ()); @@ -213,12 +339,17 @@ public void toggleDoor(Location doorLocation) { return; } - BlockState state = block.getState(); - Openable openable = (Openable) state.getData(); - openable.setOpen(!openable.isOpen()); - state.setData((MaterialData) openable); - state.update(); - plugin.compatibility.playIronDoorSound(block.getLocation()); + boolean isOpen = XBlock.isOpen( block ); + XBlock.setOpened( block, !isOpen ); + +// BlockState state = block.getState(); +// Openable openable = (Openable) state.getData(); +// openable.setOpen(!openable.isOpen()); +// state.setData((MaterialData) openable); +// state.update(); + + SpigotPrison.getInstance().getCompatibility() + .playIronDoorSound(block.getLocation()); } @Override public void log(String message, Object... format) { @@ -267,10 +398,12 @@ public void toggleDoor(Location doorLocation) { return builder.build().text(); } - @SuppressWarnings( "deprecation" ) +// @SuppressWarnings( "deprecation" ) @Override public void showTitle(Player player, String title, String subtitle, int fade) { org.bukkit.entity.Player play = Bukkit.getPlayer(player.getName()); - play.sendTitle(title, subtitle); +// play.sendTitle(title, subtitle); + + Titles.sendTitle( play, title, subtitle ); } @Override public void showActionBar(Player player, String text, int duration) { @@ -292,10 +425,24 @@ public boolean shouldShowAlerts() { } private boolean isDoor(Material block) { - return block == Material.ACACIA_DOOR || block == Material.BIRCH_DOOR - || block == Material.DARK_OAK_DOOR || block == Material.IRON_DOOR_BLOCK - || block == Material.JUNGLE_DOOR || block == Material.WOODEN_DOOR - || block == Material.SPRUCE_DOOR; + + Material acaciaDoor = Material.matchMaterial( "ACACIA_DOOR" ); + Material birchDoor = Material.matchMaterial( "BIRCH_DOOR" ); + Material darkOakDoor = Material.matchMaterial( "DARK_OAK_DOOR" ); + Material ironDoor = Material.matchMaterial( "IRON_DOOR_BLOCK" ); + Material jungleDoor = Material.matchMaterial( "JUNGLE_DOOR" ); + Material woodenDoor = Material.matchMaterial( "WOODEN_DOOR" ); + Material spruceDoor = Material.matchMaterial( "SPRUCE_DOOR" ); + +// return block == Material.ACACIA_DOOR || block == Material.BIRCH_DOOR +// || block == Material.DARK_OAK_DOOR || block == Material.IRON_DOOR_BLOCK +// || block == Material.JUNGLE_DOOR || block == Material.WOODEN_DOOR +// || block == Material.SPRUCE_DOOR; + + return block == acaciaDoor || block == birchDoor || + block == darkOakDoor || block == ironDoor || + block == jungleDoor || block == woodenDoor || + block == spruceDoor; } @Override public Map getCapabilities() { @@ -305,4 +452,162 @@ private boolean isDoor(Material block) { return capabilities; } + @Override + public void identifyRegisteredPlugins() { + PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); + + // reset so it will reload cleanly: + cmdVersion.getRegisteredPlugins().clear(); +// cmdVersion.getRegisteredPluginData().clear(); + + Server server = SpigotPrison.getInstance().getServer(); + + // Finally print the version after loading the prison plugin: +// PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); + + // Store all loaded plugins within the PrisonCommand for later inclusion: + for ( Plugin plugin : server.getPluginManager().getPlugins() ) { + String name = plugin.getName(); + String version = plugin.getDescription().getVersion(); + String value = "&7" + name + " &3(&a" + version + "&3)"; + cmdVersion.getRegisteredPlugins().add( value ); + + cmdVersion.addRegisteredPlugin( name, version ); + } + + // NOTE: The following code does not actually get all of the commands that have been + // registered with the bukkit plugin registry. So commenting this out and may revisit + // in the future. Only tested with 1.8.8 so may work better with more cent version. +// SimplePluginManager spm = (SimplePluginManager) Bukkit.getPluginManager(); +// +// try { +// // The following code is based upon work provided by Technius: +// // https://bukkit.org/threads/get-all-the-available-commands.61941/ +// PluginManager manager = server.getPluginManager(); +// SimplePluginManager spm = (SimplePluginManager) manager; +// //List plugins = null; +// //Map lookupNames = null; +// SimpleCommandMap commandMap = null; +// Map knownCommands = null; +// if (spm != null) { +// //Field pluginsField = spm.getClass().getDeclaredField("plugins"); +// //Field lookupNamesField = spm.getClass().getDeclaredField("lookupNames"); +// Field commandMapField = spm.getClass().getDeclaredField("commandMap"); +// //pluginsField.setAccessible(true); +// //lookupNamesField.setAccessible(true); +// commandMapField.setAccessible(true); +// //plugins = (List) pluginsField.get(spm); +// //lookupNames = (Map) lookupNamesField.get(spm); +// commandMap = (SimpleCommandMap) commandMapField.get(spm); +// Field knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands"); +// knownCommandsField.setAccessible(true); +// knownCommands = (Map) knownCommandsField.get(commandMap); +// } +// +// if (commandMap != null) { +// for (Iterator> it = knownCommands.entrySet().iterator(); it.hasNext(); ) { +// Map.Entry entry = it.next(); +// if (entry.getValue() instanceof org.bukkit.command.PluginCommand) { +// org.bukkit.command.PluginCommand c = (org.bukkit.command.PluginCommand) entry.getValue(); +// //"c" is the command +// +// String pluginName = c.getPlugin().getName(); +// String pluginVersion = c.getPlugin().getDescription().getVersion(); +// String commandName = c.getName(); +// List commandAliases = c.getAliases(); +// +// // Log the command and it's aliases: +// cmdVersion.addPluginDetails( pluginName, pluginVersion, commandName, commandAliases ); +// } +// } +// } +// } +// catch ( NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e ) { +// e.printStackTrace(); +// } + + + } + + + + public SpigotPlaceholders getPlaceholders() { + return placeholders; + } + + + + @Override + public YamlFileIO getYamlFileIO( File yamlFile ) { + return new SpigotYamlFileIO( yamlFile ); + } + + + /** + * Forces the plugin config to reload. + * + * @return + */ + @Override + public void reloadConfig() { + + SpigotPrison.getInstance().reloadConfig(); + } + + @Override + public String getConfigString( String key ) { + return SpigotPrison.getInstance().getConfig().getString( key ); + } + + /** + *

This returns the boolean value that is associated with the key. + * It has to match on true to return a true value. If the key does + * not exist, then it returns a value of false. + *

+ * + * @param key + * @return + */ + @Override + public boolean getConfigBooleanFalse( String key ) { + + String val = SpigotPrison.getInstance().getConfig().getString( key ); + + return ( val != null && val.trim().equalsIgnoreCase( "true" ) ); + } + + /** + *

This returns the boolean value that is associated with the key. + * It has to match on true to return a true value. If the key does + * not exist, then it returns a value of true. + *

+ * + * @param key + * @return + */ + @Override + public boolean getConfigBooleanTrue( String key ) { + + String val = SpigotPrison.getInstance().getConfig().getString( key ); + + return ( val == null || val.trim().equalsIgnoreCase( "true" ) ); + } + + /** + * This listing that is returned, should be the XMaterial enum name + * for the blocks that are valid on the server. + * + * @return + */ + @Override + public void getAllPlatformBlockTypes( List blockTypes ) { + + SpigotUtil.getAllPlatformBlockTypes( blockTypes ); + } + + @Override + public PrisonBlock getPrisonBlock( String blockName ) { + + return SpigotUtil.getPrisonBlock( blockName ); + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPrison.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPrison.java index 268eb0f6c..7bbdd404f 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPrison.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotPrison.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,45 +19,64 @@ package tech.mcprison.prison.spigot; import java.io.File; -import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; -import java.util.logging.Level; +import java.util.Optional; +import java.util.concurrent.Callable; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.SimpleCommandMap; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; import org.inventivetalent.update.spiget.SpigetUpdate; import org.inventivetalent.update.spiget.UpdateCallback; -import org.inventivetalent.update.spiget.comparator.VersionComparator; import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.PrisonCommand; import tech.mcprison.prison.alerts.Alerts; import tech.mcprison.prison.integration.Integration; import tech.mcprison.prison.mines.PrisonMines; import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.output.ChatDisplay; +import tech.mcprison.prison.output.LogLevel; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.spigot.autofeatures.AutoManager; +import tech.mcprison.prison.spigot.autofeatures.AutoManagerFeatures; +import tech.mcprison.prison.spigot.block.OnBlockBreakEventListener; +import tech.mcprison.prison.spigot.commands.PrisonShortcutCommands; +import tech.mcprison.prison.spigot.commands.PrisonSpigotCommands; import tech.mcprison.prison.spigot.compat.Compatibility; +import tech.mcprison.prison.spigot.compat.Spigot113; import tech.mcprison.prison.spigot.compat.Spigot18; import tech.mcprison.prison.spigot.compat.Spigot19; import tech.mcprison.prison.spigot.economies.EssentialsEconomy; +import tech.mcprison.prison.spigot.economies.GemsEconomy; import tech.mcprison.prison.spigot.economies.SaneEconomy; import tech.mcprison.prison.spigot.economies.VaultEconomy; import tech.mcprison.prison.spigot.gui.GUIListener; +import tech.mcprison.prison.spigot.gui.GuiConfig; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; import tech.mcprison.prison.spigot.permissions.LuckPermissions; +import tech.mcprison.prison.spigot.permissions.LuckPerms5; import tech.mcprison.prison.spigot.permissions.VaultPermissions; import tech.mcprison.prison.spigot.placeholder.MVdWPlaceholderIntegration; import tech.mcprison.prison.spigot.placeholder.PlaceHolderAPIIntegration; +import tech.mcprison.prison.spigot.player.SlimeBlockFunEventListener; +import tech.mcprison.prison.spigot.sellall.SellAllCommands; +import tech.mcprison.prison.spigot.sellall.SellAllConfig; +import tech.mcprison.prison.spigot.spiget.BluesSpigetSemVerComparator; /** * The plugin class for the Spigot implementation. * * @author Faizaan A. Datoo + * @author GABRYCA */ public class SpigotPrison extends JavaPlugin { @@ -68,11 +87,30 @@ public class SpigotPrison extends JavaPlugin { boolean debug = false; private File dataDirectory; - private boolean doAlertAboutConvert = false; +// private boolean doAlertAboutConvert = false; + + private AutoManagerFeatures autoFeatures = null; +// private FileConfiguration autoFeaturesConfig = null; + + private static SpigotPrison config; + + public static SpigotPrison getInstance(){ + return config; + } + +// ###Tab-Complete### +// private TreeSet registeredCommands = new TreeSet<>(); + // @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void onLoad() { + + /** + * Old versions of prison MUST be upgraded with v3.0.x or even v3.1.1. + * Upgrading from old versions of prison to v3.2.x is not supported. + * Please upgrade to an earlier release of v3.0.x then to v3.2.1. + // The meta file is used to see if the folder needs converting. // If the folder doesn't contain it, it's probably not a Prison 3 thing. File metaFile = new File(getDataFolder(), ".meta"); @@ -88,43 +126,128 @@ public void onLoad() { try { metaFile.createNewFile(); } catch (IOException e) { - System.out.println( + Output.get().logError( "Could not create .meta file, this will cause problems with the converter!"); } } + */ } @Override public void onEnable() { + config = this; this.saveDefaultConfig(); debug = getConfig().getBoolean("debug", false); initDataDir(); initCommandMap(); initCompatibility(); - initMetrics(); initUpdater(); this.scheduler = new SpigotScheduler(this); - GUIListener.get().init(this); - Prison.get().init(new SpigotPlatform(this)); + + Prison.get().init(new SpigotPlatform(this), Bukkit.getVersion()); Prison.get().getLocaleManager().setDefaultLocale(getConfig().getString("default-language", "en_US")); - new SpigotListener(this).init(); + + new GuiConfig(); + + GUIListener.get().init(this); + Bukkit.getPluginManager().registerEvents(new ListenersPrisonManager(),this); + Bukkit.getPluginManager().registerEvents(new PrisonSpigotCommands(), this); + + Bukkit.getPluginManager().registerEvents(new AutoManager(), this); + Bukkit.getPluginManager().registerEvents(new OnBlockBreakEventListener(), this); + Bukkit.getPluginManager().registerEvents(new SlimeBlockFunEventListener(), this); + + getCommand("prisonmanager").setExecutor(new PrisonSpigotCommands()); + + // Only register the command if not enabled so it will not conflict with other sellall plugins: + if ( SellAllCommands.isEnabled() ) { + new SellAllConfig(); + + getCommand("sellall").setExecutor(new SellAllCommands()); + } + + + new SpigotListener().init(); + + Prison.get().getCommandHandler().registerCommands(new PrisonShortcutCommands()); + initIntegrations(); initModules(); - if (doAlertAboutConvert) { - Alerts.getInstance().sendAlert( - "&7An old installation of Prison has been detected. &3Type /prison convert to convert your old data automatically. &7If you already converted, delete the 'Prison.old' folder so that we stop nagging you."); - } + applyDeferredIntegrationInitializations(); + + extractCommandsForAutoComplete(); + + initMetrics(); + +// if (doAlertAboutConvert) { +// Alerts.getInstance().sendAlert( +// "&7An old installation of Prison has been detected. &3Type /prison convert to convert your old data automatically. &7If you already converted, delete the 'Prison.old' folder so that we stop nagging you."); +// } + + + Prison.get().getPlatform().getPlaceholders().printPlaceholderStats(); + + + + // Finally print the version after loading the prison plugin: + PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); + +// // Store all loaded plugins within the PrisonCommand for later inclusion: +// for ( Plugin plugin : Bukkit.getPluginManager().getPlugins() ) { +// String name = plugin.getName(); +// String version = plugin.getDescription().getVersion(); +// String value = "&7" + name + " &3(&a" + version + "&3)"; +// cmdVersion.getRegisteredPlugins().add( value ); +// } + + ChatDisplay cdVersion = cmdVersion.displayVersion(); + cdVersion.toLog( LogLevel.INFO ); + + // Provides a startup test of blocks available for the version of spigot that being used: + if ( getConfig().getBoolean("prison-block-compatibility-report") ) { + SpigotUtil.testAllPrisonBlockTypes(); + } + + Output.get().logInfo( "Prison - Finished loading." ); + } @Override public void onDisable() { - this.scheduler.cancelAll(); + if (this.scheduler != null ) { + this.scheduler.cancelAll(); + } Prison.get().deinit(); } + public static FileConfiguration getGuiConfig(){ + GuiConfig messages = new GuiConfig(); + return messages.getFileGuiConfig(); + } + + public static FileConfiguration getSellAllConfig(){ + SellAllConfig configs = new SellAllConfig(); + return configs.getFileSellAllConfig(); + } + + public AutoManagerFeatures getAutoFeatures() { + return autoFeatures; + } + + public void setAutoFeatures( AutoManagerFeatures autoFeatures ) { + this.autoFeatures = autoFeatures; + } + + + + + public static String format(String format){ + return ChatColor.translateAlternateColorCodes('&', format); + } + private void initMetrics() { if (!getConfig().getBoolean("send-metrics", true)) { return; // Don't check if they don't want it @@ -143,16 +266,39 @@ private void initMetrics() { // Report the API level metrics.addCustomChart( new Metrics.SimplePie("api_level", () -> "API Level " + Prison.API_LEVEL)); + + Optional prisonMinesOpt = Prison.get().getModuleManager().getModule( PrisonMines.MODULE_NAME ); + Optional prisonRanksOpt = Prison.get().getModuleManager().getModule( PrisonRanks.MODULE_NAME ); + + int mineCount = prisonMinesOpt.map(module -> ((PrisonMines) module).getMineManager().getMines().size()).orElse(0); + int rankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getRankCount()).orElse(0); + int ladderCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getladderCount()).orElse(0); + + metrics.addCustomChart(new Metrics.MultiLineChart("mines_ranks_and_ladders", new Callable>() { + @Override + public Map call() throws Exception { + Map valueMap = new HashMap<>(); + valueMap.put("mines", mineCount); + valueMap.put("ranks", rankCount); + valueMap.put("ladders", ladderCount); + return valueMap; + } + })); } private void initUpdater() { if (!getConfig().getBoolean("check-updates")) { return; // Don't check if they don't want it } + +// String currentVersion = getDescription().getVersion(); SpigetUpdate updater = new SpigetUpdate(this, Prison.SPIGOTMC_ORG_PROJECT_ID); // SpigetUpdate updater = new SpigetUpdate(this, 1223); - updater.setVersionComparator(VersionComparator.EQUAL); + + BluesSpigetSemVerComparator aRealSemVerComparator = new BluesSpigetSemVerComparator(); + updater.setVersionComparator( aRealSemVerComparator ); +// updater.setVersionComparator(VersionComparator.EQUAL); updater.checkForUpdate(new UpdateCallback() { @Override @@ -192,73 +338,100 @@ private void initCommandMap() { } private void initCompatibility() { - String[] version = Bukkit.getVersion().split("\\."); - int minorVersionInt = 9; - try { - minorVersionInt = Integer.parseInt(version[1]); - } catch (NumberFormatException e) { - try { - minorVersionInt = - Integer.parseInt(version[1].substring(0, version[1].indexOf(')'))); - } catch (Exception ex) { - Output.get().logError( - "Unable to determine server version. Assuming spigot 1.9 or greater."); - } - } - - if (minorVersionInt <= 8) { + + if ( new BluesSpigetSemVerComparator().compareMCVersionTo("1.9.0") < 0 ) { compatibility = new Spigot18(); - } else { + } + else if ( new BluesSpigetSemVerComparator().compareMCVersionTo("1.13.0") < 0 ) { compatibility = new Spigot19(); } + else { + compatibility = new Spigot113(); + } getLogger().info("Using version adapter " + compatibility.getClass().getName()); } - private void initIntegrations() { - registerIntegration("Essentials", EssentialsEconomy.class); - registerIntegration("SaneEconomy", SaneEconomy.class); - registerIntegration("Vault", VaultEconomy.class); + private void initIntegrations() { - registerIntegration("LuckPerms", LuckPermissions.class); - registerIntegration("Vault", VaultPermissions.class); + registerIntegration(new VaultEconomy()); + registerIntegration(new EssentialsEconomy()); + registerIntegration(new SaneEconomy()); + registerIntegration(new GemsEconomy()); - registerIntegration("MVdWPlaceholderAPI", MVdWPlaceholderIntegration.class); + registerIntegration(new VaultPermissions()); + registerIntegration(new LuckPerms5()); + registerIntegration(new LuckPermissions()); - registerIntegration("PlaceholderAPI", PlaceHolderAPIIntegration.class); + registerIntegration(new MVdWPlaceholderIntegration()); + registerIntegration(new PlaceHolderAPIIntegration()); + +// registerIntegration(new WorldGuard6Integration()); +// registerIntegration(new WorldGuard7Integration()); } - - private void registerIntegration(String pluginName, Class integration) { - if (Bukkit.getPluginManager().isPluginEnabled(pluginName)) { - try { - PrisonAPI.getIntegrationManager().register(integration.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - getLogger() - .log(Level.WARNING, "Could not initialize integration " + integration.getName(), - e); - } - } + + /** + *

This "tries" to reload the placeholder integrations, which may not + * always work, and can fail. It's here to try to do something, but + * it may not work. At least we tried. + *

+ * + */ + public void reloadIntegrationsPlaceholders() { + + MVdWPlaceholderIntegration ph1 = new MVdWPlaceholderIntegration(); + PlaceHolderAPIIntegration ph2 = new PlaceHolderAPIIntegration(); + + registerIntegration( ph1 ); + registerIntegration( ph2 ); + + ph1.deferredInitialization(); + ph2.deferredInitialization(); + } + + private void registerIntegration(Integration integration) { + integration.setRegistered( + Bukkit.getPluginManager().isPluginEnabled(integration.getProviderName()) ); + + integration.integrate(); + + PrisonAPI.getIntegrationManager().register(integration); } private void initModules() { YamlConfiguration modulesConf = loadConfig("modules.yml"); + // TODO: This business logic needs to be moved to the Module Manager: if (modulesConf.getBoolean("mines")) { Prison.get().getModuleManager() .registerModule(new PrisonMines(getDescription().getVersion())); } else { - Output.get().logInfo("Not loading mines because it's disabled in modules.yml."); + Output.get().logInfo("&7Modules: &cPrison Mines are disabled and were not Loaded. "); + Output.get().logInfo("&7 Prison Mines have been disabled in &2plugins/Prison/modules.yml&7."); + Prison.get().getModuleManager().getDisabledModules().add( PrisonMines.MODULE_NAME ); } if (modulesConf.getBoolean("ranks")) { Prison.get().getModuleManager() .registerModule(new PrisonRanks(getDescription().getVersion())); } else { - Output.get().logInfo("Not loading ranks because it's disabled in modules.yml"); + Output.get().logInfo("&3Modules: &cPrison Ranks, Ladders, and Players are disabled and were not Loaded. "); + Output.get().logInfo("&7 Prison Ranks have been disabled in &2plugins/Prison/modules.yml&7."); + Prison.get().getModuleManager().getDisabledModules().add( PrisonRanks.MODULE_NAME ); } } + private void applyDeferredIntegrationInitializations() { + for ( Integration deferredIntegration : PrisonAPI.getIntegrationManager().getDeferredIntegrations() ) { + deferredIntegration.deferredInitialization(); + } + } + + public Compatibility getCompatibility() { + return compatibility; + } + private File getBundledFile(String name) { getDataFolder().mkdirs(); File file = new File(getDataFolder(), name); @@ -275,5 +448,70 @@ private YamlConfiguration loadConfig(String file) { File getDataDirectory() { return dataDirectory; } - + + + /** + *

This function will register any missing "command" and will + * set the usable onTabComplete to the one within this class, + * that follows this function. + *

+ * + */ + private void extractCommandsForAutoComplete() { +/* + * ###Tab-Complete### (search for other occurrences of this tag) + * + * The following works up to a certain point, but is disabled until + * a full solution can be implemented. + * + List commandKeys = Prison.get().getCommandHandler().getRootCommandKeys(); + + registeredCommands.clear(); + registeredCommands.addAll( commandKeys ); + + // commands are already broken down to elements with roots: Keep the following + // just in case we need to expand with other uses: + for ( String cmdKey : commandKeys ) { + + Output.get().logInfo( "SpigotPrison.extractCommandsForAutoComplete: Command: %s", cmdKey ); + + Optional registeredCommand = Prison.get().getPlatform().getCommand(cmdKey); + if ( !registeredCommand.isPresent() ) { + tech.mcprison.prison.commands.PluginCommand rootPcommand = new tech.mcprison.prison.commands.PluginCommand(cmdKey, "--", "/" + cmdKey); + Prison.get().getPlatform().registerCommand(rootPcommand); + } + + PluginCommand pCommand = this.getCommand(cmdKey); + if ( pCommand != null ) { + pCommand.setTabCompleter(this); + } else { + Output.get().logInfo( "SpigotPrison.extractCommandsForAutoComplete: " + + "## Error not found ## Command: %s ", cmdKey ); + } + } + */ + + } +/* + * ###Tab-Complete### + * + * This function is disabled until tab complete can be fully implemented. + * + * @see org.bukkit.plugin.java.JavaPlugin#onTabComplete(org.bukkit.command.CommandSender, org.bukkit.command.Command, java.lang.String, java.lang.String[]) + * + * // Not being used... + @Override + public List onTabComplete( CommandSender sender, Command command, String alias, String[] args ) + { + List results = new ArrayList<>(); + Output.get().logInfo( "SpigotPrison.onTabComplete: Command: %s :: %s", command.getLabel(), command.getName() ); + + // Map> cmds = getDescription().getCommands(); + +// registeredCommands + + return results; + } + */ + } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotScheduler.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotScheduler.java index 68654a71e..6180d7d15 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotScheduler.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotScheduler.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotUtil.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotUtil.java index afda7be00..abc852914 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotUtil.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/SpigotUtil.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,23 +18,29 @@ package tech.mcprison.prison.spigot; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.material.MaterialData; + +import com.cryptomorin.xseries.XMaterial; + +import tech.mcprison.prison.internal.block.PrisonBlock; import tech.mcprison.prison.internal.inventory.InventoryType; import tech.mcprison.prison.internal.inventory.Viewable; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.spigot.compat.BlockTestStats; import tech.mcprison.prison.spigot.game.SpigotWorld; import tech.mcprison.prison.util.BlockType; import tech.mcprison.prison.util.Location; import tech.mcprison.prison.util.Text; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * Utilities for converting Prison-Core types to Spigot types. * @@ -43,22 +49,339 @@ public class SpigotUtil { private SpigotUtil() { + super(); } /* * BlockType and Material */ - @SuppressWarnings( "deprecation" ) - public static BlockType materialToBlockType(Material material) { - return BlockType.getBlock(material.getId()); // To be safe, we use legacy ID + public static XMaterial getXMaterial( Material material) { + return XMaterial.matchXMaterial(material); } - - @SuppressWarnings( "deprecation" ) - public static MaterialData blockTypeToMaterial(BlockType type) { - Material material = Material.getMaterial(type.getLegacyId()); - return new MaterialData(material, (byte) type.getData()); // To be safe, we use legacy ID + + public static XMaterial getXMaterial( String materialName ) { + return XMaterial.matchXMaterial( materialName ).orElse( null ); + } + + /** + *

Gets the XMaterial based upon the BlockType name, and if it fails to hit + * anything, then it falls back on to the id, of which XMaterial strips the + * prefix of "minecraft:". + *

+ * + * @param prisonBlockType + * @return + */ + public static XMaterial getXMaterial( BlockType prisonBlockType ) { + + XMaterial xMat = SpigotPrison.getInstance().getCompatibility() + .getXMaterial( prisonBlockType ); + + return xMat; + } + + public static Material getMaterial( BlockType prisonBlockType ) { + XMaterial xMat = getXMaterial( prisonBlockType ); + + return xMat == null ? null : xMat.parseMaterial(); } + + + + public static BlockType blockToBlockType( Block spigotBlock ) { + BlockType results = SpigotPrison.getInstance().getCompatibility() + .getBlockType( spigotBlock ); + +// +// XMaterial xMatMatch = XMaterial.matchXMaterial( material ); +// +// for ( BlockType blockType : BlockType.values() ) { +// XMaterial xMat = getXMaterial( blockType ); +// if ( xMat != null ) { +// results = blockType; +// break; +// } +// } + + return results; + } + + /** + *

Returns a stack of BlockType or a stack of air. + *

+ * + * @param prisonBlockType + * @param amount + * @return + */ + public static ItemStack getItemStack( BlockType prisonBlockType, int amount ) { + ItemStack bukkitStack = null; + XMaterial xMat = getXMaterial( prisonBlockType ); + if ( xMat != null ) { + bukkitStack = xMat.parseItem(); + bukkitStack.setAmount( amount ); + } + else { + bukkitStack = getXMaterial( "air" ).parseItem(); + bukkitStack.setAmount( 1 ); + } + return bukkitStack; + } + + public static ItemStack getItemStack( Material material, int amount ) { + XMaterial xMat = getXMaterial( material ); + ItemStack bukkitStack = xMat.parseItem(); + bukkitStack.setAmount( amount ); + return bukkitStack; + } + + + public static ItemStack getItemStack( XMaterial xMaterial, int amount ) { + ItemStack bukkitStack = xMaterial.parseItem(); + bukkitStack.setAmount( amount ); + return bukkitStack; + } + + public static void getAllPlatformBlockTypes( List blockTypes ) { + + for ( XMaterial xMat : XMaterial.values() ) { + if ( xMat.isSupported() ) { + + Material mat = xMat.parseMaterial(); + if ( mat != null ) { + if ( mat.isBlock() ) { + PrisonBlock block = new PrisonBlock( mat.name() ); + + blockTypes.add( block ); + } + } + else { + Output.get().logWarn( "### SpigotUtil.testAllPrisonBlockTypes: " + + "Possible XMaterial FAIL: XMaterial " + xMat.name() + + " is supported for this version, but the XMaterial cannot " + + "be mapped to an actual Material."); + } + } + } + } + + /** + *

This will take a string name of a block, and convert it to the + * String name of a XMaterial. If it cannot directly perform the + * conversion, then it will fall back to using the old prison's + * BlockType to perform the conversion. + *

+ * + * @param blockName + * @return + */ + public static PrisonBlock getPrisonBlock( String blockName ) { + + PrisonBlock results = null; + BlockType bTypeObsolete = null; + + XMaterial xMat = getXMaterial( blockName ); + + if ( xMat == null ) { + // Try to get the material through the old prison blocks: + bTypeObsolete = BlockType.getBlock( blockName ); + + xMat = getXMaterial( bTypeObsolete ); + } + + if ( xMat != null ) { + results = new PrisonBlock( xMat.name() ); + + if ( bTypeObsolete != null ) { + results.setLegacyBlock( true ); + } + } + else { + results = new PrisonBlock( blockName ); + results.setValid( false ); + } + return results; + } + + + public static void testAllPrisonBlockTypes() { + double version = XMaterial.getVersion(); + + StringBuilder sbNoMap = new StringBuilder(); + StringBuilder sbNotSupported = new StringBuilder(); + + int supportedBlockCountPrison = 0; + int supportedBlockCountXMaterial = 0; + + for ( BlockType block : BlockType.values() ) { + + if ( block.isBlock() ) { + XMaterial xMat = getXMaterial( block ); + + if ( xMat == null ) { + if ( sbNoMap.length() > 0 ) { + sbNoMap.append( " " ); + } + + Material mat = getMaterial( block ); + + String bName = block.name() + (mat == null ? "" : "(" + mat.name() + ")"); + sbNoMap.append( bName ); + } + else if ( !xMat.isSupported() ) { + if ( sbNotSupported.length() > 0 ) { + sbNotSupported.append( " " ); + } + sbNotSupported.append( block.name() ); + } + else { + supportedBlockCountPrison++; + } + } + } + + for ( XMaterial xMat : XMaterial.values() ) { + if ( xMat.isSupported() ) { + + Material mat = xMat.parseMaterial(); + if ( mat != null ) { + if ( mat.isBlock() ) { + supportedBlockCountXMaterial++; + } + } + else { + Output.get().logWarn( "### SpigotUtil.testAllPrisonBlockTypes: " + + "Possible XMaterial FAIL: XMaterial " + xMat.name() + + " is supported for this version, but the XMaterial cannot " + + "be mapped to an actual Material."); + } + } + } + + // Next test all of the spigot/bukkit Materials: + BlockTestStats stats = SpigotPrison.getInstance().getCompatibility() + .testCountAllBlockTypes(); + + + Output.get().logWarn( "### SpigotUtil.testAllPrisonBlockTypes: Bukkit version: " + version + + " Supported Prison Blocks: " + supportedBlockCountPrison + + " Supported XMaterial Blocks: " + supportedBlockCountXMaterial ); + + Output.get().logWarn( "### SpigotUtil.testAllPrisonBlockTypes: Raw Bukkit/Spigot " + stats.toString() ); + + logTestBlocks( sbNoMap, "### SpigotUtil.testAllPrisonBlockTypes: " + + "Prison Blocks no maps to XMaterial: " ); + logTestBlocks( sbNotSupported, "### SpigotUtil.testAllPrisonBlockTypes: " + + "Prison Blocks not supported with version: " ); + } + + + private static void logTestBlocks( StringBuilder sb, String message ) { + + int start = 0; + int end = 150; + + while ( sb.length() > end ) { + end = sb.indexOf( " ", end ); + Output.get().logWarn( message + + (end < 0 ? sb.substring( start ) : sb.substring( start, end ))); + + start = end; + end += 150; + } + + Output.get().logWarn( message + sb.substring( start )); + } + +// @SuppressWarnings( "deprecation" ) +// public static BlockType materialToBlockType(Material material) { +// return BlockType.getBlock(material.getId()); // To be safe, we use legacy ID +// } + +// @SuppressWarnings( "deprecation" ) +// public static MaterialData blockTypeToMaterial(BlockType type) { +// Material material = Material.getMaterial(type.getLegacyId()); +// if ( material == null ) { +// material = Material.STONE; +// } +// return new MaterialData(material, (byte) type.getData()); // To be safe, we use legacy ID +//// Material material = Material.getMaterial(type.getLegacyId()); +//// if ( material == null ) { +//// material = Material.STONE; +//// } +//// +//// return new MaterialData(material, (byte) type.getData()); // To be safe, we use legacy ID +//// +//// +//// +//// MaterialData results = null; +//// +//// if ( type.getMaterialVersion() == MaterialVersion.v1_13 ) { +//// Output.get().logInfo( String.format( "SpigotUtil.blockTypeToMaterial: v1_13 : %s ", +//// type.getId()) ); +//// +//// // Material type for 1.13 and higher have a legacyID == 0: +//// Material material = null; +//// material = getMaterial( type.getId() ); +//// +//// if ( material == null ) { +//// String materialName = type.getId().toUpperCase(); +//// material = getMaterial( materialName ); +//// +//// Output.get().logInfo( String.format( "SpigotUtil.blockTypeToMaterial: was null : %s -> %s [%s]", +//// type.name(), materialName, (material == null ? "null" : "NOT null")) ); +//// +////// if ( material == null ) { +////// material = Material. +////// Output.get().logInfo( String.format( "SpigotUtil.blockTypeToMaterial: was null : %s -> %s [%s]", +////// type.name(), materialName, (material == null ? "null" : "NOT null")) ); +////// +////// } +//// } +//// else { +//// Output.get().logInfo( String.format( "SpigotUtil.blockTypeToMaterial: %s [%s]", +//// type.name(), (material == null ? "null" : "NOT null")) ); +//// +//// } +//// +//// if ( material == null ) { +//// material = Material.STONE; +//// } +//// results = new MaterialData(material); +//// } +//// else { +////// Output.get().logInfo( String.format( "SpigotUtil.blockTypeToMaterial: v1_8 : %s %s data=%s", +////// type.getId(), Integer.toString( type.getLegacyId()), +////// Integer.toString( type.getData())) ); +////// // type.getMaterialVersion() == MaterialVersion.v1_8 +//// +//// // Material types for 1.12 and lower: +//// Material material = Material.getMaterial(type.getLegacyId()); +//// if ( material == null ) { +//// material = Material.STONE; +//// } +//// +//// results = new MaterialData(material, (byte) type.getData()); // To be safe, we use legacy ID +//// +//// } +//// return results; +// } + +// private static Material getMaterial( String materialName ) { +// Material results = null; +// +// try { +// results = Material.matchMaterial( materialName ); +// } +// catch ( Exception e ) { +// // Do nothing for now... +// // Will try other combination later and will report failure if needed; +// Output.get().logInfo( "&cSpigotUtil.getMaterial() Failure : &7" + e.getMessage() ); +// } +// +// return results; +// } /* * Location @@ -81,7 +404,8 @@ public static org.bukkit.Location prisonLocationToBukkit(Location prisonLocation */ public static tech.mcprison.prison.internal.ItemStack bukkitItemStackToPrison( - ItemStack bukkitStack) { + ItemStack bukkitStack) { + if (bukkitStack == null || bukkitStack.getType().equals(Material.AIR)) { return new tech.mcprison.prison.internal.ItemStack(0, BlockType.AIR); } @@ -100,8 +424,10 @@ public static tech.mcprison.prison.internal.ItemStack bukkitItemStackToPrison( } int amount = bukkitStack.getAmount(); - - BlockType type = materialToBlockType(bukkitStack.getType()); + + BlockType type = SpigotPrison.getInstance().getCompatibility() + .getBlockType( bukkitStack ); +// BlockType type = materialToBlockType(bukkitStack.getType()); List lore = meta.hasLore() ? meta.getLore() : Collections.emptyList(); String[] lore_arr = lore.toArray(new String[lore.size()]); @@ -110,33 +436,36 @@ public static tech.mcprison.prison.internal.ItemStack bukkitItemStackToPrison( } public static ItemStack prisonItemStackToBukkit( - tech.mcprison.prison.internal.ItemStack prisonStack) { + tech.mcprison.prison.internal.ItemStack prisonStack) { int amount = prisonStack.getAmount(); - MaterialData materialData = blockTypeToMaterial(prisonStack.getMaterial()); - - ItemStack bukkitStack = new ItemStack(materialData.getItemType(), amount); - bukkitStack.setData(materialData); - + + ItemStack bukkitStack = getItemStack( prisonStack.getMaterial(), amount ); + +// MaterialData materialData = blockTypeToMaterial(prisonStack.getMaterial()); +// +// ItemStack bukkitStack = new ItemStack(materialData.getItemType(), amount); +// bukkitStack.setData(materialData); + ItemMeta meta; if (bukkitStack.getItemMeta() == null || !bukkitStack.hasItemMeta()) { - meta = Bukkit.getItemFactory().getItemMeta(bukkitStack.getType()); + meta = Bukkit.getItemFactory().getItemMeta(bukkitStack.getType()); } else { - meta = bukkitStack.getItemMeta(); + meta = bukkitStack.getItemMeta(); } - + if (meta != null) { - if (prisonStack.getDisplayName() != null) { - meta.setDisplayName(Text.translateAmpColorCodes(prisonStack.getDisplayName())); - } - if (prisonStack.getLore() != null) { - List colored = new ArrayList<>(); - for (String uncolor : prisonStack.getLore()) { - colored.add(Text.translateAmpColorCodes(uncolor)); - } - meta.setLore(colored); - } - bukkitStack.setItemMeta(meta); - } + if (prisonStack.getDisplayName() != null) { + meta.setDisplayName(Text.translateAmpColorCodes(prisonStack.getDisplayName())); + } + if (prisonStack.getLore() != null) { + List colored = new ArrayList<>(); + for (String uncolor : prisonStack.getLore()) { + colored.add(Text.translateAmpColorCodes(uncolor)); + } + meta.setLore(colored); + } + bukkitStack.setItemMeta(meta); + } return bukkitStack; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManager.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManager.java new file mode 100644 index 000000000..951734a51 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManager.java @@ -0,0 +1,255 @@ +package tech.mcprison.prison.spigot.autofeatures; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +import org.bukkit.inventory.ItemStack; + +import com.vk2gpz.tokenenchant.event.TEBlockExplodeEvent; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.spigot.SpigotPrison; + + +/** + * @author GABRYCA + * @author RoyalBlueRanger + */ +public class AutoManager + extends AutoManagerFeatures + implements Listener { + + public AutoManager() { + super(); + + // Save this instance within the SpigotPrison instance so it can be accessed + // from non-event listeners: + SpigotPrison.getInstance().setAutoFeatures( this ); + } + + +// /** +// *

Had to set to a EventPriorty.LOW so other plugins can work with the blocks. +// * The other plugins were EZBlock & SellAll. This function was canceling the +// * event after it auto picked it up, so the other plugins were not registering +// * the blocks as being broken. +// *

+// * +// * @param e +// */ +// @EventHandler(priority=EventPriority.LOW) +// public void onBlockBreak(BlockBreakEvent e) { +// +// if ( !e.isCancelled() && e.getBlock().getType() != null) { +// +// // Get the player objects: Spigot and the Prison player: +// Player p = e.getPlayer(); +// // SpigotPlayer player = new SpigotPlayer( p ); +// +// // Validate that the event is happening within a mine since the +// // onBlockBreak events here are only valid within the mines: +// Optional mmOptional = Prison.get().getModuleManager().getModule( PrisonMines.MODULE_NAME ); +// if ( mmOptional.isPresent() && mmOptional.get().isEnabled() ) { +// PrisonMines mineManager = (PrisonMines) mmOptional.get(); +// +// for ( Mine mine : mineManager.getMines() ) { +// SpigotBlock block = new SpigotBlock(e.getBlock()); +// if ( mine.isInMine( block.getLocation() ) ) { +// +// applyAutoEvents( e, mine, p ); +// break; +// } +// } +// } +// } +// } + + /** + *

The optimized logic on how an BlockBreakEvent is handled is within the OnBlockBreakEventListener + * class and optimizes mine reuse. + *

+ * + *

Had to set to a EventPriorty.LOW so other plugins can work with the blocks. + * The other plugins were EZBlock & SellAll. This function was canceling the + * event after it auto picked it up, so the other plugins were not registering + * the blocks as being broken. + *

+ * + * + */ + @Override + @EventHandler(priority=EventPriority.LOW) + public void onBlockBreak(BlockBreakEvent e) { + super.onBlockBreak(e); + } + + @Override + @EventHandler(priority=EventPriority.LOW) + public void onTEBlockExplode(TEBlockExplodeEvent e) { + super.onTEBlockExplode(e); + } + + + + @Override + public void doAction( Mine mine, BlockBreakEvent e ) { + applyAutoEvents( e, mine ); + } + + + @Override + public void doAction( Mine mine, TEBlockExplodeEvent e, int blockCount ) { + applyAutoEvents( e, mine, blockCount ); + } + + // Prevents players from picking up armorStands (used for holograms), only if they're invisible + @EventHandler + public void manipulate(PlayerArmorStandManipulateEvent e) { + if(!e.getRightClicked().isVisible()) { + e.setCancelled(true); + } + } + + private void applyAutoEvents( BlockBreakEvent e, Mine mine ) { + + if ( isBoolean( AutoFeatures.isAutoManagerEnabled ) && !e.isCancelled() ) { + + Player player = e.getPlayer(); + + double lorePickup = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Pickup, player ); + double loreSmelt = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Smelt, player ); + double loreBlock = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Block, player ); + + boolean permPickup = player.hasPermission( + getMessage( AutoFeatures.permissionAutoPickup ) ) || + lorePickup == 100.0 || + lorePickup > 0 && lorePickup <= getRandom().nextDouble() * 100; + boolean permSmelt = player.hasPermission( + getMessage( AutoFeatures.permissionAutoSmelt ) ) || + loreSmelt == 100.0 || + loreSmelt > 0 && loreSmelt <= getRandom().nextDouble() * 100; + boolean permBlock = player.hasPermission( + getMessage( AutoFeatures.permissionAutoBlock ) ) || + loreBlock == 100.0 || + loreBlock > 0 && loreBlock <= getRandom().nextDouble() * 100; + + // + + + // AutoPickup + if ( permPickup || isBoolean( AutoFeatures.autoPickupEnabled )) { + + autoFeaturePickup(e, player ); + } + + + // AutoSmelt + if ( permSmelt || isBoolean( AutoFeatures.autoSmeltEnabled )){ + + autoFeatureSmelt( e, player ); + } + + // AutoBlock + if ( permBlock || isBoolean( AutoFeatures.autoBlockEnabled )) { + + autoFeatureBlock( e, player ); + } + + // NOTE: This may be in duplication... durability is calculated in auto pickup: + // Calculate durability if enabled: + // isCalculateDurabilityEnabled must be enabled before loreDurabiltyResistance will + // even be checked. +// if ( isBoolean( AutoFeatures.isCalculateDurabilityEnabled ) && +// e.isCancelled()) { +// +// ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( player ); +// +// // value of 0 = normal durability. Value 100 = never calculate durability. +// int durabilityResistance = 0; +// if ( isBoolean( AutoFeatures.loreDurabiltyResistance ) ) { +// durabilityResistance = getDurabilityResistance( itemInHand, +// getMessage( AutoFeatures.loreDurabiltyResistanceName ) ); +// } +// +// calculateDurability( player, itemInHand, durabilityResistance ); +// } + + + // A block was broke... so record that event on the tool: + if ( isBoolean( AutoFeatures.loreTrackBlockBreakCount ) && + e.isCancelled()) { + // The event was canceled, so the block was successfully broke, so increment the name counter: + + ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( player ); + + itemLoreCounter( itemInHand, + getMessage( AutoFeatures.loreBlockBreakCountName ), 1 ); + } + } + + } + + + /** + *

This function gets called for EACH block that is impacted by the + * explosion event. The event may have have a list of blocks, but not all + * blocks may be included in the mine. This function is called ONLY when + * a block is within a mine, + *

+ * + *

The event TEBlockExplodeEvent has already taken place and it handles all + * actions such as auto pickup, auto smelt, and auto block. The only thing we + * need to do is to record the number of blocks that were removed during + * the explosion event. The blocks should have been replaced by air. + *

+ * + *

Normally, prison based auto features, and or the perms need to be enabled + * for the auto features to be enabled, but since this is originating from another + * plugin and another external configuration, we cannot test for any real perms + * or configs. The fact that this event happens within one of the mines is + * good enough, and must be counted. + *

+ * + * @param e + * @param mine + */ + private void applyAutoEvents( TEBlockExplodeEvent e, Mine mine, int blockCount ) { + + Player p = e.getPlayer(); + +// double lorePickup = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Pickup, p ); +// double loreSmelt = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Smelt, p ); +// double loreBlock = doesItemHaveAutoFeatureLore( ItemLoreEnablers.Block, p ); +// +// boolean permPickup = p.hasPermission( "prison.autofeatures.pickup" ) || +// lorePickup == 100.0 || +// lorePickup > 0 && lorePickup <= getRandom().nextDouble() * 100; +// boolean permSmelt = p.hasPermission( "prison.autofeatures.smelt" ) || +// loreSmelt == 100.0 || +// loreSmelt > 0 && loreSmelt <= getRandom().nextDouble() * 100; +// boolean permBlock = p.hasPermission( "prison.autofeatures.block" ) || +// loreBlock == 100.0 || +// loreBlock > 0 && loreBlock <= getRandom().nextDouble() * 100; + +// if ( permPickup || permSmelt || permBlock || +// isAreEnabledFeatures()) + { + + + ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( p ); + + + if ( e.isCancelled() ) { + // The event was canceled, so the block was successfully broke, so increment the name counter: + + itemLoreCounter( itemInHand, + getMessage( AutoFeatures.loreBlockExplosionCountName ), blockCount ); + } + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManagerFeatures.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManagerFeatures.java new file mode 100644 index 000000000..785151224 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/autofeatures/AutoManagerFeatures.java @@ -0,0 +1,1586 @@ +package tech.mcprison.prison.spigot.autofeatures; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; + +import com.cryptomorin.xseries.XMaterial; + +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.TextComponent; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.SpigotUtil; +import tech.mcprison.prison.spigot.block.OnBlockBreakEventListener; +import tech.mcprison.prison.spigot.compat.Compatibility; +import tech.mcprison.prison.spigot.game.SpigotPlayer; +import tech.mcprison.prison.spigot.spiget.BluesSpigetSemVerComparator; +import tech.mcprison.prison.util.Text; + +/** + *

This class controls the data and the basic functions for auto features. + * It loads the settings only once, and uses them on each event that is raised + * in order to reduce overhead in reloading all of them on each block break + * event. Then, if an external process should happen to save changes to + * these settings, then it will reload the settings so they are always + * current. + *

+ * + * + */ +public class AutoManagerFeatures + extends OnBlockBreakEventListener { + + public enum ItemLoreCounters { + + // NOTE: the String value must include a trailing space! + + itemLoreBlockBreakCount( ChatColor.LIGHT_PURPLE + "Prison Blocks Mined:" + + ChatColor.GRAY + " "), + + itemLoreBlockExplodeCount( ChatColor.LIGHT_PURPLE + "Prison Blocks Exploded:" + + ChatColor.GRAY + " "); + + + private final String lore; + ItemLoreCounters( String lore ) { + this.lore = lore; + } + public String getLore() { + return lore; + } + + } + + public enum ItemLoreEnablers { + Pickup, + Smelt, + Block + ; + } + + private Random random = new Random(); + + private AutoFeaturesFileConfig autoFeaturesConfig = null; + +// private Configuration autoConfigs; + +// private boolean isAutoManagerEnabled = false; + +// private boolean dropItemsIfInventoryIsFull = false; +// private boolean playSoundIfInventoryIsFull = false; +// private boolean hologramIfInventoryIsFull = false; + +// private boolean autoPickupEnabled = false; +// private boolean autoPickupAllBlocks = false; +// private boolean autoPickupCobbleStone = false; +// private boolean autoPickupStone = false; +// private boolean autoPickupGoldOre = false; +// private boolean autoPickupIronOre = false; +// private boolean autoPickupCoalOre = false; +// private boolean autoPickupDiamondOre = false; +// private boolean autoPickupRedstoneOre = false; +// private boolean autoPickupEmeraldOre = false; +// private boolean autoPickupQuartzOre = false; +// private boolean autoPickupLapisOre = false; +// private boolean autoPickupSnowBall = false; +// private boolean autoPickupGlowstoneDust = false; + + // AutoSmelt booleans from configs +// private boolean autoSmeltEnabled = false; +// private boolean autoSmeltAllBlocks = false; +// private boolean autoSmeltGoldOre = false; +// private boolean autoSmeltIronOre = false; + + // AutoBlock booleans from configs +// private boolean autoBlockEnabled = false; +// private boolean autoBlockAllBlocks = false; +// private boolean autoBlockGoldBlock = false; +// private boolean autoBlockIronBlock = false; +// private boolean autoBlockCoalBlock = false; +// private boolean autoBlockDiamondBlock = false; +// private boolean autoBlockRedstoneBlock = false; +// private boolean autoBlockEmeraldBlock = false; +// private boolean autoBlockQuartzBlock = false; +// private boolean autoBlockPrismarineBlock = false; +// private boolean autoBlockLapisBlock = false; +// private boolean autoBlockSnowBlock = false; +// private boolean autoBlockGlowstone = false; + + + public AutoManagerFeatures() { + super(); + + setup(); + } + + + private void setup() { + + this.autoFeaturesConfig = new AutoFeaturesFileConfig(); + +// this.autoConfigs = getAutoFeaturesConfig(); +// this.autoConfigs = SpigotPrison.getInstance().getAutoFeaturesConfig(); + +// this.isAutoManagerEnabled = isBoolean( AutoFeatures.isAutoManagerEnabled ); + + //if (isAreEnabledFeatures()) + { + +// this.dropItemsIfInventoryIsFull = isBoolean( AutoFeatures.dropItemsIfInventoryIsFull ); +// this.playSoundIfInventoryIsFull = isBoolean( AutoFeatures.playSoundIfInventoryIsFull ); +// this.hologramIfInventoryIsFull = isBoolean( AutoFeatures.hologramIfInventoryIsFull ); + + // AutoPickup booleans from configs +// this.autoPickupEnabled = isBoolean( AutoFeatures.autoPickupEnabled ); +// this.autoPickupAllBlocks = isBoolean( AutoFeatures.autoPickupAllBlocks ); +// this.autoPickupCobbleStone = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupCobbleStone ); +// this.autoPickupStone = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupStone ); +// this.autoPickupGoldOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupGoldOre ); +// this.autoPickupIronOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupIronOre ); +// this.autoPickupCoalOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupCoalOre ); +// this.autoPickupDiamondOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupDiamondOre ); +// this.autoPickupRedstoneOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupRedStoneOre ); +// this.autoPickupEmeraldOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupEmeraldOre ); +// this.autoPickupQuartzOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupQuartzOre ); +// this.autoPickupLapisOre = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupLapisOre ); +// this.autoPickupSnowBall = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupSnowBall ); +// this.autoPickupGlowstoneDust = autoPickupAllBlocks || isBoolean( AutoFeatures.autoPickupGlowstoneDust ); + + // AutoSmelt booleans from configs +// this.autoSmeltEnabled = isBoolean( AutoFeatures.autoSmeltEnabled ); +// this.autoSmeltAllBlocks = isBoolean( AutoFeatures.autoSmeltAllBlocks ); +// this.autoSmeltGoldOre = autoSmeltAllBlocks || isBoolean( AutoFeatures.autoSmeltGoldOre ); +// this.autoSmeltIronOre = autoSmeltAllBlocks || isBoolean( AutoFeatures.autoSmeltIronOre ); + + // AutoBlock booleans from configs +// this.autoBlockEnabled = isBoolean( AutoFeatures.autoBlockEnabled ); +// this.autoBlockAllBlocks = isBoolean( AutoFeatures.autoBlockAllBlocks ); +// this.autoBlockGoldBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockGoldBlock ); +// this.autoBlockIronBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockIronBlock ); +// this.autoBlockCoalBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockCoalBlock ); +// this.autoBlockDiamondBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockDiamondBlock ); +// this.autoBlockRedstoneBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockRedstoneBlock ); +// this.autoBlockEmeraldBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockEmeraldBlock ); +// this.autoBlockQuartzBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockQuartzBlock ); +// this.autoBlockPrismarineBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockPrismarineBlock ); +// this.autoBlockLapisBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockLapisBlock ); +// this.autoBlockSnowBlock = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockSnowBlock ); +// this.autoBlockGlowstone = autoBlockAllBlocks || isBoolean( AutoFeatures.autoBlockGlowstone ); + + } + } + + public AutoFeaturesFileConfig getAutoFeaturesConfig() { + return autoFeaturesConfig; + } + + protected boolean isBoolean( AutoFeatures feature ) { + return autoFeaturesConfig.isFeatureBoolean( feature ); + } + + protected String getMessage( AutoFeatures feature ) { + return autoFeaturesConfig.getFeatureMessage( feature ); + } + + +// /** +// *

This lazy loading of the FileConfiguration for the AutoFeatures will ensure +// * the file is loaded from the file system only one time and only when it is first +// * used. This ensures that if it is never used, it is never loaded in to memory. +// *

+// * +// * @return +// */ +// private FileConfiguration getAutoFeaturesConfig() { +// if ( this.autoFeaturesConfig == null ) { +// AutoFeaturesFileConfig afc = new AutoFeaturesFileConfig(); +// this.autoFeaturesConfig = afc.getConfig(); +// +// } +// return autoFeaturesConfig; +// } + + +// /** +// *

This change in this function, to move it in to this class, allows the +// * reloading of the parameters that controls all of the behaviors whenever +// * the data is saved. +// *

+// * +// * @return +// */ +// public boolean saveAutoFeaturesConfig() { +// boolean success = false; +// FileConfiguration afConfig = getAutoFeaturesConfig(); +// +// if ( afConfig != null ) { +// AutoFeaturesFileConfig afc = new AutoFeaturesFileConfig(); +// success = afc.saveConf(afConfig); +// +// // reload the internal settings: +// setup(); +// } +// return success; +// } + + /** + *

If the fortune level is zero, then this function will always return a value of one. + *

+ * + *

If it is non-zero, then this function will return a value of one, plus an + * equally distributed chance of returning an additional 0 to fortuneLevel + * bonuses of that material. + *

+ * + *

This applies to "all" materials within the mine, including chests, shulkers, + * signs etc... + *

+ * + * @param fortuneLevel + * @return + */ + public int getDropCount(int fortuneLevel) { + int j = + fortuneLevel == 0 ? + 1 : + 1 + getRandom().nextInt(fortuneLevel + 1); + + return j; + } + + + protected boolean hasSilkTouch (ItemStack itemInHand){ + return itemInHand.getEnchantments().containsKey(Enchantment.SILK_TOUCH); + } + + protected boolean hasFortune(ItemStack itemInHand){ + return itemInHand.getEnchantments().containsKey(Enchantment.LOOT_BONUS_BLOCKS); + } + protected short getFortune(ItemStack itemInHand){ + return (short) itemInHand.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS); + } + + /* + * Drops are wrong with old 1.14.4 releases of spigot + * but got fixed in newer versions. + * + * For older versions, a good way to get the right drops would be to use BlockDropItemEvent.getItems(), but it's deprecated + * */ + protected int autoPickup( boolean autoPickup, Player player, ItemStack itemInHand, BlockBreakEvent e ) { + int count = 0; + if (autoPickup) { + + // The following is not the correct drops: + Collection drops = e.getBlock().getDrops(itemInHand); + if (drops != null && drops.size() > 0 ) { + + // Need better drop calculation that is not using the getDrops function. + short fortuneLevel = getFortune(itemInHand); + + + // Adds in additional drop items: + calculateDropAdditions( itemInHand, drops ); + + if ( isBoolean( AutoFeatures.isCalculateSilkEnabled ) && + hasSilkTouch( itemInHand )) { + + calculateSilkTouch( itemInHand, drops ); + } + + // Add the item to the inventory + for ( ItemStack itemStack : drops ) { + + if ( isBoolean( AutoFeatures.isCalculateFortuneEnabled ) ) { + // calculateFortune directly modifies the quantity on the blocks ItemStack: + calculateFortune( itemStack, fortuneLevel ); + } + + count += itemStack.getAmount(); + dropExtra( player.getInventory().addItem(itemStack), player, e.getBlock() ); + } + + // Auto pickup has been successful. Now clean up. + if ( count > 0 ) { + + // Set the broken block to AIR and cancel the event + e.setCancelled(true); + e.getBlock().setType(Material.AIR); + + // Maybe needed to prevent drop side effects: + e.getBlock().getDrops().clear(); + + // calculate durability impact: Include item durability resistance. + if ( isBoolean( AutoFeatures.isCalculateDurabilityEnabled ) ) { + + // value of 0 = normal durability. Value 100 = never calculate durability. + int durabilityResistance = 0; + if ( isBoolean( AutoFeatures.loreDurabiltyResistance ) ) { + durabilityResistance = getDurabilityResistance( itemInHand, + getMessage( AutoFeatures.loreDurabiltyResistanceName ) ); + } + + calculateDurability( player, itemInHand, durabilityResistance ); + } + } + } + } + return count; + } + + protected void autoSmelt( boolean autoSmelt, String sourceStr, String destinationStr, Player p, Block block ) { + + if ( autoSmelt ) { + XMaterial source = SpigotUtil.getXMaterial( sourceStr ); + XMaterial destination = SpigotUtil.getXMaterial( destinationStr ); + + ItemStack sourceStack = source.parseItem(); + ItemStack destStack = destination.parseItem(); + + if ( sourceStack != null && destStack != null && + p.getInventory().containsAtLeast( sourceStack, 1 ) ) { + + int count = itemCount(source, p); + if ( count > 0 ) { + sourceStack.setAmount( count ); + destStack.setAmount( count ); + + p.getInventory().removeItem(sourceStack); + + dropExtra( p.getInventory().addItem(destStack), p, block ); + } + } + + } + } + protected void autoBlock( boolean autoBlock, String sourceStr, String destinationStr, Player p, Block block ) { + autoBlock(autoBlock, sourceStr, destinationStr, 9, p, block ); + } + + protected void autoBlock( boolean autoBlock, String sourceStr, String destinationStr, int targetCount, Player p, Block block ) { + + XMaterial source = SpigotUtil.getXMaterial( sourceStr ); + XMaterial destination = SpigotUtil.getXMaterial( destinationStr ); + + if ( autoBlock ) { + int count = itemCount(source, p); + if ( count >= targetCount ) { + int mult = count / targetCount; + + p.getInventory().removeItem(SpigotUtil.getItemStack(source, mult * targetCount)); + dropExtra( p.getInventory().addItem(SpigotUtil.getItemStack(destination, mult)), p, block); + } + } + } + +// /** +// *

Lapis is really dyed ink sacks, so need to have special processing to ensure we process the +// * correct material, and not just any ink sack. +// *

+// * +// *

Warning: this will not work with minecraft 1.15.x since magic numbers have been +// * eliminated. +// *

+// * +// * @param autoBlock +// * @param player +// */ +// protected void autoBlockLapis( boolean autoBlock, Player player, Block block ) { +// if ( autoBlock ) { +// // ink_sack = 351:4 +// +// Material mat = Material.matchMaterial( "INK_SACK" ); +// short typeId = 4; +// +// // try both methods to get lapis: +// +// try { +// convertLapisBlock( player, block, mat, typeId ); +// } +// catch ( Exception e ) { +// // Ignore exception. +// } +// +// mat = Material.matchMaterial( "lapis_lazuli" ); +// if ( mat != null ) { +// +// try { +// convertLapisBlock( player, block, mat ); +// } +// catch ( Exception e ) { +// // Ignore exception. +// } +// } +// +// } +// } + + +// private void convertLapisBlock( Player player, Block block, Material mat, int typeId ) { +// XMaterial xMat = SpigotUtil.getXMaterial( mat ); +// int count = itemCount(xMat, typeId, player); +// +// if ( count >= 9 ) { +// int mult = count / 9; +// +// ItemStack removeLapisItemStack = XMaterial.LAPIS_LAZULI.parseItem(); +// removeLapisItemStack.setAmount( mult * 9 ); +// +//// ItemStack removeLapisItemStack = new ItemStack( mat, mult * 9, (short) typeId); +// player.getInventory().removeItem(removeLapisItemStack); +// +// dropExtra( player.getInventory().addItem(new ItemStack(Material.LAPIS_BLOCK, mult)), player, block ); +// +// } +// } + +// private void convertLapisBlock( Player player, Block block, Material mat ) { +// XMaterial xMat = SpigotUtil.getXMaterial( mat ); +// int count = itemCount(xMat, player); +// +// if ( count >= 9 ) { +// int mult = count / 9; +// +// ItemStack removeLapisItemStack = XMaterial.LAPIS_LAZULI.parseItem(); +// removeLapisItemStack.setAmount( mult * 9 ); +// +//// ItemStack removeLapisItemStack = new ItemStack( mat, mult * 9 ); +// player.getInventory().removeItem(removeLapisItemStack); +// +// dropExtra( player.getInventory().addItem( +// new ItemStack(Material.LAPIS_BLOCK, mult)), player, block ); +// } +// } + + private int itemCount(XMaterial source, Player player) { + int count = 0; + if ( source != null ) { + ItemStack testStack = source.parseItem(); + + PlayerInventory inv = player.getInventory(); + for (ItemStack is : inv.getContents() ) { + if ( is != null && is.isSimilar( testStack ) ) { + count += is.getAmount(); + } + } + } + return count; + } + + +// private int itemCount(Material source, int typeId, Player player) { +// int count = 0; +// PlayerInventory inv = player.getInventory(); +// +// for (ItemStack is : inv.getContents()) { +// +// if ( is != null && is.getType() != null && is.getType().compareTo( source ) == 0 ) { +// count += is.getAmount(); +// } +// } +// return count; +// } + + /** + *

If the player does not have any more inventory room for the current items, then + * it will drop the extra. + *

+ * @param extra + * @param player + * @param block + */ + private void dropExtra( HashMap extra, Player player, Block block ) { + if ( extra != null && extra.size() > 0 ) { + for ( ItemStack itemStack : extra.values() ) { + player.getWorld().dropItem( player.getLocation(), itemStack ); + + notifyPlayerThatInventoryIsFull( player, block ); + } + } + } + + private void notifyPlayerThatInventoryIsFull( Player player, Block block ) { + + String message = autoFeaturesConfig.getFeatureMessage( AutoFeatures.inventoryIsFull ); + + // Play sound when full + if ( isBoolean( AutoFeatures.playSoundIfInventoryIsFull ) ) { + Prison.get().getMinecraftVersion() ; + + // This hard coding the Sound enum causes failures in spigot 1.8.8 since it does not exist: + Sound sound; + try { + sound = Sound.valueOf("ANVIL_USE"); // pre 1.9 sound + } catch(IllegalArgumentException e) { + sound = Sound.valueOf("BLOCK_ANVIL_PLACE"); // post 1.9 sound + } + + player.playSound(player.getLocation(), sound, 10F, 1F); + } + + // holographic display for showing full inventory does not work well. +// if ( isBoolean( AutoFeatures.hologramIfInventoryIsFull ) ) { +// displayMessageHologram( block, message , player); +// } +// else { + actionBarVersion(player, message); +// } + } + + private void actionBarVersion(Player player, String message) { + if ( new BluesSpigetSemVerComparator().compareMCVersionTo("1.9.0") < 0 ) { + displayActionBarMessage(player, message); + } + else { + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, + TextComponent.fromLegacyText(SpigotPrison.format(message))); + } + } + + /** + * This is not usable since it not only prevents the player from mining when it is + * displayed, but it cannot be seen since it is shown above the player's head. + * + * The player can continue to mine other materials even though one kind of resource + * be full. + * + * @param block + * @param message + * @param p + */ + @SuppressWarnings( "unused" ) + private void displayMessageHologram(Block block, String message, Player p){ + ArmorStand as = (ArmorStand) block.getLocation().getWorld().spawnEntity( + p.getLocation(), EntityType.ARMOR_STAND); + as.setGravity(false); + as.setCanPickupItems(false); + as.setCustomNameVisible(true); + as.setVisible(false); + as.setCustomName(SpigotPrison.format(message)); + Bukkit.getScheduler().scheduleSyncDelayedTask(SpigotPrison.getInstance(), as::remove, (7L * 20L)); + } + + private void displayActionBarMessage(Player player, String message) { + SpigotPlayer prisonPlayer = new SpigotPlayer( player ); + Prison.get().getPlatform().showActionBar( prisonPlayer, message, 80 ); + } + + + /** + *

Using the ItemLoreEnables, check to see if the item in the hand has the + * specified Lore. If so, then return 100.0 if just the lore, otherwise + * if it has a number to the right of the Lore, convert it to a double and + * return it. + *

+ * + *

The only valid value to follow the lore, can only be a double number, + * or an integer. For example 1.234, 50, 75.567, 99.0, or 100.0. + * Any value less than 0 will be zero, which is the same as no lore (disabled). + * Any value greater than 100 will be 100.0. No values following the lore will + * be treated as 100.0 percent. Do not include percent sign or unit of measure. + *

+ * + * @param loreEnabler + * @param player + * @return Percent chance of Lore enablement. + */ + protected double doesItemHaveAutoFeatureLore( ItemLoreEnablers loreEnabler, Player player ) { + double results = 100.0; + + ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( player ); + if ( itemInHand.hasItemMeta() ) { + ItemMeta meta = itemInHand.getItemMeta(); + if ( meta.hasLore() ) { + for ( String lore : meta.getLore() ) { + if ( lore.startsWith( loreEnabler.name() )) { + String value = lore.replace( loreEnabler.name(), "" ).trim(); + + if ( value.length() > 0 ) { + + try { + results = Double.parseDouble( value ); + } + catch ( NumberFormatException e ) { + + // Error: Default to 100% + // Do not generate log messages since there will be 1000's... + results = 100.0; + } + + if ( results < 0.0 ) { + results = 0.0; + } + + if ( results > 100.0 ) { + results = 100.0; + } + } + } + } + } + } + + return results; + } + + protected int autoFeaturePickup( BlockBreakEvent e, Player p ) { + Material brokenBlock = e.getBlock().getType(); + String blockName = brokenBlock.toString().toLowerCase(); + + ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( p ); + + int count = 0; + + if ( isBoolean( AutoFeatures.autoPickupAllBlocks ) ) { + count += autoPickup( true, p, itemInHand, e ); + + } + else { + + switch (blockName) { + case "cobblestone": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupCobbleStone ), + p, itemInHand, e ); + break; + + case "stone": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupStone ), + p, itemInHand, e ); + break; + + case "gold_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupGoldOre ), + p, itemInHand, e ); + break; + + case "iron_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupIronOre ), + p, itemInHand, e ); + break; + + case "coal_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupCoalOre ), + p, itemInHand, e ); + break; + + case "diamond_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupDiamondOre ), + p, itemInHand, e ); + break; + + case "redstone_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupRedStoneOre ), + p, itemInHand, e ); + break; + + case "emerald_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupEmeraldOre ), + p, itemInHand, e ); + break; + + case "quartz_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupQuartzOre ), + p, itemInHand, e ); + break; + + case "lapis_ore": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupLapisOre ), + p, itemInHand, e ); + break; + + case "snow_ball": + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupSnowBall ), + p, itemInHand, e ); + break; + + case "glowstone_dust": // works 1.15.2 + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ) || + isBoolean( AutoFeatures.autoPickupGlowstoneDust ), + p, itemInHand, e ); + break; + + default: + count += autoPickup( isBoolean( AutoFeatures.autoPickupAllBlocks ), p, itemInHand, e ); + break; + } + + + } + +// Output.get().logInfo( "In mine: %s blockName= [%s] %s drops= %s count= %s dropNumber= %s ", +// mine.getName(), blockName, Integer.toString( dropNumber ), +// (e.getBlock().getDrops(itemInHand) != null ? e.getBlock().getDrops(itemInHand).size() : "-=null=-"), +// Integer.toString( count ), Integer.toString( dropNumber ) +// ); + + return count; + } + + + protected void autoFeatureSmelt( BlockBreakEvent e, Player p ) + { + Block block = e.getBlock(); + + autoSmelt( isBoolean( AutoFeatures.autoSmeltAllBlocks ) || isBoolean( AutoFeatures.autoSmeltGoldOre ), + "GOLD_ORE", "GOLD_INGOT", p, block ); + + autoSmelt( isBoolean( AutoFeatures.autoSmeltAllBlocks ) || isBoolean( AutoFeatures.autoSmeltIronOre ), + "IRON_ORE", "IRON_INGOT", p, block ); + } + + + protected void autoFeatureBlock( BlockBreakEvent e, Player p ) + { + Block block = e.getBlock(); + + // Any autoBlock target could be enabled, and could have multiples of 9, so perform the + // checks within each block type's function call. So in one pass, could hit on more + // than one of these for multiple times too. + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockGoldBlock ), + "GOLD_INGOT", "GOLD_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockIronBlock ), + "IRON_INGOT", "IRON_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockCoalBlock ), + "COAL", "COAL_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockDiamondBlock ), + "DIAMOND", "DIAMOND_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockRedstoneBlock ), + "REDSTONE","REDSTONE_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockEmeraldBlock ), + "EMERALD", "EMERALD_BLOCK", p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockQuartzBlock ), + "QUARTZ", "QUARTZ_BLOCK", 4, p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockPrismarineBlock ), + "PRISMARINE_SHARD", "PRISMARINE", 4, p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockSnowBlock ), + "SNOW_BALL", "SNOW_BLOCK", 4, p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockGlowstone ), + "GLOWSTONE_DUST", "GLOWSTONE", 4, p, block ); + + autoBlock( isBoolean( AutoFeatures.autoBlockAllBlocks ) || + isBoolean( AutoFeatures.autoBlockLapisBlock ), + "LAPIS_LAZULI", "LAPIS_BLOCK", p, block ); + } + + + + protected void itemLoreCounter( ItemStack itemInHand, String itemLore, int blocks ) + { + if ( itemInHand.hasItemMeta() ) { + + List lore = new ArrayList<>(); + + itemLore = itemLore.trim() + " "; + + itemLore = Text.translateAmpColorCodes( itemLore.trim() + " "); + +// String prisonBlockBroken = itemLore.getLore(); + + ItemMeta meta = itemInHand.getItemMeta(); + + if ( meta.hasLore() ) { + lore = meta.getLore(); + + boolean found = false; + for( int i = 0; i < lore.size(); i++ ) { + if ( lore.get( i ).startsWith( itemLore ) ) { + String val = lore.get( i ).replace( itemLore, "" ).trim(); + + int count = blocks; + try { + count += Integer.parseInt( val ); + } + catch ( NumberFormatException e1 ) { + Output.get().logError( "AutoManager: tool counter failure. lore= [" + + lore.get( i ) + "] val= [" + val + "] error: " + + e1.getMessage() ); } + + lore.set( i, itemLore + count ); + + found = true; + break; + } + } + + if ( !found ) { + lore.add( itemLore + 1 ); + } + + + } else { + lore.add( itemLore + 1 ); + } + + meta.setLore( lore ); + + itemInHand.setItemMeta( meta ); + + // incrementCounterInName( itemInHand, meta ); + + } + } + + /** + *

This function will search for the loreDurabiltyResistanceName within the + * item in the hand, if found it will return the number if it exists. If not + * found, then it will return a value of zero, indicating that no special resistance + * exists, and that durability should be applied as normal. + *

+ * + *

If there is no value after the lore name, then the default is 100 %. + * If a value follows the lore name, then it must be an integer. + * If it is less than 0, then 0. If it is greater than 100, then 100. + *

+ * + * @param itemInHand + * @param itemLore + * @return + */ + protected int getDurabilityResistance( ItemStack itemInHand, String itemLore ) { + int results = 0; + + if ( itemInHand.hasItemMeta() ) { + + List lore = new ArrayList<>(); + + itemLore = itemLore.trim() + " "; + + itemLore = Text.translateAmpColorCodes( itemLore.trim() + " "); + +// String prisonBlockBroken = itemLore.getLore(); + + ItemMeta meta = itemInHand.getItemMeta(); + + if ( meta.hasLore() ) { + lore = meta.getLore(); + + for( int i = 0; i < lore.size(); i++ ) { + if ( lore.get( i ).startsWith( itemLore ) ) { + + // It has the durability resistance lore, so set the results to 100. + // If a value is set, then it will be replaced. + results = 100; + + String val = lore.get( i ).replace( itemLore, "" ).trim(); + + try { + results += Integer.parseInt( val ); + } + catch ( NumberFormatException e1 ) { + Output.get().logError( "AutoManager: tool durability failure. lore= [" + + lore.get( i ) + "] val= [" + val + "] error: " + + e1.getMessage() ); } + + break; + } + } + + } + + if ( results > 100d ) { + results = 100; + } + else if ( results < 0 ) { + results = 0; + } + + } + + return results; + } + + + protected void incrementCounterInName( ItemStack itemInHand, ItemMeta meta ) { + String name = meta.getDisplayName(); + if ( !meta.hasDisplayName() || name == null || name.trim().length() == 0 ) { + name = itemInHand.getType().name().toLowerCase().replace("_", " ").trim(); + name += " [1]"; + } else { + + int j = name.lastIndexOf( ']' ); + if ( j == -1 ) { + name += " [1]"; + } else { + + if ( j != -1 ) { + int i = name.lastIndexOf( '[', j ); + + if ( i != -1 ) { + String numStr = name.substring( i + 1, j ); + +// Output.get().logInfo( String.format( "AutoManager: name: %s : %s ", +// name, numStr) ); + + + try { + int blocksMined = Integer.parseInt( numStr ); + name = name.substring( 0, i ).trim() + " [" + ++blocksMined + "]"; + } + catch ( NumberFormatException e1 ) { + Output.get().logError( "AutoManager: tool counter failure. tool name= [" + + name + "] error: " + + e1.getMessage() ); + } + + } + } + } + + + } + if ( name != null ) { + meta.setDisplayName( name ); + itemInHand.setItemMeta( meta ); + + } + } + + /** + *

This should calculate and apply the durability consumption on the tool. + *

+ * + *

The damage is calculated as a value of one durability, but all damage can be + * skipped if the tool has a durability enchantment. If it does, then there is a + * percent chance of 1 in (1 + durabilityLevel). So if a tool has a durability level + * of 1, then there is a 50% chance. Level of 2, then a 66.6% chance. Level of 3 has + * a 75% chance. And a level of 9 has a 90% chance. There are no upper limits on + * durability enchantment levels. + *

+ * + *

Some blocks may have a damage of greater than 1, but for now, this + * does not take that in to consideration. If hooking up in the future, just + * set the initial damage to the correct value based upon block type that was mined. + *

+ * + *

The parameter durabilityResistance is optional, to disable use a value of ZERO. + * This is a percentage and is calculated first. If random value is equal to the parameter + * or less, then it will skip the durability calculations for the current event. + *

+ * + *

Based upon the following URL. See Tool Durability. + *

+ * https://minecraft.gamepedia.com/Item_durability + * + * @param player + * @param itemInHand - Should be the tool they used to mine or dig the block + * @param durabilityResistance - Chance to prevent durability wear being applied. + * Zero always disables this calculation and allows normal durability calculations + * to be performed. 100 always prevents wear. + */ + protected void calculateDurability( Player player, ItemStack itemInHand, int durabilityResistance ) { + short damage = 1; // Generally 1 unless instant break block then zero. + + if ( durabilityResistance >= 100 ) { + damage = 0; + } + else if ( durabilityResistance > 0 ) { + + if ( getRandom().nextInt( 100 ) <= durabilityResistance ) { + damage = 0; + } + } + + if ( damage > 0 && itemInHand.containsEnchantment( Enchantment.DURABILITY ) ) { + int durabilityLevel = itemInHand.getEnchantmentLevel( Enchantment.DURABILITY ); + + // the chance of losing durability is 1 in (1+level) + // So if the random int == 0, then take damage, otherwise none. + if ( getRandom().nextInt( durabilityLevel ) > 0 ) { + damage = 0; + } + } + + if ( damage > 0 ) { + Compatibility compat = SpigotPrison.getInstance().getCompatibility(); + + int maxDurability = compat.getDurabilityMax( itemInHand ); + + int durability = compat.getDurability( itemInHand );; + + int newDurability = durability + damage; + + if ( newDurability > maxDurability ) { + // Item breaks! ;( + compat.breakItemInMainHand( player ); + } + else { + compat.setDurability( itemInHand, newDurability ); + } + + player.updateInventory(); + + } + } + + /** + *

This function is based upon the following wiki page. + *

+ * https://minecraft.gamepedia.com/Fortune + * + *

"Ore: For coal ore, diamond ore, emerald ore, lapis lazuli ore, + * nether gold ore,‌[upcoming: JE 1.16] and nether quartz ore, + * Fortune I gives a 33% chance to multiply drops by 2 (averaging 33% increase), + * Fortune II gives a chance to multiply drops by 2 or 3 (25% chance each, + * averaging 75% increase), and Fortune III gives a chance to multiply drops + * by 2, 3, or 4 (20% chance each, averaging 120% increase). + *

+ *

"Generally speaking, Fortune gives a weight of 2 to a normal drop chance + * and adds a weight of 1 for each extra drop multiplier. The drop multipliers + * are the integers between 2 and Fortune Level + 1, inclusive. + *

+ *

"The formula to calculate the average drops multiplier is + * 1/(Fortune Level+2) + (Fortune Level+1)/2, which means + * Fortune IV gives 2.67x drops on average, Fortune V gives + * 3.14x drops on average, etc. " + *

+ * + *

Discrete random Glowstone, melons, nether wart, redstone ore, + * sea lanterns, and sweet berries use a discrete uniform distribution, + * meaning each possible drop amount is equally likely to be dropped. + * Fortune increases the maximum number of drops by 1 per level. However, + * maximum drop limitations may apply: glowstone has a cap of 4 glowstone + * dust, sea lanterns have a cap of 5 prismarine crystals, and melons have + * a cap of 9 melon slices. If a drop higher than these maximums is rolled, + * it is rounded down to the cap. + *

+ * + * + * @param blocks + * @param fortuneLevel + */ + protected void calculateFortune( ItemStack blocks, int fortuneLevel ) { + + if ( fortuneLevel > 0 ) { + int count = blocks.getAmount(); + + int multiplier = 1; + + + // Due to variations with gold and wood PickAxe need to use a dynamic + // Material name selection which will fit for the version of MC that is + // being ran. + Material block = blocks.getType(); + + if ( block == Material.COAL || + block == Material.DIAMOND || + block == Material.EMERALD || + block == Material.LAPIS_BLOCK || + block == Material.GOLD_BLOCK || + block == Material.QUARTZ_BLOCK || + block == Material.COAL_ORE || + block == Material.DIAMOND_ORE || + block == Material.EMERALD_ORE || + block == Material.LAPIS_ORE || + block == Material.GOLD_ORE || + block == Material.matchMaterial( "QUARTZ_ORE" ) ) { + + multiplier = calculateFortuneMultiplier( fortuneLevel, multiplier ); + + // multiply the multiplier: + count *= multiplier; + } + else if ( block == Material.GLOWSTONE || + block == Material.GLOWSTONE_DUST || + block == Material.REDSTONE || + block == Material.SEA_LANTERN || + block == Material.matchMaterial( "GLOWING_REDSTONE_ORE" ) || + block == Material.PRISMARINE || + + block == Material.matchMaterial( "BEETROOT_SEEDS" ) || + block == Material.CARROT || + block == Material.MELON || + block == Material.MELON_SEEDS || + block == Material.matchMaterial( "NETHER_WARTS" ) || + block == Material.POTATO || + block == Material.GRASS || + block == Material.WHEAT ) { + multiplier = getRandom().nextInt( fortuneLevel ); + + // limits slightly greater than standard: + if ( block == Material.GLOWSTONE ) { + // standard: 4 + if ( multiplier > 5 ) { + multiplier = 5; + } + } + else if ( block == Material.SEA_LANTERN ) { + // standard: 5 + if ( multiplier > 6 ) { + multiplier = 6; + } + } + else if ( block == Material.MELON ) { + // standard: 9 + if ( multiplier > 11 ) { + multiplier = 11; + } + } + + // add the multiplier to the count: + count += multiplier; + + } + + // The count has the final value so set it as the amount: + blocks.setAmount( count ); + + } + + + + // cannot use switches with dynamic Material types: +// switch ( blocks.getType() ){ +// +// case COAL: +// case DIAMOND: +// case EMERALD: +// case LAPIS_BLOCK: +// case GOLD_BLOCK: +// case QUARTZ_BLOCK: +// case COAL_ORE: +// case DIAMOND_ORE: +// case EMERALD_ORE: +// case LAPIS_ORE: +// case GOLD_ORE: +// case QUARTZ_ORE: +// +// multiplier = calculateFortuneMultiplier( fortuneLevel, multiplier ); +// +// // multiply the multiplier: +// count *= multiplier; +// break; +// +// case GLOWSTONE: +// case GLOWSTONE_DUST: +// case REDSTONE: +// case SEA_LANTERN: +// case GLOWING_REDSTONE_ORE: +// case PRISMARINE: +// +// case BEETROOT_SEEDS: +// case CARROT: +// case MELON: +// case MELON_SEEDS: +// case NETHER_WARTS: +// case POTATO: +// case GRASS: +// case WHEAT: +// +// multiplier = getRandom().nextInt( fortuneLevel ); +// +// switch ( blocks.getType() ) +// { +// // limits slightly greater than standard: +// case GLOWSTONE: +// // standard: 4 +// if ( multiplier > 5 ) { +// multiplier = 5; +// } +// break; +// case SEA_LANTERN: +// // standard: 5 +// if ( multiplier > 6 ) { +// multiplier = 6; +// } +// break; +// case MELON: +// // standard: 9 +// if ( multiplier > 11 ) { +// multiplier = 11; +// } +// +// default: +// break; +// } +// +// // add the multiplier to the count: +// count += multiplier; +// +// default: +// break; +// } + +// // The count has the final value so set it as the amount: +// blocks.setAmount( count ); + } + + + private int calculateFortuneMultiplier( int fortuneLevel, int multiplier ) + { + int rnd = getRandom().nextInt( 100 ); + + switch ( fortuneLevel ) + { + case 0: + break; + case 1: + if ( rnd <= 33 ) { + multiplier = 2; + } + break; + + case 2: + if ( rnd <= 25 ) { + multiplier = 2; + } + else if ( rnd <= 50 ) { + multiplier = 3; + } + break; + + case 3: + if ( rnd <= 20 ) { + multiplier = 2; + } + else if ( rnd <= 40 ) { + multiplier = 3; + } + else if ( rnd <= 60 ) { + multiplier = 4; + } + break; + + + case 4: + if ( rnd <= 16 ) { + multiplier = 2; + } + else if ( rnd <= 32 ) { + multiplier = 3; + } + else if ( rnd <= 48 ) { + multiplier = 4; + } + else if ( rnd <= 64 ) { + multiplier = 5; + } + break; + + default: + // values of 5 or higher + if ( rnd <= 16 ) { + multiplier = 2; + } + else if ( rnd <= 32 ) { + multiplier = 3; + } + else if ( rnd <= 48 ) { + multiplier = 4; + } + else if ( rnd <= 64 ) { + multiplier = 5; + } + else if ( rnd <= 74 ) { + // Only 8% not 16% chance + multiplier = 6; + } + break; + } + return multiplier; + } + + /** + *

This function has yet to be implemented, but it should implement behavior if + * silk touch is enabled for the tool. + *

+ * + * @param itemInHand + * @param drops + */ + @SuppressWarnings( "unused" ) + private void calculateSilkTouch( ItemStack itemInHand, Collection drops ) { + + for ( ItemStack itemStack : drops ) { + + // If stack is gravel, then there is a 10% chance of droping flint. + + } + + } + + /** + *

Because of the use of getDrops() function, not all of the correct drops are actually + * dropped. This function tries to restore some of those special drops. + *

+ * + *

Example of what this does, is to provide a random drop of flint when mining + * coal ore. + *

+ * + *

"When a block of gravel is mined, there is a 10% chance for a single piece of flint + * to drop instead of the gravel block. When mined with a Fortune-enchanted tool, this chance + * increases to 14% at Fortune I, 25% at Fortune II, and 100% at Fortune III. Gravel mined + * using a tool with Silk Touch or gravel that fell on a non-solid block never produces flint." + * wiki + *

+ * + *

For example gravel having random flint drops. + *

+ * + * @param itemInHand + * @param drops + */ + private void calculateDropAdditions( ItemStack itemInHand, Collection drops ) { + + for ( ItemStack itemStack : drops ) { + + // If gravel and has the 10% chance whereas rnd is zero, which is 1 out of 10. + // But if has silk touch, then never drop flint. + calculateDropAdditionsGravelFlint( itemInHand, itemStack, drops ); + + } + + } + + + /** + *

For gravel flint drops, this function adds special processing to increase the quantity + * of flint drops from the standard 1, to be influence by fortune enchants. If fortune + * is >= 3, then add one to the quantity drop, plus a random chance to add floor(fortune / 5). + * So if fortune is 3, then the drop will always be 2. If fortune is 5, then drop will + * be 2, plus a random chance of one additional drop. If fortune is 20, then drop will be + * 2, plus an equal random chance to add 0 to 4 additional flints to the quantity of + * the flint drop. The other thing that is different from vanilla, is that if the player + * will get a flint drop, they will still get the normal gravel drop. + *

+ * + * @param itemInHand + * @param itemStack + * @param drops + */ + private void calculateDropAdditionsGravelFlint( ItemStack itemInHand, + ItemStack itemStack, Collection drops ) { + if ( itemStack.getType() == Material.GRAVEL && + !hasSilkTouch( itemInHand ) ) { + + int quantity = 1; + int threshold = 10; + + // If fortune is enabled on the tool, then increase drop oddds by: + // 1 = 14%, 2 = 25%, 3+ = 100% + int fortune = getFortune( itemInHand ); + switch ( fortune ) { + case 0: + // No additional threshold when fortune is zero: + break; + case 1: + threshold = 14; + break; + case 2: + threshold = 25; + break; + case 3: + default: + // if Fortune 3 or higher, default to 100% drop: + threshold = 100; + break; + } + + // If zero, then 10% chance of 1 out of 10. + if ( getRandom().nextInt( 100 ) <= threshold ) { + + // If fortune is >= 3, then add one to the quantity drop, plus a + // random chance to add floor(fortune / 5). + if ( fortune >= 3 ) { + quantity += 1 + getRandom().nextInt( Math.floorDiv( fortune, 5 ) ); + } + + ItemStack flintStack = new ItemStack( Material.FLINT, quantity ); + drops.add( flintStack ); + } + } + } + + public Random getRandom() { + return random; + } + +// public boolean isAutoManagerEnabled() { +// return isAutoManagerEnabled; +// } + +// public boolean isDropItemsIfInventoryIsFull() { +// return dropItemsIfInventoryIsFull; +// } + +// public boolean isPlaySoundIfInventoryIsFull() { +// return playSoundIfInventoryIsFull; +// } + +// public boolean isHologramIfInventoryIsFull() { +// return hologramIfInventoryIsFull; +// } + +// public boolean isAutoPickupEnabled() { +// return autoPickupEnabled; +// } +// +// public boolean isAutoPickupAllBlocks() { +// return autoPickupAllBlocks; +// } + +// public boolean isAutoPickupCobbleStone() { +// return autoPickupCobbleStone; +// } +// +// public boolean isAutoPickupStone() { +// return autoPickupStone; +// } +// +// public boolean isAutoPickupGoldOre() { +// return autoPickupGoldOre; +// } +// +// public boolean isAutoPickupIronOre() { +// return autoPickupIronOre; +// } +// +// public boolean isAutoPickupCoalOre() { +// return autoPickupCoalOre; +// } +// +// public boolean isAutoPickupDiamondOre() { +// return autoPickupDiamondOre; +// } +// +// public boolean isAutoPickupRedstoneOre() { +// return autoPickupRedstoneOre; +// } +// +// public boolean isAutoPickupEmeraldOre() { +// return autoPickupEmeraldOre; +// } +// +// public boolean isAutoPickupQuartzOre() { +// return autoPickupQuartzOre; +// } +// +// public boolean isAutoPickupLapisOre() { +// return autoPickupLapisOre; +// } +// +// public boolean isAutoPickupSnowBall() { +// return autoPickupSnowBall; +// } +// +// public boolean isAutoPickupGlowstoneDust() { +// return autoPickupGlowstoneDust; +// } + +// public boolean isAutoSmeltEnabled() { +// return autoSmeltEnabled; +// } + +// public boolean isAutoSmeltAllBlocks() { +// return autoSmeltAllBlocks; +// } +// +// public boolean isAutoSmeltGoldOre() { +// return autoSmeltGoldOre; +// } +// +// public boolean isAutoSmeltIronOre() { +// return autoSmeltIronOre; +// } + +// public boolean isAutoBlockEnabled() { +// return autoBlockEnabled; +// } +// +// public boolean isAutoBlockAllBlocks() { +// return autoBlockAllBlocks; +// } + +// public boolean isAutoBlockGoldBlock() { +// return autoBlockGoldBlock; +// } +// +// public boolean isAutoBlockIronBlock() { +// return autoBlockIronBlock; +// } +// +// public boolean isAutoBlockCoalBlock() { +// return autoBlockCoalBlock; +// } +// +// public boolean isAutoBlockDiamondBlock() { +// return autoBlockDiamondBlock; +// } +// +// public boolean isAutoBlockRedstoneBlock() { +// return autoBlockRedstoneBlock; +// } +// +// public boolean isAutoBlockEmeraldBlock() { +// return autoBlockEmeraldBlock; +// } +// +// public boolean isAutoBlockQuartzBlock() { +// return autoBlockQuartzBlock; +// } +// +// public boolean isAutoBlockPrismarineBlock() { +// return autoBlockPrismarineBlock; +// } +// +// public boolean isAutoBlockLapisBlock() { +// return autoBlockLapisBlock; +// } +// +// public boolean isAutoBlockSnowBlock() { +// return autoBlockSnowBlock; +// } +// +// public boolean isAutoBlockGlowstone() { +// return autoBlockGlowstone; +// } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventListener.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventListener.java new file mode 100644 index 000000000..f0dcff45d --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventListener.java @@ -0,0 +1,373 @@ +package tech.mcprison.prison.spigot.block; + +import java.util.Optional; +import java.util.TreeMap; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; + +import com.vk2gpz.tokenenchant.event.TEBlockExplodeEvent; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.modules.Module; + +/** + *

This is a pivotal class that "monitors" onBlockBreak events so it can + * help keep accurate counts within all mines of how many blocks have been + * mined. + *

+ * + *

Note: Because this is a MONITOR event, we cannot do anything with the + * target block here. Mostly because everything has already been done with it, and + * this is only intended to MONITOR the final results. + *

+ * + *

This is a very critical class too, in that every single block break on + * server will hit this class, even if the block is not within a mine. + * Therefore it is paramount that the computational cost, and temporal consumption, + * is as minimal as possible. Many of the behaviors, or goals, that this class + * tries to accomplish can be done through simpler way, but performance is + * a high goal so some of the code may be convoluted, but it has its purpose. + *

+ * + *

Performance considerations: Unfortunately, this class can only be optimized + * for mines. If a player is within a mine and breaking blocks, then we can + * help ensure the next block they break within the mine will have optimal performance + * in getting the reference to that mine. If they are outside of all mines, like + * in a free world, then all mines will have to be checked each time. There may + * be room for optimizations in the future. + *

+ * + *

Goals and Purposes: These are needs of this class, or more specifically, the + * goals and purposes that it is trying to solve.

+ * + *
    + *
  • Record onBlockBreak events within a mine - Primary goal and purpose
  • + *
  • If a mine becomes empty, submit a manual reset - Secondary purpose
  • + *
+ * + *

Resources: These are the resources that are needed and their interdependencies. + *

+ * + *
    + *
  • Ranks Module: Not needed & not used. + *
      + *
    • Would have added a level of complexity that is not needed
    • + *
    • Could have added a last mine used field, but the overhead of + * getting a Prison player object would have been too high.
    + *
  • Using internal player lookup and caching instead for optimal performance
  • + *
+ * + *
  • MineModule: Required + *
      + *
    • Used to find the correct mine
    • + *
    • Actions performed on the mine: increment block break count & submit a manual + * mine reset job if block count hits zero.
    • + *
    + *
  • + * + * + * + *

    General performance results: Enable the commented out code to test the milliseconds it + * spends on monitoring: + *

    + * + *
      + *
    • Spigot 1.8.8, drop, single block mine: + * 0.03371 0.033003 0.036944 0.033234 0.032782 0.030926 0.036374 0.036431
    • + *
    • Spigot 1.8.8, drop, large block mine: + * 0.01759 0.017882 0.00455 .003232 0.002935 0.00344 0.00344 0.003248 0.002831 0.002898
    • + *
    • Spigot 1.8.8, drop, wilderness: + * 0.024488 0.016086 0.010138 0.007265 0.009756 0.008951 0.009319 0.008664 0.007657 0.007013
    • + *
    • Spigot 1.8.8, drop, wilderness: + * 0.014462 0.009286 0.011038 0.011808 0.014291 0.011617 0.007516 0.008267 0.005324 0.004896
    • + *
    • Spigot 1.8.8, drop, wilderness, disabled: + * 0.001549 0.001138 0.001162 0.001487 0.00181 0.000084
    • + * + *
    + * + */ +public class OnBlockBreakEventListener + implements Listener { + +// private final TreeMap playerCache; + + private PrisonMines prisonMineManager; + private boolean mineModuleDisabled = false; + + private int uses = 0; + private long usesElapsedTimeNano = 0L; + + public OnBlockBreakEventListener() { + super(); + +// this.playerCache = new TreeMap<>(); + this.prisonMineManager = null; + } + + /** + *

    The EventPriorty.MONITOR means that the state of the event is OVER AND DONE, + * so this function CANNOT do anything with the block, other than "monitor" what + * happened. That is all we need to do, is to just count the number of blocks within + * a mine that have been broken. + *

    + * + *

    Note: Because this is a MONITOR event, we cannot do anything with the + * target block here. Mostly because everything has already been done with it, and + * this is only intended to MONITOR the final results. + *

    + * + *

    One interesting fact about this monitoring is that we know that a block was broken, + * not because of what is left (should be air), but because this function was called. + * There is a chance that the event was canceled and the block remains unbroken, which + * is what WorldGuard would do. But the event will also be canceled when auto pickup is + * enabled, and at that point the BlockType will be air. + *

    + * + *

    If the event is canceled it's important to check to see that the BlockType is Air, + * since something already broke the block and took the drop. + * If it is not canceled we still need to count it since it will be a normal drop. + *

    + * + * @param e + */ + @EventHandler(priority=EventPriority.MONITOR) + public void onBlockBreak(BlockBreakEvent e) { + + genericBlockEvent( e ); + } + + @EventHandler(priority=EventPriority.MONITOR) + public void onTEBlockExplode(TEBlockExplodeEvent e) { + + genericBlockExplodeEvent( e ); + } + + private void genericBlockEvent( BlockBreakEvent e ) { + // Fast fail: If the prison's mine manager is not loaded, then no point in processing anything. + if ( getPrisonMineManager() != null ) { + + // long startNano = System.nanoTime(); + + + boolean isAir = e.getBlock().getType() != null && e.getBlock().getType() == Material.AIR; + + // If canceled it must be AIR, otherwise if it is not canceled then + // count it since it will be a normal drop + if ( e.isCancelled() && isAir || !e.isCancelled() ) { + + // Need to wrap in a Prison block so it can be used with the mines: + SpigotBlock block = new SpigotBlock(e.getBlock()); + + Long playerUUIDLSB = Long.valueOf( e.getPlayer().getUniqueId().getLeastSignificantBits() ); + + // Get the cached mine, if it exists: + Mine mine = getPlayerCache().get( playerUUIDLSB ); + + if ( mine == null || !mine.isInMine( block.getLocation() ) ) { + // Look for the correct mine to use. + // Set mine to null so if cannot find the right one it will return a null: + mine = findMineLocation( block ); + + // Store the mine in the player cache if not null: + if ( mine != null ) { + getPlayerCache().put( playerUUIDLSB, mine ); + } + } + + // This is where the processing actually happens: + if ( mine != null ) { + doAction( mine, e ); + } + } + + // for debug use: Uncomment to use. +// String message = incrementUses(System.nanoTime() - startNano); +// if ( message != null ) { +// e.getPlayer().sendMessage( message ); +// } + } + } + + + /** + *

    Since there are multiple blocks associated with this event, pull out the player first and + * get the mine, then loop through those blocks to make sure they are within the mine. + *

    + * + *

    The logic in this function is slightly different compared to genericBlockEvent() because this + * event contains multiple blocks so it's far more efficient to process the player data once. + * So that basically needed a slight refactoring. + *

    + * + * @param e + */ + private void genericBlockExplodeEvent( TEBlockExplodeEvent e ) + { + // Fast fail: If the prison's mine manager is not loaded, then no point in processing anything. + if ( getPrisonMineManager() != null ) { + + // long startNano = System.nanoTime(); + Long playerUUIDLSB = Long.valueOf( e.getPlayer().getUniqueId().getLeastSignificantBits() ); + + // Get the cached mine, if it exists: + Mine mine = getPlayerCache().get( playerUUIDLSB ); + + if ( mine == null ) { + + // have to go through all blocks since some blocks may be outside the mine. + // but terminate search upon first find: + for ( Block blk : e.blockList() ) { + // Need to wrap in a Prison block so it can be used with the mines: + SpigotBlock block = new SpigotBlock(blk); + + // Look for the correct mine to use. + // Set mine to null so if cannot find the right one it will return a null: + mine = findMineLocation( block ); + + // Store the mine in the player cache if not null: + if ( mine != null ) { + getPlayerCache().put( playerUUIDLSB, mine ); + + // we found the mine! + break; + } + } + } + + // now process all blocks: + if ( mine != null ) { + // have to go through all blocks since some blocks may be outside the mine. + // but terminate search upon first find: + + int blockCount = 0; + for ( Block blk : e.blockList() ) { + boolean isAir = blk.getType() != null && blk.getType() == Material.AIR; + + // If canceled it must be AIR, otherwise if it is not canceled then + // count it since it will be a normal drop + if ( e.isCancelled() && isAir || !e.isCancelled() ) { + + // Need to wrap in a Prison block so it can be used with the mines: + SpigotBlock block = new SpigotBlock(blk); + + if ( !mine.isInMine( block.getLocation() ) ) { + + blockCount++; + } + + } + } + if ( blockCount > 0 ) { + // This is where the processing actually happens: + doAction( mine, e, blockCount ); + + } + } + + + + // for debug use: Uncomment to use. +// String message = incrementUses(System.nanoTime() - startNano); +// if ( message != null ) { +// e.getPlayer().sendMessage( message ); +// } + } + } + + + public void doAction( Mine mine, BlockBreakEvent e ) { + mine.incrementBlockBreakCount(); + mine.incrementTotalBlocksMined(); + + // Other possible processing: + + // Checks to see if the mine ran out of blocks, and if it did, then + // it will reset the mine: + mine.checkZeroBlockReset(); + } + + public void doAction( Mine mine, TEBlockExplodeEvent e, int blockCount ) { + + mine.addBlockBreakCount( blockCount ); + mine.addTotalBlocksMined( blockCount ); + + // Other possible processing: + + // Checks to see if the mine ran out of blocks, and if it did, then + // it will reset the mine: + mine.checkZeroBlockReset(); + } + + private Mine findMineLocation( SpigotBlock block ) { + return getPrisonMineManager().findMineLocation( block.getLocation() ); + } + +// /** +// *

    Search all mines to find if the given block is located within any +// * of the mines. If not, then return a null. +// *

    +// * +// * @param block +// * @return +// */ +// private Mine findMineLocation( SpigotBlock block ) { +// Mine mine = null; +// for ( Mine m : getPrisonMineManager().getMines() ) { +// if ( m.isInMine( block.getLocation() ) ) { +// mine = m; +// break; +// } +// } +// return mine; +// } + + private TreeMap getPlayerCache() { + return getPrisonMineManager().getPlayerCache(); + } + + private PrisonMines getPrisonMineManager() { + if ( prisonMineManager == null && !isMineModuleDisabled() ) { + Optional mmOptional = Prison.get().getModuleManager().getModule( PrisonMines.MODULE_NAME ); + if ( mmOptional.isPresent() && mmOptional.get().isEnabled() ) { + PrisonMines prisonMines = (PrisonMines) mmOptional.get(); + this.prisonMineManager = prisonMines; + } else { + setMineModuleDisabled( true ); + } + } + return prisonMineManager; + } + + private boolean isMineModuleDisabled() { + return mineModuleDisabled; + } + private void setMineModuleDisabled( boolean mineModuleDisabled ) { + this.mineModuleDisabled = mineModuleDisabled; + } + + + @SuppressWarnings( "unused" ) + private synchronized String incrementUses(Long elapsedNano) { + String message = null; + usesElapsedTimeNano += elapsedNano; + + if ( ++uses >= 100 ) { + double avgNano = usesElapsedTimeNano / uses; + double avgMs = avgNano / 1000000; + message = String.format( "OnBlockBreak: count= %s avgNano= %s avgMs= %s ", + Integer.toString(uses), Double.toString(avgNano), Double.toString(avgMs) ); + + uses = 0; + usesElapsedTimeNano = 0L; + } + return message; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotBlock.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotBlock.java index d4ee355e5..58b5895dc 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotBlock.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotBlock.java @@ -18,18 +18,19 @@ package tech.mcprison.prison.spigot.block; -import org.bukkit.material.MaterialData; +import java.util.ArrayList; +import java.util.List; + import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.block.Block; import tech.mcprison.prison.internal.block.BlockFace; import tech.mcprison.prison.internal.block.BlockState; +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.spigot.SpigotPrison; import tech.mcprison.prison.spigot.SpigotUtil; import tech.mcprison.prison.util.BlockType; import tech.mcprison.prison.util.Location; -import java.util.ArrayList; -import java.util.List; - /** * @author Faizaan A. Datoo */ @@ -50,14 +51,81 @@ public SpigotBlock(org.bukkit.block.Block bBlock) { } @Override public BlockType getType() { - return SpigotUtil.materialToBlockType(bBlock.getType()); + return SpigotPrison.getInstance().getCompatibility().getBlockType( bBlock ); +// return SpigotUtil.materialToBlockType(bBlock.getType()); } - @SuppressWarnings( "deprecation" ) - @Override public void setType(BlockType type) { - MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); - bBlock.setType(materialData.getItemType()); - bBlock.setData(materialData.getData()); + @Override + public PrisonBlock getPrisonBlock() { + return SpigotPrison.getInstance().getCompatibility().getPrisonBlock( bBlock ); + + } + + public void setPrisonBlock( PrisonBlock prisonBlock ) { + SpigotPrison.getInstance().getCompatibility(). + updateSpigotBlock( prisonBlock, bBlock ); + } + + /** + *

    When setting the Data and Type, turn off apply physics which will reduce the over head on block updates + * by about 1/3. Really do not need to apply physics in the mines especially if no air blocks and nothing + * that could fall (sand) or flow is placed. + *

    + */ + @Override + public void setType(BlockType blockType) { + + SpigotPrison.getInstance().getCompatibility() + .updateSpigotBlock( blockType, bBlock ); + +// if ( type != null && type != BlockType.IGNORE ) { +// +// Material mat = SpigotUtil.getMaterial( type ); +// if ( mat != null ) { +// bBlock.setType( mat, false ); +// } +// +//// Optional xMatO = XMaterial.matchXMaterial( type.name() ); +//// +//// if ( xMatO.isPresent() ) { +//// XMaterial xMat = xMatO.get(); +//// Optional matO = xMat.parseMaterial(); +//// +//// if ( matO.isPresent() ) { +//// Material mat = matO.get(); +//// +//// bBlock.setType( mat, false ); +//// +//// } +//// } +// else { +// // spigot 1.8.8 support for XMaterial: +// // MOSS_STONE LAPIS_LAZULI_ORE LAPIS_LAZULI_BLOCK PILLAR_QUARTZ_BLOCK +// // +// +// Output.get().logWarn( "SpigotBlock.setType: could not match BlockType " + +// type.name() + " defaulting to AIR instead."); +// +// mat = SpigotUtil.getMaterial( BlockType.AIR ); +// if ( mat != null ) { +// bBlock.setType( mat, false ); +// } +// } +// +//// try { +//// MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); +//// bBlock.setType(materialData.getItemType(), false); +//// if ( type.getMaterialVersion() == MaterialVersion.v1_8) { +//// +//// bBlock.setData(materialData.getData(), false); +//// } +//// } +//// catch ( Exception e ) { +//// Output.get().logError( +//// String.format( "BlockType could not be set: %s %s ", +//// (type == null ? "(null)" : type.name()), e.getMessage()) ); +//// } +// } } @Override public BlockState getState() { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonShortcutCommands.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonShortcutCommands.java new file mode 100644 index 000000000..66af76131 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonShortcutCommands.java @@ -0,0 +1,98 @@ +package tech.mcprison.prison.spigot.commands; + +import org.bukkit.event.Listener; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.commands.Arg; +import tech.mcprison.prison.commands.Command; +import tech.mcprison.prison.internal.CommandSender; +import tech.mcprison.prison.spigot.SpigotPrison; + +import java.util.Objects; + +/** + * @author RoyalBlueRanger + * @author GABRYCA + */ +public class PrisonShortcutCommands implements Listener { + + /** + *

    This command, /Prison gui and many others are more of convenience commands which + * allows access to the gui from the base /prison commands. This will allow + * the players to find it easier, and it will also be easier to recall. + *

    + * + *

    The actual gui command, which is /prisonmanager gui is not able to + * be integrated in to the main prison command sets due to the requirement of + * the gui being native spigot. Cannot mix the two. But can have /prison gui + * internally call /prisonmanger gui to give the illusion they are connected. + *

    + * + * @param sender + */ + + @Command(identifier = "prison gui", description = "Opens the Prison GUI menus.", + permissions = "prison.gui", onlyPlayers = true) + public void prisonGui(CommandSender sender) { + + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prison-gui-enabled")).equalsIgnoreCase("true"))){ + sender.sendMessage(SpigotPrison.format("&cThe GUI's disabled, if you want to use it, edit the config.yml!")); + return; + } + + + String formatted = "prisonmanager gui"; + Prison.get().getPlatform().dispatchCommand(sender, formatted); + } + + @Command(identifier = "mines", onlyPlayers = false, + altPermissions = {"-none-", "mines.admin"}) + public void minesGUICommand(CommandSender sender) { + if (!sender.hasPermission("mines.admin") && Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("mines-gui-enabled")).equalsIgnoreCase("true")) { + sender.dispatchCommand("prisonmanager mines"); + } else { + sender.dispatchCommand("mines help"); + } + } + + @Command(identifier = "ranks", onlyPlayers = false, + altPermissions = {"-none-", "ranks.admin"}) + public void ranksGUICommand(CommandSender sender, + @Arg(name = "ladder", def = "default", + description = "If player has no permission to /ranks then /ranks list will be ran instead.") + String ladderName) { + if (!sender.hasPermission("ranks.admin")) { + if ((ladderName.equalsIgnoreCase("default") || ladderName.equalsIgnoreCase("ranks")) && Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("ranks-gui-enabled")).equalsIgnoreCase("true")){ + sender.dispatchCommand("prisonmanager ranks"); + } else if (ladderName.equalsIgnoreCase("prestiges") && Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("ranks-gui-prestiges-enabled")).equalsIgnoreCase("true")){ + sender.dispatchCommand("prisonmanager prestiges"); + } else { + sender.dispatchCommand("ranks list " + ladderName); + } + } else { + sender.dispatchCommand("ranks help"); + } + } + + @Command(identifier = "prestiges", onlyPlayers = true, altPermissions = {"-none-", "prison.admin"}) + public void prestigesGUICommand(CommandSender sender){ + + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prestiges")).equalsIgnoreCase("true"))) { + sender.sendMessage(SpigotPrison.format("&cPrestiges are disabled by default, please edit it in your config.yml!")); + return; + } + + if (Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prestiges-gui-enabled")).equalsIgnoreCase("true")) { + sender.dispatchCommand( "prisonmanager prestiges"); + } else { + sender.dispatchCommand( "ranks list prestiges"); + } + } + + @Command(identifier = "prestige", onlyPlayers = true, altPermissions = "-none-") + public void prestigesPrestigeCommand(CommandSender sender) { + if (Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prestiges")).equalsIgnoreCase("true")) { + sender.dispatchCommand("prisonmanager prestige"); + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotCommands.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotCommands.java new file mode 100644 index 000000000..330c027c2 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotCommands.java @@ -0,0 +1,227 @@ +package tech.mcprison.prison.spigot.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleManager; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.managers.LadderManager; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.SpigotPrisonGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotPlayerMinesGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotConfirmPrestigeGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotPlayerPrestigesGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotPlayerRanksGUI; + +import java.util.Objects; + +/** + * @author GABRYCA + * @author RoyalBlueRanger + */ +public class PrisonSpigotCommands implements CommandExecutor, Listener { + + boolean isChatEventActive; + int id; + + @EventHandler(priority = EventPriority.LOWEST) + public void onChat(AsyncPlayerChatEvent e) { + if (isChatEventActive){ + Player p = e.getPlayer(); + String message = e.getMessage(); + Bukkit.getScheduler().cancelTask(id); + if (message.equalsIgnoreCase("cancel")){ + isChatEventActive = false; + p.sendMessage(SpigotPrison.format("&cPrestige cancelled")); + e.setCancelled(true); + } else if (message.equalsIgnoreCase("confirm")){ + Bukkit.getScheduler().runTask(SpigotPrison.getInstance(), () -> Bukkit.getServer().dispatchCommand(p, "rankup prestiges")); + e.setCancelled(true); + isChatEventActive = false; + } + } + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prison-gui-enabled")).equalsIgnoreCase("true"))){ + sender.sendMessage(SpigotPrison.format("&cThe GUI's disabled, if you want to use it, edit the config.yml!")); + return true; + } + + if(!(sender instanceof Player || sender instanceof tech.mcprison.prison.internal.Player)){ + sender.sendMessage(SpigotPrison.format("&cLooks like you aren't a player")); + return true; + } + + Player p = null; + if (sender instanceof Player) { + p = (Player) sender; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (args.length == 0) { + sender.sendMessage(SpigotPrison.format("&cIncorrect usage, the command should be /prisonmanager -gui-ranks-mines-prestiges-prestige")); + return true; + } + + + if (args[0].equalsIgnoreCase("ranks")){ + return prisonmanagerRanks(sender, p, GuiConfig); + } else if (args[0].equalsIgnoreCase("mines")){ + return prisonmanagerMines(sender, p, GuiConfig); + } else if (args[0].equalsIgnoreCase("prestiges")) { + return prisonmanagerPrestiges(sender, p, GuiConfig); + } else if (args[0].equalsIgnoreCase("prestige")){ + return prisonmanagerPrestige(sender, p); + } else if (args[0].equalsIgnoreCase("gui")){ + return prisonmanagerGUI(sender, p); + + } + + return true; + } + + private boolean prisonmanagerPrestige(CommandSender sender, Player p) { + if (SpigotPrison.getInstance().getConfig().getBoolean("prestiges")) { + + if (!(PrisonRanks.getInstance().getLadderManager().getLadder("prestiges").isPresent())) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "ranks ladder create prestiges"); + } + + PrisonRanks rankPlugin; + + ModuleManager modMan = Prison.get().getModuleManager(); + Module module = modMan == null ? null : modMan.getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + + rankPlugin = (PrisonRanks) module; + + LadderManager lm = null; + if (rankPlugin != null) { + lm = rankPlugin.getLadderManager(); + } + + if (lm != null && (!(lm.getLadder("default").isPresent()) || + !(lm.getLadder("default").get().getLowestRank().isPresent()) || + lm.getLadder("default").get().getLowestRank().get().name == null)) { + sender.sendMessage(SpigotPrison.format("&cThere aren't ranks in the default ladder")); + return true; + } + + if (lm != null && (!(lm.getLadder("prestiges").isPresent()) || + !(lm.getLadder("prestiges").get().getLowestRank().isPresent()) || + lm.getLadder("prestiges").get().getLowestRank().get().name == null)) { + sender.sendMessage(SpigotPrison.format("&cThere aren't prestiges in the prestige ladder")); + return true; + } + + if (Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prestige-confirm-gui")).equalsIgnoreCase("true")) { + try { + SpigotConfirmPrestigeGUI gui = new SpigotConfirmPrestigeGUI(p); + gui.open(); + } catch (Exception ex) { + prestigeByChat(sender, p); + } + } else { + prestigeByChat(sender, p); + } + + } + return true; + } + + private void prestigeByChat(CommandSender sender, Player p) { + isChatEventActive = true; + sender.sendMessage(SpigotPrison.format(SpigotPrison.getGuiConfig().getString("Gui.Lore.PrestigeWarning") + SpigotPrison.getGuiConfig().getString("Gui.Lore.PrestigeWarning2") + SpigotPrison.getGuiConfig().getString("Gui.Lore.PrestigeWarning3"))); + sender.sendMessage(SpigotPrison.format("&aConfirm&3: Type the word &aconfirm &3 to confirm")); + sender.sendMessage(SpigotPrison.format("&cCancel&3: Type the word &ccancel &3to cancel, &cyou've 15 seconds!")); + Player finalP = p; + id = Bukkit.getScheduler().scheduleSyncDelayedTask(SpigotPrison.getInstance(), () -> { + isChatEventActive = false; + finalP.sendMessage(SpigotPrison.format("&cYou ran out of time, prestige cancelled.")); + }, 20L * 15); + } + + private boolean prisonmanagerPrestiges(CommandSender sender, Player p, Configuration guiConfig) { + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prestiges")).equalsIgnoreCase("true"))) { + sender.sendMessage(SpigotPrison.format("&cPrestiges are disabled by default, please edit it in your config.yml!")); + return true; + } + if (!(Objects.requireNonNull(guiConfig.getString("Options.Prestiges.GUI_Enabled")).equalsIgnoreCase("true"))) { + sender.sendMessage(SpigotPrison.format("&cSorry, but this GUI's disabled in your GuiConfig.yml")); + return true; + } + if (Objects.requireNonNull(guiConfig.getString("Options.Prestiges.Permission_GUI_Enabled")).equalsIgnoreCase("true")) { + if (!(sender.hasPermission(Objects.requireNonNull(guiConfig.getString("Options.Prestiges.Permission_GUI"))))){ + sender.sendMessage(SpigotPrison.format("&cSorry, but you're missing the permission to open this GUI [" + guiConfig.getString("Options.Prestiges.Permission_GUI") + "]")); + return true; + } + SpigotPlayerPrestigesGUI gui = new SpigotPlayerPrestigesGUI(p); + gui.open(); + } else { + SpigotPlayerPrestigesGUI gui = new SpigotPlayerPrestigesGUI(p); + gui.open(); + } + return true; + } + + private boolean prisonmanagerMines(CommandSender sender, Player p, Configuration guiConfig) { + if (!(Objects.requireNonNull(guiConfig.getString("Options.Mines.GUI_Enabled")).equalsIgnoreCase("true"))){ + sender.sendMessage(SpigotPrison.format("&cSorry, but this GUI's disabled in your GuiConfig.yml")); + return true; + } + if (Objects.requireNonNull(guiConfig.getString("Options.Mines.Permission_GUI_Enabled")).equalsIgnoreCase("true")){ + if (!(sender.hasPermission(Objects.requireNonNull(guiConfig.getString("Options.Mines.Permission_GUI"))))){ + sender.sendMessage(SpigotPrison.format("&cSorry, but you're missing the permission to open this GUI [" + guiConfig.getString("Options.Mines.Permission_GUI") + "]")); + return true; + } + SpigotPlayerMinesGUI gui = new SpigotPlayerMinesGUI(p); + gui.open(); + } else { + SpigotPlayerMinesGUI gui = new SpigotPlayerMinesGUI(p); + gui.open(); + } + return true; + } + + private boolean prisonmanagerRanks(CommandSender sender, Player p, Configuration guiConfig) { + if (!(Objects.requireNonNull(guiConfig.getString("Options.Ranks.GUI_Enabled")).equalsIgnoreCase("true"))) { + sender.sendMessage(SpigotPrison.format("&cSorry, but this GUI's disabled in your GuiConfig.yml")); + return true; + } + if (Objects.requireNonNull(guiConfig.getString("Options.Ranks.Permission_GUI_Enabled")).equalsIgnoreCase("true")) { + if (!(sender.hasPermission(Objects.requireNonNull(guiConfig.getString("Options.Ranks.Permission_GUI"))))) { + sender.sendMessage(SpigotPrison.format("&cSorry, but you're missing the permission to open this GUI [" + guiConfig.getString("Options.Ranks.Permission_GUI") + "]")); + return true; + } + SpigotPlayerRanksGUI gui = new SpigotPlayerRanksGUI(p); + gui.open(); + } else { + SpigotPlayerRanksGUI gui = new SpigotPlayerRanksGUI(p); + gui.open(); + } + return true; + } + + private boolean prisonmanagerGUI(CommandSender sender, Player p) { + if ((sender.hasPermission("prison.admin") || sender.hasPermission("prison.prisonmanagergui"))){ + SpigotPrisonGUI gui = new SpigotPrisonGUI(p); + gui.open(); + return true; + } + return false; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/BlockTestStats.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/BlockTestStats.java new file mode 100644 index 000000000..4e513c18b --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/BlockTestStats.java @@ -0,0 +1,89 @@ +package tech.mcprison.prison.spigot.compat; + +/** + *

    This class is used to store test results of block tests. + * Currently only bukkit (spigot) are using this, but could + * be extended and used by other tests. + *

    + * + *

    The field maxData was used with the intentions of capturing the + * highest magic number that could be used with a material. That magic + * number would have to be used on an ItemStack to get different variations, + * but unfortunately, this failed as a test since any value could be used + * since it appears to be the client that has the actual mapping to valid + * types. + * The magic numbers related data is not included here since there is + * no way to generate an ItemStack with an arbitrary data value and + * test that it's valid. + *

    + * + */ +public class BlockTestStats { + + private int countItems = 0; + private int countBlocks = 0; + + private int materialSize = 0; + + private short maxData = 0; + + public void addCountItems() { + countItems++; + } + public void addCountBlocks() { + countBlocks++; + } + public void addMaxData( short data ) { + if ( data > maxData ) { + maxData = data; + } + } + + @Override + public String toString() { + return "Materials = " + getMaterialSize() + + " countBlocks = " + getCountBlocks() + + " countItems = " + getCountItems(); + // The magic numbers related data is not included here since there is + // no way to generate an ItemStack with an arbitrary data value and + // test that it's valid. + // + " max data = " + getMaxData(); + } + + public int getCountItems() + { + return countItems; + } + public void setCountItems( int countItems ) + { + this.countItems = countItems; + } + + public int getCountBlocks() + { + return countBlocks; + } + public void setCountBlocks( int countBlocks ) + { + this.countBlocks = countBlocks; + } + + public int getMaterialSize() + { + return materialSize; + } + public void setMaterialSize( int materialSize ) + { + this.materialSize = materialSize; + } + + public short getMaxData() + { + return maxData; + } + public void setMaxData( short maxData ) + { + this.maxData = maxData; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Compatibility.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Compatibility.java index a095a9f57..742bdf69b 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Compatibility.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Compatibility.java @@ -19,6 +19,7 @@ package tech.mcprison.prison.spigot.compat; import org.bukkit.Location; +import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; @@ -28,16 +29,23 @@ * * @author Faizaan A. Datoo */ -public interface Compatibility { +public interface Compatibility + extends CompatibilityGUI { + + + public EquipmentSlot getHand(PlayerInteractEvent e); - EquipmentSlot getHand(PlayerInteractEvent e); + public ItemStack getItemInMainHand(PlayerInteractEvent e); - ItemStack getItemInMainHand(PlayerInteractEvent e); - - void playIronDoorSound(Location loc); + public ItemStack getItemInMainHand(Player player); + + public void breakItemInMainHand(Player player); + + public void playIronDoorSound(Location loc); enum EquipmentSlot { HAND, OFF_HAND, FEET, LEGS, CHEST, HEAD } + } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityBlocks.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityBlocks.java new file mode 100644 index 000000000..6eead633d --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityBlocks.java @@ -0,0 +1,39 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; + +import com.cryptomorin.xseries.XMaterial; + +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.util.BlockType; + +public interface CompatibilityBlocks { + + public BlockType getBlockType(Block spigotBlock); + + public PrisonBlock getPrisonBlock(Block spigotBlock); + + public XMaterial getXMaterial( Block spigotBlock ); + + public XMaterial getXMaterial( BlockType blockType ); + + public void updateSpigotBlock( BlockType blockType, Block spigotBlock ); + + public void updateSpigotBlock( PrisonBlock prisonBlock, Block spigotBlock ); + + public BlockType getBlockType( ItemStack spigotStack ); + + public void updateSpigotBlock( XMaterial xMat, Block spigotBlock ); + + + public BlockTestStats testCountAllBlockTypes(); + + + public int getDurabilityMax( ItemStack itemInHand ); + + public int getDurability( ItemStack itemInHand ); + + public void setDurability( ItemStack itemInHand, int newDurability ); + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityCache.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityCache.java new file mode 100644 index 000000000..3ddbf8f0c --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityCache.java @@ -0,0 +1,139 @@ +package tech.mcprison.prison.spigot.compat; + +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; + +import com.cryptomorin.xseries.XMaterial; + +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.util.BlockType; + +/** + *

    This function provides a way to cache BlockTypes and XMaterial + * types so as to eliminate a high cost of access after the first time + * the resource was accessed. + *

    + * + */ +public class CompatibilityCache { + + public static final byte NO_DATA_VALUE = (byte) -1; + + public static final XMaterial NULL_TOKEN = XMaterial.VOID_AIR; + + + private Map blockTypeCache; + + private Map xMaterialCache; + + public CompatibilityCache() { + super(); + + this.blockTypeCache = new TreeMap<>(); + this.xMaterialCache = new TreeMap<>(); + } + + + + + public BlockType getCachedBlockType( Block spigotBlock, byte data ) { + String key = spigotBlock.getType().name() + ( data <= 0 ? "" : ":" +data); + + BlockType blockType = blockTypeCache.get( key ); + +// return blockType == BlockType.NULL_BLOCK ? null : blockType; + return blockType; + } + public void putCachedBlockType( Block spigotBlock, byte data, BlockType blockType ) { + if ( spigotBlock != null ) { + String key = spigotBlock.getType().name() + ( data <= 0 ? "" : ":" +data); + + if ( !blockTypeCache.containsKey( key ) ) { + blockTypeCache.put( key, blockType == null ? BlockType.NULL_BLOCK : blockType ); + } + } + } + + + public BlockType getCachedBlockType( ItemStack spigotStack, byte data ) { + String key = spigotStack.getType().name() + ( data <= 0 ? "" : ":" +data); + + BlockType blockType = blockTypeCache.get( key ); + +// return blockType == BlockType.NULL_BLOCK ? null : blockType; + return blockType; + } + public void putCachedBlockType( ItemStack spigotStack, byte data, BlockType blockType ) { + if ( spigotStack != null ) { + String key = spigotStack.getType().name() + ( data <= 0 ? "" : ":" +data); + + if ( !blockTypeCache.containsKey( key ) ) { + blockTypeCache.put( key, blockType == null ? BlockType.NULL_BLOCK : blockType ); + } + } + } + + + + + public XMaterial getCachedXMaterial( PrisonBlock prisonBlock ) + { + String key = prisonBlock.getBlockName(); + + XMaterial xMat = xMaterialCache.get( key ); + + // Using VOID_AIR as temp placeholder for null values: +// return xMat == XMaterial.VOID_AIR ? null : xMat; + return xMat; + } + + public void putCachedXMaterial( PrisonBlock prisonBlock, XMaterial xMat ) + { + String key = prisonBlock.getBlockName(); + + if ( !xMaterialCache.containsKey( key ) ) { + // Using VOID_AIR as temp placeholder for null values: + xMaterialCache.put( key, xMat == null ? XMaterial.VOID_AIR : xMat ); + } + } + + public XMaterial getCachedXMaterial( Block spigotBlock, byte data ) { + String key = spigotBlock.getType().name() + ( data <= 0 ? "" : ":" +data); + + XMaterial xMat = xMaterialCache.get( key ); + + // Using VOID_AIR as temp placeholder for null values: +// return xMat == XMaterial.VOID_AIR ? null : xMat; + return xMat; + } + public void putCachedXMaterial( Block spigotBlock, byte data, XMaterial xMat ) { + String key = spigotBlock.getType().name() + ( data <= 0 ? "" : ":" +data); + + if ( !xMaterialCache.containsKey( key ) ) { + // Using VOID_AIR as temp placeholder for null values: + xMaterialCache.put( key, xMat == null ? XMaterial.VOID_AIR : xMat ); + } + } + + public XMaterial getCachedXMaterial( BlockType blockType, byte data ) { + String key = blockType.name() + ( data <= 0 ? "" : ":" +data); + + XMaterial xMat = xMaterialCache.get( key ); + + // Using VOID_AIR as temp placeholder for null values: +// return xMat == XMaterial.VOID_AIR ? null : xMat; + return xMat; + } + public void putCachedXMaterial( BlockType blockType, byte data, XMaterial xMat ) { + String key = blockType.name() + ( data <= 0 ? "" : ":" +data); + + if ( !xMaterialCache.containsKey( key ) ) { + // Using VOID_AIR as temp placeholder for null values: + xMaterialCache.put( key, xMat == null ? XMaterial.VOID_AIR : xMat ); + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityGUI.java new file mode 100644 index 000000000..b3086359a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/CompatibilityGUI.java @@ -0,0 +1,8 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.event.inventory.InventoryEvent; + +public interface CompatibilityGUI extends CompatibilityBlocks { + + public String getGUITitle(InventoryEvent e); +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113.java new file mode 100644 index 000000000..d1372fa18 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113.java @@ -0,0 +1,45 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.Effect; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +public class Spigot113 + extends Spigot113GUI + implements Compatibility { + + @Override + public EquipmentSlot getHand(PlayerInteractEvent e) { + if (e.getHand() == null) { + return null; + } else { + return EquipmentSlot.valueOf(e.getHand().name()); + } + } + + @Override + public ItemStack getItemInMainHand(PlayerInteractEvent e) { + return e.getPlayer().getInventory().getItemInMainHand(); + } + + @Override + public ItemStack getItemInMainHand(Player player) { + return player.getInventory().getItemInMainHand(); + } + + @Override + public void playIronDoorSound(Location loc) { + loc.getWorld().playEffect(loc, Effect.IRON_DOOR_TOGGLE, null); + } + + + @Override + public void breakItemInMainHand( Player player ) { + player.getInventory().setItemInMainHand( null ); + + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 0.85F); + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113Blocks.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113Blocks.java new file mode 100644 index 000000000..973b80a06 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113Blocks.java @@ -0,0 +1,233 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; + +import com.cryptomorin.xseries.XMaterial; + +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.internal.block.PrisonBlockTypes.InternalBlockTypes; +import tech.mcprison.prison.util.BlockType; + +public abstract class Spigot113Blocks + extends CompatibilityCache + implements CompatibilityBlocks { + + public BlockType getBlockType(Block spigotBlock) { + BlockType results = getCachedBlockType( spigotBlock, NO_DATA_VALUE ); + + if ( results == null ) { + if ( spigotBlock != null ) { + + results = BlockType.getBlock( spigotBlock.getType().name() ); + + putCachedBlockType( spigotBlock, NO_DATA_VALUE, results ); + } + } + + return results == BlockType.NULL_BLOCK ? null : results; + } + + public PrisonBlock getPrisonBlock(Block spigotBlock) { + PrisonBlock pBlock = null; + + XMaterial xMat = getXMaterial( spigotBlock ); + + if ( xMat != null ) { + pBlock = new PrisonBlock( xMat.name() ); + } + + return pBlock; + } + + public BlockType getBlockType(ItemStack spigotStack) { + BlockType results = getCachedBlockType( spigotStack, NO_DATA_VALUE ); + + if ( results == null ) { + if ( spigotStack != null ) { + + results = BlockType.getBlock( spigotStack.getType().name() ); + + putCachedBlockType( spigotStack, NO_DATA_VALUE, results ); + } + } + + return results == BlockType.NULL_BLOCK ? null : results; + } + + public XMaterial getXMaterial( Block spigotBlock ) { + XMaterial results = getCachedXMaterial( spigotBlock, NO_DATA_VALUE ); + + if ( results == null ) { + if ( spigotBlock != null ) { + results = XMaterial.matchXMaterial( spigotBlock.getType().name() ).orElse( null ); + + putCachedXMaterial( spigotBlock, NO_DATA_VALUE, results ); + } + } + + return results == NULL_TOKEN ? null : results; + } + + + public XMaterial getXMaterial( PrisonBlock prisonBlock ) { + XMaterial results = null; + + if ( prisonBlock != null ) { + + results = getCachedXMaterial( prisonBlock ); + if ( results == null ) { + + String blockName = prisonBlock.getBlockName(); + + results = XMaterial.matchXMaterial( blockName ).orElse( null ); + + putCachedXMaterial( prisonBlock, results ); + } + + } + + return results == NULL_TOKEN ? null : results; + } + + + /** + *

    This function tries to use up to three different sources to get a match + * on the XMaterial. Just because the XMateral may be a match, does not + * mean it actually is a valid Block for that version of spigot. + *

    + * + * @param blockType + * @return + */ + public XMaterial getXMaterial( BlockType blockType ) { + XMaterial results = getCachedXMaterial( blockType, NO_DATA_VALUE ); + + if ( results == null ) { + if ( blockType != null && blockType != BlockType.IGNORE ) { + + results = XMaterial.matchXMaterial( blockType.getXMaterialName() ).orElse( null ); + + if ( results == null ) { + results = XMaterial.matchXMaterial( blockType.getXMaterialNameLegacy() ).orElse( null ); + + } + + if ( results == null ) { + + for ( String altName : blockType.getXMaterialAltNames() ) { + results = XMaterial.matchXMaterial( altName ).orElse( null ); + + if ( results != null ) { + break; + } + } + } + + putCachedXMaterial( blockType, NO_DATA_VALUE, results ); + } + + } + + return results == NULL_TOKEN ? null : results; + } + + + public void updateSpigotBlock( BlockType blockType, Block spigotBlock ) { + + if ( blockType != null && blockType != BlockType.IGNORE && spigotBlock != null ) { + + XMaterial xMat = getXMaterial( blockType ); + + updateSpigotBlock( xMat, spigotBlock ); + } + } + + + public void updateSpigotBlock( PrisonBlock prisonBlock, Block spigotBlock ) { + + if ( prisonBlock != null && + !prisonBlock.getBlockName().equalsIgnoreCase( InternalBlockTypes.IGNORE.name() ) && + spigotBlock != null ) { + + XMaterial xMat = getXMaterial( prisonBlock ); + + if ( xMat != null ) { + + updateSpigotBlock( xMat, spigotBlock ); + } + } + } + + + public void updateSpigotBlock( XMaterial xMat, Block spigotBlock ) { + + if ( xMat != null ) { + Material newType = xMat.parseMaterial(); + if ( newType != null ) { + // No physics update: + spigotBlock.setType( newType, false ); + + } + } + } + + /** + *

    This function is supposed to find all possible blocks available + * on the server. The number of available items, and blocks, will vary based + * upon different version the server is running. + *

    + * + *

    This function is not simple for 1.8 mode of block types. The primary + * reason for this is that there is a list of materials that is available, + * but depending upon what magic numbers (data) you set on the ItemStacks, + * you will be different blocks or items. Like the difference between oak + * logs and birch logs. + *

    + * + * @return + */ + public BlockTestStats testCountAllBlockTypes() { + BlockTestStats stats = new BlockTestStats(); + + stats.setMaterialSize( Material.values().length ); + + // go through all available materials: + for ( Material mat : Material.values() ) { + + // Must create an item stack: + ItemStack iStack = new ItemStack( mat, 1 ); + + if ( iStack != null ) { + + if ( mat.isBlock() ) { + stats.addCountBlocks(); + } + else if ( mat.isItem() ) { + stats.addCountItems(); + } + } + } + + return stats; + } + + + public int getDurabilityMax( ItemStack itemInHand ) { + return itemInHand.getType().getMaxDurability(); + } + + public int getDurability( ItemStack itemInHand ) { + + Damageable damage = (Damageable) itemInHand.getItemMeta(); + return damage.getDamage(); + } + + public void setDurability( ItemStack itemInHand, int newDamage ) { + + Damageable damage = (Damageable) itemInHand.getItemMeta(); + damage.setDamage( newDamage ); + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113GUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113GUI.java new file mode 100644 index 000000000..62acc09b9 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot113GUI.java @@ -0,0 +1,14 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.event.inventory.InventoryEvent; + +public abstract class Spigot113GUI + extends Spigot113Blocks + implements CompatibilityGUI { + + @Override + public String getGUITitle(InventoryEvent e){ + return e.getView().getTitle(); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18.java index a232637c2..1fcae90ce 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18.java @@ -20,25 +20,48 @@ import org.bukkit.Effect; import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; /** * @author Faizaan A. Datoo */ -public class Spigot18 implements Compatibility { +public class Spigot18 + extends Spigot18GUI + implements Compatibility { - @Override public EquipmentSlot getHand(PlayerInteractEvent e) { + + + @Override + public EquipmentSlot getHand(PlayerInteractEvent e) { return EquipmentSlot.HAND; // Spigot 1.8 only has one hand } @SuppressWarnings( "deprecation" ) - @Override public ItemStack getItemInMainHand(PlayerInteractEvent e) { + @Override + public ItemStack getItemInMainHand(PlayerInteractEvent e) { return e.getPlayer().getItemInHand(); } - @Override public void playIronDoorSound(Location loc) { + @SuppressWarnings( "deprecation" ) + @Override + public ItemStack getItemInMainHand(Player player ) { + return player.getItemInHand(); + } + + @Override + public void playIronDoorSound(Location loc) { loc.getWorld().playEffect(loc, Effect.DOOR_TOGGLE, null); } + @SuppressWarnings( "deprecation" ) + @Override + public void breakItemInMainHand( Player player ) { + player.setItemInHand( null ); + + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 0.85F); + } + } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18Blocks.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18Blocks.java new file mode 100644 index 000000000..b4f48703d --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18Blocks.java @@ -0,0 +1,363 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.inventory.ItemStack; + +import com.cryptomorin.xseries.XMaterial; + +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.internal.block.PrisonBlockTypes.InternalBlockTypes; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.util.BlockType; + +public abstract class Spigot18Blocks + extends CompatibilityCache + implements CompatibilityBlocks { + + + /** + *

    This function provides a minecraft v1.8 way of getting + * the prison BlockType from a bukkit Block. This function + * should be used for all block types prior to 1.13.x because + * of the use of magic numbers. The variations of types for a + * base material cannot be accessed in any other way. + * For example the item lapis lazuli, which is not a block type, + * but it is one of the primary problem item. + *

    + * + *

    For versions 1.13.x and higher, a different function would + * need to be used to get the BlockType. + *

    + * + * @param spigotBlock + * @return + */ + @SuppressWarnings( "deprecation" ) + public BlockType getBlockType(Block spigotBlock) { + BlockType results = null; + + if ( spigotBlock != null ) { + + int id = spigotBlock.getType().getId(); + short data = spigotBlock.getData(); + + results = getCachedBlockType( spigotBlock, (byte) data ); + if ( results == null ) { + + // NOTE: namespace is 1.13+ +// Output.get().logInfo( "### getBlockType: " + spigotBlock.getType().name() + " " + +// spigotBlock.getType().getKey().getKey() + " " + +// spigotBlock.getType().getKey().getNamespace() ); + + results = BlockType.getBlock(id, data); + + if ( results == null ) { + Output.get().logWarn( "Spigot1.8Blocks.getBlockType() : " + + "Spigot block cannot be mapped to a prison BlockType : " + + spigotBlock.getType().name() + + " id = " + id + " data = " + data + + " BlockType = " + ( results == null ? "null" : results.name())); + + } + + putCachedBlockType( spigotBlock, (byte) data, results ); + } + } + + return results == BlockType.NULL_BLOCK ? null : results; + } + + + public PrisonBlock getPrisonBlock(Block spigotBlock) { + PrisonBlock pBlock = null; + + XMaterial xMat = getXMaterial( spigotBlock ); + + if ( xMat != null ) { + pBlock = new PrisonBlock( xMat.name() ); + } + + return pBlock; + } + + + @SuppressWarnings( "deprecation" ) + public BlockType getBlockType( ItemStack spigotStack ) { + BlockType results = null; + + if ( spigotStack != null ) { + + int id = spigotStack.getType().getId(); + short data = spigotStack.getData().getData(); + + results = getCachedBlockType( spigotStack, (byte) data ); + if ( results == null ) { + results = BlockType.getBlock(id, data); + + putCachedBlockType( spigotStack, (byte) data, results ); + } + } + + return results == BlockType.NULL_BLOCK ? null : results; + } + + @SuppressWarnings( "deprecation" ) + public XMaterial getXMaterial( Block spigotBlock ) { + XMaterial results = null; + + if ( spigotBlock != null ) { +// int id = spigotBlock.getType().getId(); + short data = spigotBlock.getData(); + + results = getCachedXMaterial( spigotBlock, (byte) data ); + if ( results == null ) { + + String blockName = spigotBlock.getType().name() + + ( data > 0 ? ":" + data : "" ); + + results = XMaterial.matchXMaterial( blockName ).orElse( null ); + + putCachedXMaterial( spigotBlock, (byte) data, results ); + } + + } + + return results == NULL_TOKEN ? null : results; + } + + public XMaterial getXMaterial( PrisonBlock prisonBlock ) { + XMaterial results = null; + + if ( prisonBlock != null ) { + + results = getCachedXMaterial( prisonBlock ); + if ( results == null ) { + + String blockName = prisonBlock.getBlockName(); + + results = XMaterial.matchXMaterial( blockName ).orElse( null ); + + putCachedXMaterial( prisonBlock, results ); + } + + } + + return results == NULL_TOKEN ? null : results; + } + + + + public XMaterial getXMaterial( BlockType blockType ) { + XMaterial results = null; + + if ( blockType != null && blockType != BlockType.IGNORE ) { + short data = blockType.getData(); + + results = getCachedXMaterial( blockType, (byte) data ); + if ( results == null ) { + + // First match by BlockType name: + results = XMaterial.matchXMaterial( blockType.getXMaterialName() ).orElse( null ); + + // do not use... redundant with blockType.getXMaterialName(): +// results = XMaterial.matchXMaterial( blockType.name() ).orElse( null ); + + if ( results == null ) { + + // Try to match on altNames if they exist: + for ( String altName : blockType.getXMaterialAltNames() ) { + + results = XMaterial.matchXMaterial( altName ).orElse( null ); + + if ( results != null ) { + break; + } + } + + if ( results == null ) { + + // Finally, Try to match on legacy name and magic number: + results = XMaterial.matchXMaterial( blockType.getXMaterialNameLegacy() ).orElse( null ); + } + + putCachedXMaterial( blockType, (byte) data, results ); + } + + } + + } + + return results == NULL_TOKEN ? null : results; + } + + +// public Material getMaterial( BlockType blockType ) { +// Material results = null; +// +// if ( blockType != null && blockType != BlockType.IGNORE ) { +// short data = blockType.getData(); +// +//// Material.bush +//// +//// Material.matchMaterial( name, legacyName ) +//// +//// results = getCachedXMaterial( blockType, (byte) data ); +//// if ( results == null ) { +//// +//// results = XMaterial.matchXMaterial( blockType.getXMaterialNameLegacy() ).orElse( null ); +//// +//// if ( results == null ) { +//// for ( String altName : blockType.getXMaterialAltNames() ) { +//// +//// results = XMaterial.matchXMaterial( altName ).orElse( null ); +//// +//// if ( results != null ) { +//// break; +//// } +//// } +//// } +//// +//// putCachedXMaterial( blockType, (byte) data, results ); +//// } +// +// } +// +// return results == NULL_TOKEN ? null : results; +// } + + + + public void updateSpigotBlock( BlockType blockType, Block spigotBlock ) { + + if ( blockType != null && blockType != BlockType.IGNORE && spigotBlock != null ) { + + XMaterial xMat = getXMaterial( blockType ); + + if ( xMat != null ) { + + updateSpigotBlock( xMat, spigotBlock ); + } + } + } + + + public void updateSpigotBlock( PrisonBlock prisonBlock, Block spigotBlock ) { + + if ( prisonBlock != null && + !prisonBlock.getBlockName().equalsIgnoreCase( InternalBlockTypes.IGNORE.name() ) && + spigotBlock != null ) { + + XMaterial xMat = getXMaterial( prisonBlock ); + + if ( xMat != null ) { + + updateSpigotBlock( xMat, spigotBlock ); + } + } + } + + @SuppressWarnings( "deprecation" ) + public void updateSpigotBlock( XMaterial xMat, Block spigotBlock ) { + + if ( xMat != null ) { + Material newType = xMat.parseMaterial(); + if ( newType != null ) { + + BlockState bState = spigotBlock.getState(); + + // Set the block state with the new type and rawData: + bState.setType( newType );; + bState.setRawData( xMat.getData() ); + + // Force the update but don't apply the physics: + bState.update( true, false ); + + } + } + } + + + + /** + *

    This function is supposed to find all possible blocks available + * on the server. The number of available items, and blocks, will vary based + * upon different version the server is running. + *

    + * + *

    This function is not simple for 1.8 mode of block types. The primary + * reason for this is that there is a list of materials that is available, + * but depending upon what magic numbers (data) you set on the ItemStacks, + * you will be different blocks or items. Like the difference between oak + * logs and birch logs. + *

    + * + *

    One problem is that the data can be set to any value and it + * will be valid. It may not render in game, but we cannot test for that. + * So in turn, we cannot use the magic numbers to check to see if they + * are actually valid. As a result, all we can do is use the Materials + * directly, and as a result, we will miss out of the variations that + * may otherwise exist. For example with minecraft:log and minecraft:log2 + * where we can only count two for those two materials, but they actually + * have about 6 to 8 declared types, or more (I've seen naturally spawned + * log with a value of 7 and 8 which does not exist in the documentation). + *

    + * + *

    As a result of preventing arbitrarily high false reporting of + * block types, all magic number related code is commented out. + *

    + * + * @return + */ + public BlockTestStats testCountAllBlockTypes() { + BlockTestStats stats = new BlockTestStats(); + + stats.setMaterialSize( Material.values().length ); + + // go through all available materials: + for ( Material mat : Material.values() ) { + + // Need to try all data values from 0 through 20: +// for ( short data = 0; data < (short) 20; data++ ) { + + // Must create an item stack: + ItemStack iStack = new ItemStack( mat, 1 ); +// ItemStack iStack = new ItemStack( mat, 1, data ); + + if ( iStack != null ) { + +// stats.addMaxData( data ); + + if ( mat.isBlock() ) { + stats.addCountBlocks(); + } +// if ( mat.isItem() ) // isItem() is not a function in v1.8.8 + else { + stats.addCountItems(); + } + } +// } + } + + return stats; + } + + + + public int getDurabilityMax( ItemStack itemInHand ) { + return itemInHand.getType().getMaxDurability(); + } + + @SuppressWarnings( "deprecation" ) + public int getDurability( ItemStack itemInHand ) { + return itemInHand.getDurability(); + } + + @SuppressWarnings( "deprecation" ) + public void setDurability( ItemStack itemInHand, int newDurability ) { + itemInHand.setDurability( (short) newDurability ); + } + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18GUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18GUI.java new file mode 100644 index 000000000..7a30037c2 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot18GUI.java @@ -0,0 +1,15 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.event.inventory.InventoryEvent; + +public abstract class Spigot18GUI + extends Spigot18Blocks + implements CompatibilityGUI { + + @SuppressWarnings( "deprecation" ) + @Override + public String getGUITitle(InventoryEvent e){ + return e.getInventory().getTitle(); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19.java index 6e55dbb9f..707d3359e 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19.java @@ -20,6 +20,8 @@ import org.bukkit.Effect; import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; @@ -28,9 +30,12 @@ * * @author Faizaan A. Datoo */ -public class Spigot19 implements Compatibility { +public class Spigot19 + extends Spigot19GUI + implements Compatibility { - @Override public EquipmentSlot getHand(PlayerInteractEvent e) { + @Override + public EquipmentSlot getHand(PlayerInteractEvent e) { if (e.getHand() == null) { return null; } else { @@ -38,12 +43,27 @@ public class Spigot19 implements Compatibility { } } - @Override public ItemStack getItemInMainHand(PlayerInteractEvent e) { + @Override + public ItemStack getItemInMainHand(PlayerInteractEvent e) { return e.getPlayer().getInventory().getItemInMainHand(); } - @Override public void playIronDoorSound(Location loc) { + @Override + public ItemStack getItemInMainHand(Player player) { + return player.getInventory().getItemInMainHand(); + } + + @Override + public void playIronDoorSound(Location loc) { loc.getWorld().playEffect(loc, Effect.IRON_DOOR_TOGGLE, null); } + + + @Override + public void breakItemInMainHand( Player player ) { + player.getInventory().setItemInMainHand( null ); + + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 0.85F); + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19GUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19GUI.java new file mode 100644 index 000000000..e62efc4f7 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/compat/Spigot19GUI.java @@ -0,0 +1,13 @@ +package tech.mcprison.prison.spigot.compat; + +import org.bukkit.event.inventory.InventoryEvent; + +public class Spigot19GUI + extends Spigot18Blocks + implements CompatibilityGUI { + + @Override + public String getGUITitle(InventoryEvent e){ + return e.getView().getTitle(); + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssEconomyWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssEconomyWrapper.java index 0bcef9fd0..ff19a4a2f 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssEconomyWrapper.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssEconomyWrapper.java @@ -1,11 +1,12 @@ package tech.mcprison.prison.spigot.economies; +import java.math.BigDecimal; + import com.earth2me.essentials.api.Economy; import com.earth2me.essentials.api.NoLoanPermittedException; import com.earth2me.essentials.api.UserDoesNotExistException; -import tech.mcprison.prison.internal.Player; -import java.math.BigDecimal; +import tech.mcprison.prison.internal.Player; /** * A non-static wrapper of Essentials' annoyingly static {@link net.ess3.api.Economy} API. diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssentialsEconomy.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssentialsEconomy.java index 39f4576cd..87ad28755 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssentialsEconomy.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/EssentialsEconomy.java @@ -18,51 +18,127 @@ package tech.mcprison.prison.spigot.economies; -import org.bukkit.Bukkit; +import java.math.MathContext; + +import com.earth2me.essentials.api.Economy; + +import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.integration.EconomyIntegration; +import tech.mcprison.prison.integration.IntegrationType; import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.output.Output; /** * Integrates with Essentials Economy. * * @author Faizaan A. Datoo */ -public class EssentialsEconomy implements EconomyIntegration { +public class EssentialsEconomy + extends EconomyIntegration { - private EssEconomyWrapper wrapper = null; + private EssEconomyWrapper wrapper = null; + private boolean availableAsAnAlternative = false; public EssentialsEconomy() { - if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) { - wrapper = new EssEconomyWrapper(); - } + super( "EssentialsX", "Essentials" ); + } + + /** + *

    This is tied to the plugin name Essentials, which is broadly covering many + * possible behaviors, but we are interested in if Essentials Economy is being + * used. The easiest way to ensure it is active, is to try to access the + * Economy singleton, if it throws a NoClassDefFoundError then it has not + * been loaded. + *

    + * + *

    Had inconsistent luck with using a classloader to check to see if the + * Economy class has been loaded, since there could be many different instances + * of class loaders, and you have to use the right now. The default that was + * being tried was the System class loader, which was not successful at all. + * To pick and chose one would be a crap-shot at best (gambling), so the + * safest way is to just try and use it. Plain and simple, without extra overhead + * of hunting down the correct classloader. + *

    + */ + @Override + public void integrate() { + addDebugInfo( "1" ); + if ( isRegistered() // && classLoaded + ) { + try { + addDebugInfo( "2" ); + + // if an econ is already registered, then don't register this one: + boolean econAlreadySet = PrisonAPI.getIntegrationManager().getForType( IntegrationType.ECONOMY ).isPresent(); + addDebugInfo( "3:econAlreadySet=" + econAlreadySet ); + + // This is a trigger to cause the NoClassDefFoundError. This is to see if the + // the essentials economy plugin is active or not: + @SuppressWarnings( "unused" ) + MathContext mathCtx = Economy.MATH_CONTEXT; + + if ( !econAlreadySet ) { + addDebugInfo( "4" ); + this.wrapper = new EssEconomyWrapper(); + addDebugInfo( "5" ); + } else { + addDebugInfo( "6" ); + Output.get().logInfo( "EssentialsEconomy is not directly enabled - Available as backup. " ); + this.availableAsAnAlternative = true; + addDebugInfo( "7" ); + } + } + catch ( java.lang.NoClassDefFoundError | Exception e ) + { + addDebugInfo( "8:Exception:" + e.getMessage() ); + // Do not print any errors since since all we need to know is EssentialsX Econ is not active + //e.printStackTrace(); + } + } + addDebugInfo( "9" ); + } - @Override public double getBalance(Player player) { + @Override + public double getBalance(Player player) { return wrapper.getBalance(player); } - @Override public void setBalance(Player player, double amount) { + @Override + public void setBalance(Player player, double amount) { wrapper.setBalance(player, amount); } - @Override public void addBalance(Player player, double amount) { + @Override + public void addBalance(Player player, double amount) { setBalance(player, getBalance(player) + amount); } - @Override public void removeBalance(Player player, double amount) { + @Override + public void removeBalance(Player player, double amount) { setBalance(player, getBalance(player) - amount); } - @Override public boolean canAfford(Player player, double amount) { + @Override + public boolean canAfford(Player player, double amount) { return getBalance(player) >= amount; } - - @Override public String getProviderName() { - return "Essentials"; + + @Override + public boolean hasIntegrated() { + return wrapper != null; } - @Override public boolean hasIntegrated() { - return wrapper != null; + @Override + public String getDisplayName() + { + return super.getDisplayName() + + ( availableAsAnAlternative ? " (disabled)" : ""); } + + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/essentialsx.9089/"; + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomy.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomy.java new file mode 100644 index 000000000..6f63a829a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomy.java @@ -0,0 +1,158 @@ +package tech.mcprison.prison.spigot.economies; + +import tech.mcprison.prison.integration.EconomyCurrencyIntegration; +import tech.mcprison.prison.internal.Player; + +public class GemsEconomy + extends EconomyCurrencyIntegration +{ + + private GemsEconomyWrapper wrapper = null; + private boolean availableAsAnAlternative = false; + + public GemsEconomy() { + super( "GemsEconomy", "GemsEconomy" ); + } + + /** + *

    If GemsEconomy is registered, always hook this up because this may be needed + * to be used instead of the primary economy if they are using a custom currency. + *

    + */ + @Override + public void integrate() { + addDebugInfo( "1" ); + if ( isRegistered()) { + addDebugInfo( "2" ); + try { + addDebugInfo( "3" ); + this.wrapper = new GemsEconomyWrapper(); + addDebugInfo( "4" ); + } + catch ( java.lang.NoClassDefFoundError | Exception e ) { + addDebugInfo( "5:Exception:" + e.getMessage() ); + e.printStackTrace(); + } + } + addDebugInfo( "6" ); + } + + + @Override + public boolean supportedCurrency( String currencyName ) { + boolean supported = false; + + if ( wrapper != null ) { + supported = wrapper.supportedCurrency( currencyName ); + } + + return supported; + } + + @Override + public double getBalance(Player player) { + double amount = 0; + if ( wrapper != null ) { + amount = wrapper.getBalance(player); + } + return amount; + } + + @Override + public double getBalance(Player player, String currencyName) { + double amount = 0; + if ( wrapper != null ) { + amount = wrapper.getBalance(player, currencyName); + } + return amount; + } + + @Override + public void setBalance(Player player, double amount) { + if ( wrapper != null ) { + double remainder = amount - getBalance(player); + if ( remainder > 0 ) { + wrapper.addBalance( player, amount ); + } if ( remainder < 0 ) { + wrapper.withdraw( player, amount ); + } + } + } + + @Override + public void setBalance(Player player, double amount, String currencyName) { + if ( wrapper != null ) { + double remainder = amount - getBalance(player, currencyName); + if ( remainder > 0 ) { + wrapper.addBalance( player, amount, currencyName ); + } if ( remainder < 0 ) { + wrapper.withdraw( player, amount, currencyName ); + } + } + } + + @Override + public void addBalance(Player player, double amount) { + if ( wrapper != null ) { + wrapper.addBalance(player, amount); + } + } + + @Override + public void addBalance(Player player, double amount, String currencyName) { + if ( wrapper != null ) { + wrapper.addBalance(player, amount, currencyName); + } + } + + @Override + public void removeBalance(Player player, double amount) { + if ( wrapper != null ) { + wrapper.withdraw(player, amount); + } + } + + @Override + public void removeBalance(Player player, double amount, String currencyName) { + if ( wrapper != null ) { + wrapper.withdraw(player, amount, currencyName); + } + } + + @Override + public boolean canAfford(Player player, double amount) { + boolean results = false; + if ( wrapper != null ) { + results = getBalance(player) >= amount; + } + return results; + } + + @Override + public boolean canAfford(Player player, double amount, String currencyName) { + boolean results = false; + if ( wrapper != null ) { + results = getBalance(player, currencyName) >= amount; + } + return results; + } + + @Override + public boolean hasIntegrated() { + return wrapper != null; + } + + @Override + public String getDisplayName() + { + return super.getDisplayName() + + ( availableAsAnAlternative ? " (disabled)" : ""); + } + + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/gemseconomy.19655/"; + } + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomyWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomyWrapper.java new file mode 100644 index 000000000..700d745f7 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/GemsEconomyWrapper.java @@ -0,0 +1,97 @@ +package tech.mcprison.prison.spigot.economies; + +import me.xanium.gemseconomy.api.GemsEconomyAPI; +import me.xanium.gemseconomy.economy.Currency; +import tech.mcprison.prison.internal.Player; + +/** + * + * + * https://gitlab.com/xanium-s-spigot-plugins/GemsEconomy/-/blob/master/src/main/java/me/xanium/gemseconomy/api/GemsEconomyAPI.java + * + * + */ +public class GemsEconomyWrapper +{ + private GemsEconomyAPI economy; + + public GemsEconomyWrapper() { + super(); + + + this.economy = new GemsEconomyAPI(); + } + + + public boolean isEnabled() + { + return economy != null; + } + + public boolean supportedCurrency( String currencyName ) { + Currency currency = getCurrency( currencyName ); + + boolean supported = (currency != null); + + return supported; + } + + public Currency getCurrency( String currencyName ) { + Currency currency = null; + if (economy != null && + currencyName != null && currencyName.trim().length() > 0) { + currency = economy.getCurrency( currencyName ); + } + return currency; + } + + public double getBalance(Player player) { + return getBalance(player, null); + } + + public double getBalance(Player player, String currencyName) { + double results = 0; + if (economy != null) { + Currency currency = getCurrency( currencyName ); + if ( currency == null ) { + results = economy.getBalance(player.getUUID()); + } else { + results = economy.getBalance(player.getUUID(), currency); + } + } + return results; + } + + + public void addBalance(Player player, double amount) { + addBalance(player, amount, null); + } + + public void addBalance(Player player, double amount, String currencyName) { + if (economy != null) { + Currency currency = getCurrency( currencyName ); + if ( currency == null ) { + economy.deposit(player.getUUID(), amount); + } else { + economy.deposit(player.getUUID(), amount, currency); + } + } + } + + + public void withdraw(Player player, double amount) { + withdraw(player, amount, null); + } + + public void withdraw(Player player, double amount, String currencyName) { + if (economy != null) { + Currency currency = getCurrency( currencyName ); + if ( currency == null ) { + economy.withdraw(player.getUUID(), amount); + } else { + economy.withdraw(player.getUUID(), amount, currency); + } + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomy.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomy.java index 42fc47047..010892d42 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomy.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomy.java @@ -1,58 +1,80 @@ package tech.mcprison.prison.spigot.economies; -import org.appledash.saneeconomy.economy.EconomyManager; -import org.appledash.saneeconomy.economy.economable.EconomablePlayer; -import org.bukkit.Bukkit; +import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.integration.EconomyIntegration; +import tech.mcprison.prison.integration.IntegrationType; import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.output.Output; /** * @author Faizaan A. Datoo */ -public class SaneEconomy implements EconomyIntegration { - - private EconomyManager economyManager; +public class SaneEconomy + extends EconomyIntegration { + private SaneEconomyWrapper econWrapper; + private boolean availableAsAnAlternative = false; + public SaneEconomy() { - org.appledash.saneeconomy.SaneEconomy saneEconomy = (org.appledash.saneeconomy.SaneEconomy) Bukkit - .getServer().getPluginManager().getPlugin("SaneEconomy"); - if(saneEconomy == null) { - return; - } - - economyManager = saneEconomy.getEconomyManager(); + super( "SaneEconomy", "SaneEconomy" ); } + + @Override + public void integrate() { + if (isRegistered()) { + + // if an econ is already registered, then don't register this one: + boolean econAlreadySet = PrisonAPI.getIntegrationManager().getForType( IntegrationType.ECONOMY ).isPresent(); + + if ( !econAlreadySet ) { + this.econWrapper = new SaneEconomyWrapper(getProviderName()); + } else { + Output.get().logInfo( "SaneEconomy is not directly enabled - Available as backup. " ); + this.availableAsAnAlternative = true; + } - @Override public double getBalance(Player player) { - return economyManager.getBalance(toEconomablePlayer(player)); + } + } + + @Override + public double getBalance(Player player) { + return econWrapper.getBalance(player); } - @Override public void setBalance(Player player, double amount) { - economyManager.setBalance(toEconomablePlayer(player), amount); + @Override + public void setBalance(Player player, double amount) { + econWrapper.setBalance(player, amount); } - @Override public void addBalance(Player player, double amount) { + @Override + public void addBalance(Player player, double amount) { setBalance(player, getBalance(player) + amount); } - @Override public void removeBalance(Player player, double amount) { + @Override + public void removeBalance(Player player, double amount) { setBalance(player, getBalance(player) - amount); } - @Override public boolean canAfford(Player player, double amount) { + @Override + public boolean canAfford(Player player, double amount) { return getBalance(player) >= amount; } - - private EconomablePlayer toEconomablePlayer(Player player) { - return new EconomablePlayer(Bukkit.getOfflinePlayer(player.getUUID())); - } - - @Override public String getProviderName() { - return "SaneEconomy"; - } - - @Override public boolean hasIntegrated() { + + @Override + public boolean hasIntegrated() { return false; } + @Override + public String getDisplayName() + { + return super.getDisplayName() + " (API v0.15.0)"+ + ( availableAsAnAlternative ? " (disabled)" : ""); + } + + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/saneeconomy-simple-but-featureful-economy.26223/"; + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomyWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomyWrapper.java new file mode 100644 index 000000000..cee34711a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/SaneEconomyWrapper.java @@ -0,0 +1,53 @@ +package tech.mcprison.prison.spigot.economies; + +import org.appledash.saneeconomy.economy.EconomyManager; +import org.appledash.saneeconomy.economy.economable.EconomablePlayer; +import org.bukkit.Bukkit; + +import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.output.Output; + +public class SaneEconomyWrapper +{ + private EconomyManager economyManager; + + public SaneEconomyWrapper(String providerName) { + super(); + + org.appledash.saneeconomy.SaneEconomy saneEconomy = + (org.appledash.saneeconomy.SaneEconomy) + Bukkit.getServer().getPluginManager().getPlugin(providerName); + + if(saneEconomy != null) { + this.economyManager = saneEconomy.getEconomyManager(); + } + } + + public double getBalance(Player player) { + double result = 0; + + try { + result = economyManager.getBalance(toEconomablePlayer(player)); + } + catch ( Exception e ) { + Output.get().logError( "Failed to get SaneEconomy balance. " + + "Using API v0.15.0. You may need to downgrade. ", e ); + } + + return result; + } + + public void setBalance(Player player, double amount) { + try { + economyManager.setBalance(toEconomablePlayer(player), amount); + } + catch ( Exception e ) { + Output.get().logError( "Failed to set SaneEconomy balance. " + + "Using API v0.15.0. You may need to downgrade. ", e ); + } + } + + private EconomablePlayer toEconomablePlayer(Player player) { + return new EconomablePlayer(Bukkit.getOfflinePlayer(player.getUUID())); + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomy.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomy.java index f1aba2878..590bc2321 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomy.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomy.java @@ -18,67 +18,93 @@ package tech.mcprison.prison.spigot.economies; -import org.bukkit.Bukkit; -import org.bukkit.plugin.RegisteredServiceProvider; import tech.mcprison.prison.integration.EconomyIntegration; import tech.mcprison.prison.internal.Player; /** * @author Faizaan A. Datoo */ -public class VaultEconomy implements EconomyIntegration { +public class VaultEconomy + extends EconomyIntegration { - private net.milkbowl.vault.economy.Economy economy = null; + private VaultEconomyWrapper econWrapper; public VaultEconomy() { - RegisteredServiceProvider economyProvider = - Bukkit.getServer().getServicesManager() - .getRegistration(net.milkbowl.vault.economy.Economy.class); - if (economyProvider != null) { - economy = economyProvider.getProvider(); - } + super( "VaultEcon", "Vault" ); } - @SuppressWarnings( "deprecation" ) - @Override public double getBalance(Player player) { - if (economy == null) { - return 0; + @Override + public void integrate() { + addDebugInfo( "1" ); + + if ( isRegistered()) { + addDebugInfo( "2" ); + try { + addDebugInfo( "3" ); + this.econWrapper = new VaultEconomyWrapper(); + addDebugInfo( "4" ); + } + catch ( java.lang.NoClassDefFoundError | Exception e ) { + addDebugInfo( "5:Exception:" + e.getMessage() ); + // ignore this exception since it means the plugin was not loaded + } + } + addDebugInfo( "6" ); + } + + @Override + public double getBalance(Player player) { + if (hasIntegrated()) { + return econWrapper.getBalance( player ); + } else { + return 0; } - return economy.getBalance(player.getName()); } - @Override public void setBalance(Player player, double amount) { - if (economy == null) { - return; + @Override + public void setBalance(Player player, double amount) { + if (hasIntegrated()) { + econWrapper.setBalance( player, amount ); } - economy.bankWithdraw(player.getName(), getBalance(player)); - economy.bankDeposit(player.getName(), amount); } - @Override public void addBalance(Player player, double amount) { - if (economy == null) { - return; + @Override + public void addBalance(Player player, double amount) { + if (hasIntegrated()) { + econWrapper.addBalance( player, amount ); } - economy.bankDeposit(player.getName(), amount); } - @Override public void removeBalance(Player player, double amount) { - if (economy == null) { - return; + @Override + public void removeBalance(Player player, double amount) { + if (hasIntegrated()) { + econWrapper.removeBalance( player, amount ); } - economy.bankWithdraw(player.getName(), amount); } - @Override public boolean canAfford(Player player, double amount) { - return economy != null && economy.bankHas(player.getName(), amount).transactionSuccess(); + @Override + public boolean canAfford(Player player, double amount) { + return hasIntegrated() && getBalance(player) >= amount; } - - @Override public String getProviderName() { - return economy.getName(); + + @Override + public String getDisplayName() + { + return ( !hasIntegrated() ? "Vault Economy" : econWrapper.getName()) + " (Vault)"; } - - @Override public boolean hasIntegrated() { - return economy != null; + + /** + *

    Its important to not only ensure that the wrapper exists, but the contained + * economy object is actually enabled too. + *

    + */ + @Override + public boolean hasIntegrated() { + return econWrapper != null && econWrapper.isEnabled(); } + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/vault.34315/"; + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomyWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomyWrapper.java new file mode 100644 index 000000000..08bf023ef --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/economies/VaultEconomyWrapper.java @@ -0,0 +1,98 @@ +package tech.mcprison.prison.spigot.economies; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; + +import net.milkbowl.vault.economy.EconomyResponse; +import tech.mcprison.prison.internal.Player; + +public class VaultEconomyWrapper +{ + private net.milkbowl.vault.economy.Economy economy = null; + + public VaultEconomyWrapper() { + super(); + + RegisteredServiceProvider economyProvider = + Bukkit.getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); + if (economyProvider != null) { + economy = economyProvider.getProvider(); + } + } + + public boolean isEnabled() + { + return economy != null && economy.isEnabled(); + } + + public String getName() { + return economy == null ? "not enabled" : economy.getName(); + } + + @SuppressWarnings( "deprecation" ) + public double getBalance(Player player) { + double results = 0; + if (economy != null) { + if ( economy.hasBankSupport() ) { + EconomyResponse bnkBal = economy.bankBalance( player.getName() ); + if ( bnkBal.transactionSuccess() ) { + results = bnkBal.balance; + } + } else { + results = economy.getBalance(player.getName()); + } + } + + return results; + } + + @SuppressWarnings( "deprecation" ) + public void setBalance(Player player, double amount) { + if (economy != null) { + if ( economy.hasBankSupport() ) { + economy.bankWithdraw(player.getName(), getBalance(player)); + economy.bankDeposit(player.getName(), amount); + } else { + economy.withdrawPlayer( player.getName(), getBalance( player ) ); + economy.depositPlayer( player.getName(), amount ); + } + } + } + + @SuppressWarnings( "deprecation" ) + public void addBalance(Player player, double amount) { + if (economy != null) { + if ( economy.hasBankSupport() ) { + economy.bankDeposit(player.getName(), amount); + } else { + economy.depositPlayer( player.getName(), amount ); + } + } + } + + @SuppressWarnings( "deprecation" ) + public void removeBalance(Player player, double amount) { + if (economy != null) { + if ( economy.hasBankSupport() ) { + economy.bankWithdraw(player.getName(), amount); + } else { + economy.withdrawPlayer( player.getName(), amount ); + } + } + } + + @SuppressWarnings( "deprecation" ) + public boolean canAfford(Player player, double amount) { + boolean results = false; + if (economy != null) { + if ( economy.hasBankSupport() ) { + results = economy.bankHas(player.getName(), amount).transactionSuccess(); + } else { + results = economy.has(player.getName(), amount); + } + } + return results; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotCommandSender.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotCommandSender.java index 6ad25f1a3..251782a5d 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotCommandSender.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotCommandSender.java @@ -68,6 +68,11 @@ public SpigotCommandSender(org.bukkit.command.CommandSender sender) { } } + @Override + public boolean isOp() { + return bukkitSender.isOp(); + } + public org.bukkit.command.CommandSender getWrapper() { return bukkitSender; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotOfflinePlayer.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotOfflinePlayer.java new file mode 100644 index 000000000..786609f86 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotOfflinePlayer.java @@ -0,0 +1,132 @@ +package tech.mcprison.prison.spigot.game; + +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.OfflinePlayer; + +import tech.mcprison.prison.internal.ItemStack; +import tech.mcprison.prison.internal.OfflineMcPlayer; +import tech.mcprison.prison.internal.inventory.Inventory; +import tech.mcprison.prison.internal.scoreboard.Scoreboard; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.util.Gamemode; +import tech.mcprison.prison.util.Location; + +public class SpigotOfflinePlayer + implements OfflineMcPlayer { + + private OfflinePlayer offlinePlayer; + + public SpigotOfflinePlayer(OfflinePlayer offlinePlayer) { + this.offlinePlayer = offlinePlayer; + } + + @Override + public String getName() { + return offlinePlayer.getName(); + } + + @Override + public void dispatchCommand( String command ) { + + } + + @Override + public UUID getUUID() { + return offlinePlayer.getUniqueId(); + } + + @Override + public String getDisplayName() { + return offlinePlayer.getName(); + } + + @Override + public boolean isOnline() { + return false; + } + + @Override + public boolean hasPermission( String perm ) { + Output.get().logError( "SpigotOfflinePlayer.hasPermission: Cannot access permissions for offline players." ); + return false; + } + + @Override + public void setDisplayName( String newDisplayName ) { + Output.get().logError( "SpigotOfflinePlayer.setDisplayName: Cannot set display names." ); + } + + @Override + public void sendMessage( String message ) { + Output.get().logError( "SpigotOfflinePlayer.sendMessage: Cannot send messages to offline players." ); + } + + @Override + public void sendMessage( String[] messages ) { + Output.get().logError( "SpigotOfflinePlayer.sendMessage: Cannot send messages to offline players." ); + } + + @Override + public void sendRaw( String json ) { + Output.get().logError( "SpigotOfflinePlayer.sendRaw: Cannot send messages to offline players." ); + } + + @Override + public boolean doesSupportColors() { + return false; + } + + @Override + public void give( ItemStack itemStack ) { + Output.get().logError( "SpigotOfflinePlayer.give: Cannot give to offline players." ); + } + + @Override + public Location getLocation() { + Output.get().logError( "SpigotOfflinePlayer.getLocation: Offline players have no location." ); + return null; + } + + @Override + public void teleport( Location location ) { + Output.get().logError( "SpigotOfflinePlayer.teleport: Offline players cannot be teleported." ); + } + + @Override + public void setScoreboard( Scoreboard scoreboard ) { + Output.get().logError( "SpigotOfflinePlayer.setScoreboard: Offline players cannot use scoreboards." ); + } + + @Override + public Gamemode getGamemode() { + Output.get().logError( "SpigotOfflinePlayer.getGamemode: Offline is not a valid gamemode." ); + return null; + } + + @Override + public void setGamemode( Gamemode gamemode ) { + } + + @Override + public Optional getLocale() { + Output.get().logError( "SpigotOfflinePlayer.getLocale: Offline is not a valid gamemode." ); + return null; + } + + @Override + public boolean isOp() { + return false; + } + + @Override + public void updateInventory() { + } + + @Override + public Inventory getInventory() { + return null; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotWorld.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotWorld.java index 293fd960f..f399b8a58 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotWorld.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotWorld.java @@ -53,7 +53,8 @@ public SpigotWorld(org.bukkit.World bukkitWorld) { } @Override public Block getBlockAt(Location location) { - return new SpigotBlock(bukkitWorld.getBlockAt(SpigotUtil.prisonLocationToBukkit(location))); + return new SpigotBlock( + bukkitWorld.getBlockAt(SpigotUtil.prisonLocationToBukkit(location))); } public org.bukkit.World getWrapper() { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GUIListener.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GUIListener.java index ef5ab31a1..f0645e7f1 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GUIListener.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GUIListener.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,16 +19,11 @@ package tech.mcprison.prison.spigot.gui; import org.bukkit.Bukkit; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; -import tech.mcprison.prison.gui.Button; -import tech.mcprison.prison.gui.ClickedButton; import tech.mcprison.prison.gui.GUI; import tech.mcprison.prison.spigot.SpigotPrison; -import tech.mcprison.prison.spigot.game.SpigotPlayer; import java.util.ArrayList; import java.util.List; @@ -36,6 +31,7 @@ /** * @author Faizaan A. Datoo */ +// From GABRYCA, I don't know if this's still needed, I won't remove it for now, but might be in the future public class GUIListener implements Listener { private static GUIListener instance; @@ -56,29 +52,12 @@ public void registerInventory(GUI inv) { inventories.add(inv); } - @EventHandler public void reactToClick(InventoryClickEvent e) { - final GUI[] gui = new GUI[1]; - final Button[] b = - new Button[1]; // Workaround to Java lambda's stupid final rule, elements within final arrays are re-assignable >:) - inventories.stream().filter(inv -> inv.getTitle().equals(e.getInventory().getTitle())) - .forEach(inv -> { - gui[0] = inv; - b[0] = inv.getButtons().get(e.getSlot()); - }); - if (b[0] == null) { - return; - } - e.setCancelled(true); - if (b[0].isCloseOnClick()) { - e.getWhoClicked().closeInventory(); - } - b[0].getAction() - .run(new ClickedButton(b[0], gui[0], new SpigotPlayer((Player) e.getWhoClicked()))); - } - @EventHandler public void closeInventory(InventoryCloseEvent e) { // Remove it if found - inventories.removeIf(gui -> gui.getTitle().equals(e.getInventory().getTitle())); + inventories.removeIf(gui -> gui.getTitle().equals( + SpigotPrison.getInstance().getCompatibility().getGUITitle( e ) + )); +// inventories.removeIf(gui -> gui.getTitle().equals(e.getInventory().getTitle())); } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GuiConfig.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GuiConfig.java new file mode 100644 index 000000000..344495f14 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/GuiConfig.java @@ -0,0 +1,201 @@ +package tech.mcprison.prison.spigot.gui; + +import org.bukkit.Color; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import tech.mcprison.prison.spigot.SpigotPrison; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +/** + * @author GABRYCA + */ +public class GuiConfig { + + private FileConfiguration conf; + + public GuiConfig() { + + if (!Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prison-gui-enabled")).equalsIgnoreCase("true")){ + return; + } + + // Filepath + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/GuiConfig.yml"); + + // Everything's here + values(); + + // Get the final config + conf = YamlConfiguration.loadConfiguration(file); + } + + private void dataConfig(String path, String string){ + + // Filepath + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/GuiConfig.yml"); + + // Check if the config exists + if(!file.exists()){ + try { + file.createNewFile(); + conf = YamlConfiguration.loadConfiguration(file); + conf.set(path, SpigotPrison.format(string)); + conf.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + boolean newValue = false; + int editedItems = 0; + conf = YamlConfiguration.loadConfiguration(file); + if (getFileGuiConfig().getString(path) == null){ + conf.set(path, SpigotPrison.format(string)); + editedItems++; + newValue = true; + } + if (newValue) { + conf.save(file); + System.out.println(Color.AQUA + "[Prison - GuiConfig.yml]" + Color.GREEN + " Added " + editedItems + " new values to the GuiConfig.yml"); + } + } catch (IOException e2){ + e2.printStackTrace(); + } + } + + // Get the final config + conf = YamlConfiguration.loadConfiguration(file); + + + } + + private void values(){ + dataConfig("Options.Ranks.GUI_Enabled","true"); + dataConfig("Options.Ranks.Permission_GUI_Enabled","false"); + dataConfig("Options.Ranks.Permission_GUI","prison.gui.ranks"); + dataConfig("Options.Mines.GUI_Enabled","true"); + dataConfig("Options.Mines.Permission_GUI_Enabled","false"); + dataConfig("Options.Mines.Permission_GUI","prison.gui.mines"); + dataConfig("Options.Prestiges.GUI_Enabled","true"); + dataConfig("Options.Prestiges.Permission_GUI_Enabled","false"); + dataConfig("Options.Prestiges.Permission_GUI","prison.gui.prestiges"); + dataConfig("Options.Ranks.Ladder","default"); + dataConfig("Options.Ranks.Item_gotten_rank","TRIPWIRE_HOOK"); + dataConfig("Options.Ranks.Item_not_gotten_rank","REDSTONE_BLOCK"); + dataConfig("Options.Ranks.Enchantment_effect_current_rank","true"); + dataConfig("Options.Mines.PermissionWarpPlugin","mines.tp."); + dataConfig("Options.Mines.CommandWarpPlugin","mines tp"); + dataConfig("Gui.Lore.ActivateWithinMode","&8Activate Within mode."); + dataConfig("Gui.Lore.ActivateRadiusMode","&8Activate Radius mode."); + dataConfig("Gui.Lore.AutoPickupGuiManager","&8AutoPickup Manager."); + dataConfig("Gui.Lore.AutoSmeltGuiManager","&8AutoSmelt Manager."); + dataConfig("Gui.Lore.AutoBlockGuiManager","&8AutoBlock Manager."); + dataConfig("Gui.Lore.BlockType","&3BlockType: "); + dataConfig("Gui.Lore.Blocks","&3Blocks:"); + dataConfig("Gui.Lore.Blocks2","&8Manage blocks of the Mine."); + dataConfig("Gui.Lore.ClickToChoose","&8Click to choose."); + dataConfig("Gui.Lore.ClickToConfirm","&8Click to confirm."); + dataConfig("Gui.Lore.ClickToCancel","&8Click to cancel."); + dataConfig("Gui.Lore.ClickToDecrease","&8Click to decrease."); + dataConfig("Gui.Lore.ClickToIncrease","&8Click to increase."); + dataConfig("Gui.Lore.ClickToManageRank","&8Manage this rank."); + dataConfig("Gui.Lore.ClickToManageCommands","&8Manage RankUPCommands."); + dataConfig("Gui.Lore.ClickToOpen","&8Click to open."); + dataConfig("Gui.Lore.ClickToTeleport","&8Click to teleport."); + dataConfig("Gui.Lore.ClickToUse","&8Click to use."); + dataConfig("Gui.Lore.ClickToRankup","&8Click to rankup"); + dataConfig("Gui.Lore.ClickToEditBlock", "&8Click to edit percentage."); + dataConfig("Gui.Lore.Chance","&3Chance: "); + dataConfig("Gui.Lore.Command","&3Command: &7"); + dataConfig("Gui.Lore.ContainsTheRank","&3The Rank "); + dataConfig("Gui.Lore.ContainsNoCommands"," &3contains no commands."); + dataConfig("Gui.Lore.DisableNotifications","&8Disable notifications."); + dataConfig("Gui.Lore.EnabledAll","&aAll features ON"); + dataConfig("Gui.Lore.DisabledAll","&aAll features OFF"); + dataConfig("Gui.Lore.FullSoundEnabled","&aFull Inv., notify with sound ON"); + dataConfig("Gui.Lore.FullSoundDisabled","&cFull Inv., notify with sound OFF"); + dataConfig("Gui.Lore.FullHologramEnabled","&aFull Inv., notify with hologram ON"); + dataConfig("Gui.Lore.FullHologramDisabled","&cFull Inv., notify with hologram OFF"); + dataConfig("Gui.Lore.Id","&3Rank id: &7"); + dataConfig("Gui.Lore.Info","&8&l|&3Info&8|"); + dataConfig("Gui.Lore.IfYouHaveEnoughMoney","&8If you have enough money"); + dataConfig("Gui.Lore.LadderThereAre","&8There're &3"); + dataConfig("Gui.Lore.LadderCommands"," &3Commands at ladder:"); + dataConfig("Gui.Lore.LeftClickToConfirm","&aLeft-Click to confirm."); + dataConfig("Gui.Lore.LeftClickToOpen","&8Left Click to open."); + dataConfig("Gui.Lore.LeftClickToReset","&aLeft Click to reset"); + dataConfig("Gui.Lore.ManageResetTime","&8Manage the reset time of Mine."); + dataConfig("Gui.Lore.MinesButton","&8Mines GUI manager."); + dataConfig("Gui.Lore.Name","&3Rank Name: &7"); + dataConfig("Gui.Lore.Notifications","&8Edit Mines notifications."); + dataConfig("Gui.Lore.PlayersWithTheRank","&3Players at rank: &7"); + dataConfig("Gui.Lore.PrestigeWarning", "&3Prestige will reset: "); + dataConfig("Gui.Lore.PrestigeWarning2", "&3 - &bRank"); + dataConfig("Gui.Lore.PrestigeWarning3", "&3 - &bBalance"); + dataConfig("Gui.Lore.Price","&3Price: &a$"); + dataConfig("Gui.Lore.Price2","&8Price: &a$"); + dataConfig("Gui.Lore.Price3","&3Rank Price: &a$"); + dataConfig("Gui.Lore.Percentage", "&8Percentage: "); + dataConfig("Gui.Lore.PrisonTasksButton","&8Prison Tasks Manager."); + dataConfig("Gui.Lore.ResetTime","&3Reset time(s): &7"); + dataConfig("Gui.Lore.Radius","&8Radius: "); + dataConfig("Gui.Lore.RankupCommands","&8&l|&3RankUPCommands&8| &8&l- &3"); + dataConfig("Gui.Lore.Rankup","&aRankup"); + dataConfig("Gui.Lore.RanksButton","&8Ranks GUI manager."); + dataConfig("Gui.Lore.ResetButton","&8Resets the mine."); + dataConfig("Gui.Lore.RightClickToCancel","&cRight-Click to cancel."); + dataConfig("Gui.Lore.RightClickToEnable","&aRight click to enable"); + dataConfig("Gui.Lore.RightClickToToggle","&cRight click to toggle"); + dataConfig("Gui.Lore.SpawnPoint","&3Spawnpoint: &7"); + dataConfig("Gui.Lore.StatusLockedMine","&8Status: &cLocked"); + dataConfig("Gui.Lore.StatusUnlockedMine","&8Status: &aUnlocked"); + dataConfig("Gui.Lore.SpawnPoint2","&8Set the mine spawn point."); + dataConfig("Gui.Lore.SizeOfMine","&3Size of Mine: &7"); + dataConfig("Gui.Lore.Selected","&3Selected"); + dataConfig("Gui.Lore.ShiftAndRightClickToDelete","&cShift + Right click to delete."); + dataConfig("Gui.Lore.ShiftAndRightClickToDisable","&cShift + Right click to disable"); + dataConfig("Gui.Lore.ShiftAndRightClickToToggle","&cShift + Right click to toggle"); + dataConfig("Gui.Lore.StatusEnabled","&8Enabled"); + dataConfig("Gui.Lore.StatusDisabled","&8Disabled"); + dataConfig("Gui.Lore.SkipReset1","&8Skip the reset if "); + dataConfig("Gui.Lore.SkipReset2","&8not enough blocks "); + dataConfig("Gui.Lore.SkipReset3","&8have been mined."); + dataConfig("Gui.Lore.Tp","&8Tp to the mine."); + dataConfig("Gui.Lore.Tag","&3Tag: &8"); + dataConfig("Gui.Lore.Tag2","&3Rank Tag: &7"); + dataConfig("Gui.Lore.Time","&8Time: "); + dataConfig("Gui.Lore.Volume","&3Volume: &7"); + dataConfig("Gui.Lore.Value", "&3Value: &a$"); + dataConfig("Gui.Lore.World","&3World: &7"); + dataConfig("Gui.Message.CantGetRanksAdmin", "&3[PRISON WARN] &cCan't get Ranks, there might be no ranks or the Ranks module's disabled."); + dataConfig("Gui.Message.NoSellAllItems", "&cSorry but there aren't SellAll Items to show."); + dataConfig("Gui.Message.EmptyGui","&cSorry, the GUI looks empty."); + dataConfig("Gui.Message.NoBlocksMine","&cSorry but there aren't blocks inside this Mine."); + dataConfig("Gui.Message.NoMines", "&cSorry but there aren't Mines to show."); + dataConfig("Gui.Message.NoRankupCommands", "&cSorry, but there aren't rankUpCommands for this ranks, please create one to use this GUI!"); + dataConfig("Gui.Message.NoLadders", "&cSorry but there aren't ladders to show."); + dataConfig("Gui.Message.NoRanksPrestigesLadder", "&3[PRISON WARN] &cThere aren't ranks in the -prestiges- ladder!"); + dataConfig("Gui.Message.NoRanksFoundAdmin", "&cSorry, but before using this GUI you should create a Rank in this ladder!"); + dataConfig("Gui.Message.NoRanksFound", "&cSorry, but there aren't Ranks in the default or selected ladder!"); + dataConfig("Gui.Message.NoRanksFoundHelp1", "&cSorry, but there aren't Ranks in the default or selected ladder or the ladder &3["); + dataConfig("Gui.Message.NoRanksFoundHelp2", "]&c isn't found!"); + dataConfig("Gui.Message.LadderPrestigesNotFound", "&3[PRISON WARN] &cLadder -prestiges- not found!"); + dataConfig("Gui.Message.TooManyBlocks","&cSorry, but there're too many Blocks and the max's 54 for the GUI"); + dataConfig("Gui.Message.TooManyLadders","&cSorry, but there're too many ladders and the max's 54 for the GUI"); + dataConfig("Gui.Message.TooManyMines","&cSorry, but there're too many mines and the max's 54 for the GUI"); + dataConfig("Gui.Message.TooManyRankupCommands","&cSorry, but there're too many RankupCommands and the max's 54 for the GUI"); + dataConfig("Gui.Message.TooManyRanks", "&cSorry, but there're too many ranks and the max's 54 for the GUI"); + dataConfig("Gui.Message.TooManySellAllItems", "&3[PRISON WARN] &cThere are too many items and the MAX for the GUI's 54!"); + dataConfig("Gui.Message.ZeroBlocksReset1","&8Set a mine's delay "); + dataConfig("Gui.Message.ZeroBlocksReset2","&8before reset when it "); + dataConfig("Gui.Message.ZeroBlocksReset3","&8reaches zero blocks."); + } + + public FileConfiguration getFileGuiConfig(){ + return conf; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/ListenersPrisonManager.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/ListenersPrisonManager.java new file mode 100644 index 000000000..0a653b13a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/ListenersPrisonManager.java @@ -0,0 +1,1768 @@ +package tech.mcprison.prison.spigot.gui; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.gui.GUI; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.compat.Compatibility; +import tech.mcprison.prison.spigot.gui.autofeatures.SpigotAutoBlockGUI; +import tech.mcprison.prison.spigot.gui.autofeatures.SpigotAutoFeaturesGUI; +import tech.mcprison.prison.spigot.gui.autofeatures.SpigotAutoPickupGUI; +import tech.mcprison.prison.spigot.gui.autofeatures.SpigotAutoSmeltGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMineBlockPercentageGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMineInfoGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMineNotificationRadiusGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMineNotificationsGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMineResetTimeGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMinesBlocksGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMinesConfirmGUI; +import tech.mcprison.prison.spigot.gui.mine.SpigotMinesGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotLaddersGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotRankManagerGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotRankPriceGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotRankUPCommandsGUI; +import tech.mcprison.prison.spigot.gui.rank.SpigotRanksGUI; +import tech.mcprison.prison.spigot.gui.sellall.SellAllAdminGUI; +import tech.mcprison.prison.spigot.gui.sellall.SellAllPriceGUI; + + +/** + * @author GABRYCA + * @author RoyalBlueRanger + */ +public class ListenersPrisonManager implements Listener { + + private static ListenersPrisonManager instance; + public static List activeGui = new ArrayList<>(); + public boolean isChatEventActive = false; + public int id; + public String rankNameOfChat; + + public ListenersPrisonManager(){} + + + public static ListenersPrisonManager get() { + if (instance == null) { + instance = new ListenersPrisonManager(); + } + return instance; + } + + @EventHandler + public void onGuiClosing(InventoryCloseEvent e){ + + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prison-gui-enabled")).equalsIgnoreCase("true"))){ + return; + } + + Player p = (Player) e.getPlayer(); + + activeGui.remove(p.getName()); + } + + public void addToGUIBlocker(Player p){ + if(!activeGui.contains(p.getName())) + activeGui.add(p.getName()); + } + + // On chat event to rename the a Rank Tag + @EventHandler (priority = EventPriority.LOWEST) + public void onChat(AsyncPlayerChatEvent e) { + if (isChatEventActive){ + Player p = e.getPlayer(); + String message = e.getMessage(); + Bukkit.getScheduler().cancelTask(id); + if (message.equalsIgnoreCase("close")){ + isChatEventActive = false; + p.sendMessage(SpigotPrison.format("&cRename tag closed, nothing got changed")); + e.setCancelled(true); + } else { + Bukkit.getScheduler().runTask(SpigotPrison.getInstance(), () -> Bukkit.getServer().dispatchCommand(p, "ranks set tag " + rankNameOfChat + " " + message)); + e.setCancelled(true); + isChatEventActive = false; + } + } + } + + // Cancel the events of the active GUI opened from the player + private void activeGuiEventCanceller(Player p, InventoryClickEvent e){ + if(activeGui.contains(p.getName())) { + e.setCancelled(true); + } + } + + + // InventoryClickEvent + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onClick(InventoryClickEvent e){ + + // Check if GUIs are enabled + if (!(Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("prison-gui-enabled")).equalsIgnoreCase("true"))){ + return; + } + + // Get the player + Player p = (Player) e.getWhoClicked(); + + + // If you click an empty slot, this should avoid the error. + // Also if there is no button that was clicked, then it may not be a Prison GUI on click event? + if(e.getCurrentItem() == null || e.getCurrentItem().getType() == Material.AIR || + e.getCurrentItem().getItemMeta() == null || !e.getCurrentItem().hasItemMeta() || + e.getCurrentItem().getItemMeta().getDisplayName() == null) { + activeGuiEventCanceller(p, e); + return; + } else { + e.getCurrentItem().getItemMeta().getDisplayName(); + } + + // Get action of the Inventory from the event + InventoryAction action = e.getAction(); + + // If an action equals one of these, and the inventory is open from the player equals + // one of the Prison Title, it'll cancel the event + if (action.equals(InventoryAction.MOVE_TO_OTHER_INVENTORY) || action.equals(InventoryAction.HOTBAR_SWAP) || + action.equals(InventoryAction.HOTBAR_MOVE_AND_READD) || action.equals(InventoryAction.NOTHING) || + action.equals(InventoryAction.CLONE_STACK) || action.equals(InventoryAction.COLLECT_TO_CURSOR) || + action.equals(InventoryAction.DROP_ONE_SLOT) || action.equals(InventoryAction.DROP_ONE_CURSOR) || + action.equals(InventoryAction.DROP_ALL_SLOT) || action.equals(InventoryAction.DROP_ALL_CURSOR) || + action.equals(InventoryAction.PICKUP_ALL) || action.equals(InventoryAction.PICKUP_HALF) || + action.equals(InventoryAction.PICKUP_ONE) || action.equals(InventoryAction.PICKUP_SOME) || + action.equals(InventoryAction.PLACE_ALL) || action.equals(InventoryAction.PLACE_ONE) || + action.equals(InventoryAction.PLACE_SOME) || action.equals(InventoryAction.SWAP_WITH_CURSOR) || + action.equals(InventoryAction.UNKNOWN)) { + activeGuiEventCanceller(p, e); + } + + // ensure the item has itemMeta and a display name + if (!e.getCurrentItem().hasItemMeta()){ + return; + } +// WARNING DO NOT USE Objects.requireNonNull() since it will throw a NullPointerException!! +// NEVER should we want that to happen. If displayName is null then this is not our event +// and we should not be screwing with it! +// else { +// Objects.requireNonNull(e.getCurrentItem().getItemMeta()).getDisplayName(); +// } + + // Get the button name + String buttonNameMain = e.getCurrentItem().getItemMeta().getDisplayName(); + + // If the buttonmain have a name longer than 2 characters (should be with colors), it won't take care about the color ids + if ( buttonNameMain.length() > 2 ) { + buttonNameMain = buttonNameMain.substring(2); + } + + // Split the button name in parts + String[] parts = buttonNameMain.split(" "); + + // Get ranks module + Module module = Prison.get().getModuleManager().getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + + // Get compat + Compatibility compat = SpigotPrison.getInstance().getCompatibility(); + + // Get title + String title = compat.getGUITitle(e).substring(2); + + // Check if the GUI have the right title and do the actions + switch (title) { + + // Check the title and do the actions + case "PrisonManager": + + // Call the method + PrisonManagerGUI(e, p, buttonNameMain); + + break; + + // Check the title + case "RanksManager -> Ladders": { + + // Call the method + LaddersGUI(e, p, buttonNameMain, module); + + break; + } + + // Check the title of the inventory and do the actions + case "Ladders -> Ranks": { + + // Call the method + RanksGUI(e, p, buttonNameMain); + + break; + } + // Check the title and do the actions + case "Prestiges -> PlayerPrestiges": { + + // Call the method + PlayerPrestigesGUI(e, p, buttonNameMain); + + break; + } + // Check the title and do the actions + case "Prestige -> Confirmation": { + + // Call the method + PrestigeConfirmationGUI(e, p, buttonNameMain); + + break; + } + // Check the title of the inventory and do things + case "Ranks -> RankManager": { + + // Call the method + RankManagerGUI(e, p, parts); + + break; + } + // Check the title and do the actions + case "Ranks -> PlayerRanks":{ + + // Call the method + PlayerRanksGUI(e, p, buttonNameMain); + + break; + } + // Check the title and do the actions + case "RankManager -> RankUPCommands": { + + // Call the method + RankUPCommandsGUI(e, p, buttonNameMain); + + break; + } + // Check the inventory name and do the actions + case "RankManager -> RankPrice": { + + // Call the method + RankPriceGUI(e, p, parts); + + break; + } + // Check the title and do the actions + case "MinesManager -> Mines": { + + // Call the method + MinesGUI(e, p, buttonNameMain); + + break; + } + // Check the title and do the actions + case "Mines -> PlayerMines": { + + // Call the method + PlayerMinesGUI(p, buttonNameMain); + + break; + } + case "Mines -> MineInfo": { + + // Call the method + MineInfoGUI(e, p, parts); + + break; + } + + // Check the title of the inventory and do the actions + case "Mines -> Delete": { + + // Call the method + MinesDeleteGUI(p, parts); + + break; + } + + // Check the title of the inventory and do the actions + case "MineInfo -> Blocks": { + + // Call the method + BlocksGUI(e, p, parts); + + break; + } + + // Check the inventory name and do the actions + case "MineInfo -> ResetTime": { + + // Call the method + ResetTimeGUI(e, p, parts); + + break; + } + + // Check the inventory title and do the actions + case "MineInfo -> MineNotifications": { + + // Call the method + MineNotificationsGUI(e, p, parts); + + break; + } + + case "MineInfo -> BlockPercentage":{ + + mineBlockPercentage(e, p, parts); + + break; + } + + // Check the inventory title and do the actions + case "MineNotifications -> Radius": { + + // Call the method + RadiusGUI(e, p, parts); + + break; + } + // Check the inventory title and do the actions + case "PrisonManager -> AutoFeatures": { + + // Call the method + AutoFeaturesGUI(e, p, parts); + + break; + } + + // Check the title and do the actions + case "AutoFeatures -> AutoPickup":{ + + // Call the method + AutoPickupGUI(e, p, parts); + + break; + } + + // Check the title and do the actions + case "AutoFeatures -> AutoSmelt":{ + + // Call the method + AutoSmeltGUI(e, p, parts); + + break; + } + + // Check the title and do the actions + case "AutoFeatures -> AutoBlock":{ + + // Call the method + AutoBlockGUI(e, p, parts); + + break; + } + + // Check the title and do the actions + case "PrisonManager -> SellAll-Admin":{ + + SellAllAdminGUI(e, p, buttonNameMain); + + break; + } + + // Check the title and do the actions + case "SellAll -> ItemValue":{ + + SellAllItemValue(e, p, parts); + + break; + } + + // Check the title and do the actions + case "PrisonManager -> SellAll-Player":{ + + p.closeInventory(); + e.setCancelled(true); + + break; + } + } + } + + private void mineBlockPercentage(InventoryClickEvent e, Player p, String[] parts) { + + // Rename the parts + String part1 = parts[0]; + String part2 = parts[1]; + String part3 = parts[2]; + String part4 = parts[3]; + + // Initialize the variable + double decreaseOrIncreaseValue = 0; + + // If there're enough parts init another variable + if (parts.length == 5){ + decreaseOrIncreaseValue = Double.parseDouble(parts[4]); + } + + // Check the button name and do the actions + if (part1.equalsIgnoreCase("Confirm:")) { + + // Check the click type and do the actions + if (e.isLeftClick()){ + + // Execute the command + Bukkit.dispatchCommand(p,"mines block set " + part2 + " " + part3 + " " + part4); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + + // Check the click type and do the actions + } else if (e.isRightClick()){ + + // Send a message to the player + p.sendMessage(SpigotPrison.format("&cEvent cancelled.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else { + + // Cancel the event + e.setCancelled(true); + return; + } + } + + // Give to val a value + double val = Double.parseDouble(part3); + + // Check the calculator symbol + if (part4.equals("-")){ + + // Check if the value's already too low + if (!((val - decreaseOrIncreaseValue) < 0)) { + + // If it isn't too low then decrease it + val = val - decreaseOrIncreaseValue; + + // If it is too low + } else { + + // Tell to the player that the value's too low + p.sendMessage(SpigotPrison.format("&cToo low, under 0%!")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } + + // Open an updated GUI after the value changed + SpigotMineBlockPercentageGUI gui = new SpigotMineBlockPercentageGUI(p, val, part1, part2); + gui.open(); + + // Check the calculator symbol + } else if (part4.equals("+")) { + + // Check if the value isn't too high + if (!((val + decreaseOrIncreaseValue) > 100)) { + + // Increase the value + val = val + decreaseOrIncreaseValue; + + // If the value's too high then do the action + } else { + + // Close the GUI and tell it to the player + p.sendMessage(SpigotPrison.format("&cToo high, exceed 100%!")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } + + // Open a new updated GUI with new values + SpigotMineBlockPercentageGUI gui = new SpigotMineBlockPercentageGUI(p, val, part1, part2); + gui.open(); + + // Cancel the event + e.setCancelled(true); + } + } + + private void SellAllItemValue(InventoryClickEvent e, Player p, String[] parts) { + + // Rename the parts + String part1 = parts[0]; + String part2 = parts[1]; + String part3 = parts[2]; + + // Initialize the variable + int decreaseOrIncreaseValue = 0; + + // If there're enough parts init another variable + if (parts.length == 4){ + decreaseOrIncreaseValue = Integer.parseInt(parts[3]); + } + + // Check the button name and do the actions + if (part1.equalsIgnoreCase("Confirm:")) { + + // Check the click type and do the actions + if (e.isLeftClick()){ + + // Execute the command + Bukkit.dispatchCommand(p,"sellall edit " + part2 + " " + part3); + + // Close the inventory + p.closeInventory(); + + return; + + // Check the click type and do the actions + } else if (e.isRightClick()){ + + // Send a message to the player + p.sendMessage(SpigotPrison.format("&cEvent cancelled.")); + + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else { + + // Cancel the event + e.setCancelled(true); + return; + } + } + + // Give to val a value + double val = Double.parseDouble(part2); + + // Check the calculator symbol + if (part3.equals("-")){ + + // Check if the value's already too low + if (!((val - decreaseOrIncreaseValue) < 0)) { + + // If it isn't too low then decrease it + val = val - decreaseOrIncreaseValue; + + // If it is too low + } else { + + // Tell to the player that the value's too low + p.sendMessage(SpigotPrison.format("&cToo low value.")); + + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + return; + } + + // Open an updated GUI after the value changed + SellAllPriceGUI gui = new SellAllPriceGUI(p, val, part1); + gui.open(); + + // Check the calculator symbol + } else if (part3.equals("+")){ + + // Check if the value isn't too high + if (!((val + decreaseOrIncreaseValue) > 2147483646)) { + + // Increase the value + val = val + decreaseOrIncreaseValue; + + // If the value's too high then do the action + } else { + + // Close the GUI and tell it to the player + p.sendMessage(SpigotPrison.format("&cToo high value.")); + e.setCancelled(true); + p.closeInventory(); + return; + } + + // Open a new updated GUI with new values + SellAllPriceGUI gui = new SellAllPriceGUI(p, val, part1); + gui.open(); + + } + } + + private void SellAllAdminGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + if (e.isRightClick()){ + + Bukkit.dispatchCommand(p, "sellall delete " + buttonNameMain); + p.closeInventory(); + + } else if (e.isLeftClick()){ + + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/SellAllConfig.yml"); + FileConfiguration conf = YamlConfiguration.loadConfiguration(file); + + SellAllPriceGUI gui = new SellAllPriceGUI(p,Double.parseDouble(Objects.requireNonNull(conf.getString("Items." + buttonNameMain + ".ITEM_VALUE"))), buttonNameMain); + gui.open(); + + } + + e.setCancelled(true); + } + + private void PrisonManagerGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Check the Item display name and do open the right GUI + switch (buttonNameMain) { + case "Ranks": { + SpigotLaddersGUI gui = new SpigotLaddersGUI(p); + gui.open(); + break; + } + + // Check the Item display name and do open the right GUI + case "AutoManager": { + SpigotAutoFeaturesGUI gui = new SpigotAutoFeaturesGUI(p); + gui.open(); + break; + } + + // Check the Item display name and do open the right GUI + case "Mines": { + SpigotMinesGUI gui = new SpigotMinesGUI(p); + gui.open(); + break; + } + + // Check the Item display name and do open the right GUI + case "SellAll": { + SellAllAdminGUI gui = new SellAllAdminGUI(p); + gui.open(); + break; + } + } + + // Cancel the event + e.setCancelled(true); + } + + private void LaddersGUI(InventoryClickEvent e, Player p, String buttonNameMain, Module module) { + + // Check if the Ranks module's loaded + if(!(module instanceof PrisonRanks)){ + p.sendMessage(SpigotPrison.format("&cThe GUI can't open because the &3Rank module &cisn't loaded")); + p.closeInventory(); + e.setCancelled(true); + return; + } + + // Get the ladder by the name of the button got before + Optional ladder = PrisonRanks.getInstance().getLadderManager().getLadder(buttonNameMain); + + // Check if the ladder exist, everything can happen but this shouldn't + if (!ladder.isPresent()) { + p.sendMessage("What did you actually click? Sorry ladder not found."); + return; + } + + // When the player click an item with shift and right click, e.isShiftClick should be enough but i want + // to be sure's a right click + if (e.isShiftClick() && e.isRightClick()) { + + // Execute the command + Bukkit.dispatchCommand(p, "ranks ladder delete " + buttonNameMain); + e.setCancelled(true); + p.closeInventory(); + SpigotLaddersGUI gui = new SpigotLaddersGUI(p); + gui.open(); + return; + + } + + // Open the GUI of ranks + SpigotRanksGUI gui = new SpigotRanksGUI(p, ladder); + gui.open(); + + // Cancel the event + e.setCancelled(true); + } + + private void RanksGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Get the rank + Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(buttonNameMain); + + // Check if the rank exist + if (!rankOptional.isPresent()) { + p.sendMessage(SpigotPrison.format("&cThe rank " + buttonNameMain + " does not exist.")); + return; + } + + // Get the rank + Rank rank = rankOptional.get(); + + // Check clicks + if (e.isShiftClick() && e.isRightClick()) { + + // Execute the command + Bukkit.dispatchCommand(p, "ranks delete " + buttonNameMain); + e.setCancelled(true); + p.closeInventory(); + return; + + } else { + + // Open a GUI + SpigotRankManagerGUI gui = new SpigotRankManagerGUI(p, rank); + gui.open(); + + } + + // Cancel the event + e.setCancelled(true); + } + + private void PlayerPrestigesGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Check the button name and do the actions + if (buttonNameMain.equalsIgnoreCase("Prestige")){ + // Close the inventory + p.closeInventory(); + // Execute the command + Bukkit.dispatchCommand(p, "prestige"); + } + + // Cancel the event + e.setCancelled(true); + } + + private void PrestigeConfirmationGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Check the button name and do the actions + if (buttonNameMain.equalsIgnoreCase("Confirm: Prestige")){ + // Execute the command + Bukkit.dispatchCommand(p, "rankup prestiges"); + // Close the inventory + p.closeInventory(); + } else if (buttonNameMain.equalsIgnoreCase("Cancel: Don't Prestige")){ + // Send a message to the player + p.sendMessage(SpigotPrison.format("&7[&3Info&7] &cCancelled")); + // Close the inventory + p.closeInventory(); + } + + // Cancel the event + e.setCancelled(true); + } + + private void RankManagerGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Output finally the buttonname and the minename explicit out of the array + String buttonname = parts[0]; + String rankName = parts[1]; + + // Get the rank + Optional rankOptional = PrisonRanks.getInstance().getRankManager().getRank(rankName); + + // Check the button name and do the actions + if (buttonname.equalsIgnoreCase("RankupCommands")){ + + // Check if the rank exist + if (!rankOptional.isPresent()) { + // Send a message to the player + p.sendMessage(SpigotPrison.format("&c[ERROR] The rank " + rankName + " does not exist.")); + return; + } + + // Get the rank + Rank rank = rankOptional.get(); + + // Check the rankupCommand of the Rank + if (rank.rankUpCommands == null) { + // Send a message to the player + p.sendMessage(SpigotPrison.format("&c[ERROR] There aren't commands for this rank anymore.")); + } + + // Open the GUI of commands + else { + SpigotRankUPCommandsGUI gui = new SpigotRankUPCommandsGUI(p, rank); + gui.open(); + } + + // Check the button name and do the actions + } else if (buttonname.equalsIgnoreCase("RankPrice")){ + + // Check and open a GUI + if(rankOptional.isPresent()) { + SpigotRankPriceGUI gui = new SpigotRankPriceGUI(p, (int) rankOptional.get().cost, rankOptional.get().name); + gui.open(); + } + + // Check the button name and do the actions + } else if (buttonname.equalsIgnoreCase("RankTag")){ + + // Send messages to the player + p.sendMessage(SpigotPrison.format("&7[&3Info&7] &3Please write the &6tag &3you'd like to use and &6submit&3.")); + p.sendMessage(SpigotPrison.format("&7[&3Info&7] &3Input &cclose &3to cancel or wait &c30 seconds&3.")); + // Start the async task + isChatEventActive = true; + rankNameOfChat = rankName; + id = Bukkit.getScheduler().scheduleSyncDelayedTask(SpigotPrison.getInstance(), () -> { + isChatEventActive = false; + p.sendMessage(SpigotPrison.format("&cYou ran out of time, tag not changed.")); + }, 20L * 30); + p.closeInventory(); + } + + // Cancel the event + e.setCancelled(true); + } + + private void PlayerRanksGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Check the buttonName and do the actions + if (buttonNameMain.equals(SpigotPrison.format(Objects.requireNonNull(GuiConfig.getString("Gui.Lore.Rankup")).substring(2)))){ + Bukkit.dispatchCommand(p, "rankup " + GuiConfig.getString("Options.Ranks.Ladder")); + p.closeInventory(); + } + + // Cancel the event + e.setCancelled(true); + } + + private void RankUPCommandsGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Check the clickType + if (e.isShiftClick() && e.isRightClick()) { + + // Execute the command + Bukkit.dispatchCommand(p, "ranks command remove " + buttonNameMain); + // Cancel the event + e.setCancelled(true); + // Close the inventory + p.closeInventory(); + return; + + } + + // Cancel the event + e.setCancelled(true); + } + + private void RankPriceGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Rename the parts + String part1 = parts[0]; + String part2 = parts[1]; + String part3 = parts[2]; + + // Initialize the variable + int decreaseOrIncreaseValue = 0; + + // If there're enough parts init another variable + if (parts.length == 4){ + decreaseOrIncreaseValue = Integer.parseInt(parts[3]); + } + + // Check the button name and do the actions + if (part1.equalsIgnoreCase("Confirm:")) { + + // Check the click type and do the actions + if (e.isLeftClick()){ + + // Execute the command + Bukkit.dispatchCommand(p,"ranks set cost " + part2 + " " + part3); + + // Close the inventory + p.closeInventory(); + + return; + + // Check the click type and do the actions + } else if (e.isRightClick()){ + + // Send a message to the player + p.sendMessage(SpigotPrison.format("&cEvent cancelled.")); + + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else { + + // Cancel the event + e.setCancelled(true); + return; + } + } + + // Give to val a value + int val = Integer.parseInt(part2); + + // Check the calculator symbol + if (part3.equals("-")){ + + // Check if the value's already too low + if (!((val - decreaseOrIncreaseValue) < 0)) { + + // If it isn't too low then decrease it + val = val - decreaseOrIncreaseValue; + + // If it is too low + } else { + + // Tell to the player that the value's too low + p.sendMessage(SpigotPrison.format("&cToo low value.")); + + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + return; + } + + // Open an updated GUI after the value changed + SpigotRankPriceGUI gui = new SpigotRankPriceGUI(p, val, part1); + gui.open(); + + // Check the calculator symbol + } else if (part3.equals("+")){ + + // Check if the value isn't too high + if (!((val + decreaseOrIncreaseValue) > 2147483646)) { + + // Increase the value + val = val + decreaseOrIncreaseValue; + + // If the value's too high then do the action + } else { + + // Close the GUI and tell it to the player + p.sendMessage(SpigotPrison.format("&cToo high value.")); + e.setCancelled(true); + p.closeInventory(); + return; + } + + // Open a new updated GUI with new values + SpigotRankPriceGUI gui = new SpigotRankPriceGUI(p, val, part1); + gui.open(); + + } + } + + private void MinesGUI(InventoryClickEvent e, Player p, String buttonNameMain) { + + // Variables + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(buttonNameMain); + + // Check the clicks + if (e.isShiftClick() && e.isRightClick()) { + // Execute the command + Bukkit.dispatchCommand(p, "mines delete " + buttonNameMain); + // Cancel the event + e.setCancelled(true); + // Close the inventory + p.closeInventory(); + // Open a GUI + SpigotMinesConfirmGUI gui = new SpigotMinesConfirmGUI(p, buttonNameMain); + gui.open(); + return; + } + + // Open the GUI of mines info + SpigotMineInfoGUI gui = new SpigotMineInfoGUI(p, m, buttonNameMain); + gui.open(); + + // Cancel the event + e.setCancelled(true); + } + + private void PlayerMinesGUI(Player p, String buttonNameMain) { + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + String permission = SpigotPrison.format(GuiConfig.getString("Options.Mines.PermissionWarpPlugin")); + + if (p.hasPermission(permission + buttonNameMain) || p.hasPermission(permission.substring(0, permission.length() - 1))){ + Bukkit.dispatchCommand(p, SpigotPrison.format(GuiConfig.getString("Options.Mines.CommandWarpPlugin") + " " + buttonNameMain)); + } + } + + private void MineInfoGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Output finally the buttonname and the minename explicit out of the array + String buttonname = parts[0]; + String mineName = parts[1]; + + // Check the name of the button and do the actions + switch (buttonname) { + case "Blocks_of_the_Mine:": + + // Open the GUI + SpigotMinesBlocksGUI gui = new SpigotMinesBlocksGUI(p, mineName); + gui.open(); + + break; + + // Check the name of the button and do the actions + case "Reset_Mine:": + + // Check the clickType and do the actions + if (e.isLeftClick()) { + // Execute the command + Bukkit.dispatchCommand(p, "mines reset " + mineName); + } else if (e.isRightClick()){ + // Execute the command + Bukkit.dispatchCommand(p, "mines set skipReset " + mineName); + } else if (e.isRightClick() && e.isShiftClick()){ + // Execute the command + Bukkit.dispatchCommand(p, "mines set zeroBlockResetDelay " + mineName); + } + + // Cancel the event + e.setCancelled(true); + + break; + + // Check the name of the button and do the actions + case "Mine_Spawn:": + + // Execute the command + Bukkit.dispatchCommand(p, "mines set spawn " + mineName); + + // Cancel the event + e.setCancelled(true); + break; + + // Check the name of the button and do the actions + case "Mine_notifications:": + + // Open the GUI + SpigotMineNotificationsGUI gui1 = new SpigotMineNotificationsGUI(p, mineName); + gui1.open(); + + break; + + // Check the name of the button and do the actions + case "TP_to_the_Mine:": + + // Close the inventory + p.closeInventory(); + + // Execute the Command + Bukkit.dispatchCommand(p, "mines tp " + mineName); + + break; + + // Check the name of the button and do the actions + case "Reset_Time:": + + // Initialize the variables + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + int val = m.getResetTime(); + + // Open the GUI + SpigotMineResetTimeGUI gui2 = new SpigotMineResetTimeGUI(p, val, mineName); + gui2.open(); + + break; + } + } + + private void MinesDeleteGUI(Player p, String[] parts) { + + // Output finally the buttonname and the minename explicit out of the array + String buttonname = parts[0]; + String mineName = parts[1]; + + // Check the name of the button and do the actions + if (buttonname.equals("Confirm:")) { + + // Confirm + Bukkit.dispatchCommand(p, "mines delete " + mineName + " confirm"); + + // Close the Inventory + p.closeInventory(); + + // Check the name of the button and do the actions + } else if (buttonname.equals("Cancel:")) { + + // Cancel + Bukkit.dispatchCommand(p, "mines delete " + mineName + " cancel"); + + // Close the inventory + p.closeInventory(); + + } + } + + private void BlocksGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Output finally the buttonname and the minename explicit out of the array + String buttonname = parts[0]; + String mineName = parts[1]; + double percentage = Double.parseDouble(parts[2]); + + // Check the click Type and do the actions + if (e.isShiftClick() && e.isRightClick()) { + + // Execute the command + Bukkit.dispatchCommand(p, "mines block remove " + mineName + " " + buttonname); + + // Cancel the event + e.setCancelled(true); + + // Close the GUI so it can be updated + p.closeInventory(); + + // Open the GUI + SpigotMinesBlocksGUI gui = new SpigotMinesBlocksGUI(p, mineName); + gui.open(); + } else { + + SpigotMineBlockPercentageGUI gui = new SpigotMineBlockPercentageGUI(p, percentage, mineName, buttonname); + gui.open(); + + } + } + + private void ResetTimeGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Rename the parts + String part1 = parts[0]; + String part2 = parts[1]; + String part3 = parts[2]; + + // Initialize the variable + int decreaseOrIncreaseValue = 0; + + // If there're enough parts init another variable + if (parts.length == 4){ + decreaseOrIncreaseValue = Integer.parseInt(parts[3]); + } + + // Check the button name and do the actions + if (part1.equalsIgnoreCase("Confirm:")) { + + // Check the click type and do the actions + if (e.isLeftClick()){ + + // Execute the command + Bukkit.dispatchCommand(p,"mines set resettime " + part2 + " " + part3); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + + // Check the click type and do the actions + } else if (e.isRightClick()){ + + // Send a message to the player + p.sendMessage(SpigotPrison.format("&cEvent cancelled.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else { + + // Cancel the event + e.setCancelled(true); + return; + } + } + + // Give to val a value + int val = Integer.parseInt(part2); + + // Check the calculator symbol + if (part3.equals("-")){ + + // Check if the value's already too low + if (!((val - decreaseOrIncreaseValue) < 0)) { + + // If it isn't too low then decrease it + val = val - decreaseOrIncreaseValue; + + // If it is too low + } else { + + // Tell to the player that the value's too low + p.sendMessage(SpigotPrison.format("&cToo low value.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } + + // Open an updated GUI after the value changed + SpigotMineResetTimeGUI gui = new SpigotMineResetTimeGUI(p, val, part1); + gui.open(); + + // Check the calculator symbol + } else if (part3.equals("+")){ + + // Check if the value isn't too high + if (!((val + decreaseOrIncreaseValue) > 999999)) { + + // Increase the value + val = val + decreaseOrIncreaseValue; + + // If the value's too high then do the action + } else { + + // Close the GUI and tell it to the player + p.sendMessage(SpigotPrison.format("&cToo high value.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } + + // Open a new updated GUI with new values + SpigotMineResetTimeGUI gui = new SpigotMineResetTimeGUI(p, val, part1); + gui.open(); + + // Cancel the event + e.setCancelled(true); + + } + } + + private void MineNotificationsGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Output finally the buttonname and the minename explicit out of the array + String buttonname = parts[0]; + String mineName = parts[1]; + String typeNotification; + long val; + + // Init variables + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + // Check the button name and do the actions + if (buttonname.equalsIgnoreCase("Within_Mode:")){ + + // Change the value of the variable + typeNotification = "within"; + + // Execute command + Bukkit.dispatchCommand(p, "mines set notification " + mineName + " " + typeNotification + " " + "0"); + + // Cancel the event and close the inventory + e.setCancelled(true); + p.closeInventory(); + + // Check the button name and do the actions + } else if (buttonname.equalsIgnoreCase("Radius_Mode:")){ + + // Change the value of the variable + typeNotification = "radius"; + + // Get the variable value + val = m.getNotificationRadius(); + + // Open the GUI + SpigotMineNotificationRadiusGUI gui = new SpigotMineNotificationRadiusGUI(p, val, typeNotification, mineName); + gui.open(); + + // Check the button name and do the actions + } else if (buttonname.equalsIgnoreCase("Disabled_Mode:")){ + + // Change the value of the variable + typeNotification = "disabled"; + + // Execute the command + Bukkit.dispatchCommand(p, "mines set notification " + mineName + " " + typeNotification + " " + "0"); + + // Cancel the event and close the inventory + e.setCancelled(true); + p.closeInventory(); + + } + } + + private void RadiusGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Rename the variables + String part1 = parts[0]; + String part2 = parts[1]; + String part3 = parts[2]; + String typeNotification; + + // Init the variable + int decreaseOrIncreaseValue = 0; + + // Check the button name and do the actions + if (!(part1.equalsIgnoreCase("Confirm:"))){ + + // Give them a value + decreaseOrIncreaseValue = Integer.parseInt(parts[3]); + typeNotification = parts[4]; + + // Do others actions + } else { + + // Give it a value + typeNotification = parts[3]; + } + + // Check the button name and do the actions + if (part1.equalsIgnoreCase("Confirm:")) { + + // Check the click type + if (e.isLeftClick()){ + + // Execute the command + Bukkit.dispatchCommand(p,"mines set notification " + part2 + " " + typeNotification + " " + part3); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else if (e.isRightClick()){ + + // Close the inventory + p.sendMessage(SpigotPrison.format("&cEvent cancelled.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + + return; + } else { + + // Cancel the event + e.setCancelled(true); + return; + } + } + + // Init a new value + long val = Integer.parseInt(part2); + + // Check the calculator symbol + if (part3.equals("-")){ + + // Check if the value's too low + if (!((val - decreaseOrIncreaseValue) < 0)) { + + // Decrease the value + val = val - decreaseOrIncreaseValue; + + // If the value's too low + } else { + + // Close the inventory and tell it the player + p.sendMessage(SpigotPrison.format("&cToo low value.")); + + // Cancel the event + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + return; + } + + // Open a new updated GUI with new values + SpigotMineNotificationRadiusGUI gui = new SpigotMineNotificationRadiusGUI(p, val, typeNotification, part1); + gui.open(); + + // Check the calculator symbol + } else if (part3.equals("+")){ + + // Check if the value's too high + if (!((val + decreaseOrIncreaseValue) > 9999999)) { + + // Increase the value + val = val + decreaseOrIncreaseValue; + + // If the value's too high + } else { + + // Close the inventory and tell it to the player + p.sendMessage(SpigotPrison.format("&cToo high value.")); + + // Cancel the inventory + e.setCancelled(true); + + // Close the inventory + p.closeInventory(); + return; + } + + // Open a new updated GUI with new values + SpigotMineNotificationRadiusGUI gui = new SpigotMineNotificationRadiusGUI(p, val, typeNotification, part1); + gui.open(); + + } + } + + private void AutoFeaturesGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Get the config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + // Output finally the buttonname and the mode explicit out of the array + String buttonname = parts[0]; + String mode = parts[1]; + + boolean enabled = mode.equalsIgnoreCase("Enabled"); + + // Check the clickType and do the actions + if ( enabled && e.isRightClick() && e.isShiftClick() || + !enabled && e.isRightClick()){ + + if (buttonname.equalsIgnoreCase("Full_Inv_Play_Sound")){ + afConfig.setFeature( AutoFeatures.playSoundIfInventoryIsFull, !enabled ); + saveConfigAutoFeatures(e, p); + } + + if (buttonname.equalsIgnoreCase("Full_Inv_Hologram")){ + afConfig.setFeature(AutoFeatures.hologramIfInventoryIsFull, !enabled); + saveConfigAutoFeatures(e,p); + } + + if (buttonname.equalsIgnoreCase("All")){ + afConfig.setFeature(AutoFeatures.isAutoManagerEnabled, !enabled); + saveConfigAutoFeatures(e,p); + } + + } + + // Check the clickType and do the actions + if (enabled && e.isRightClick() && e.isShiftClick() || !enabled && e.isRightClick() || enabled && e.isLeftClick()){ + if (buttonname.equalsIgnoreCase("AutoPickup")){ + if (e.isLeftClick()){ + SpigotAutoPickupGUI gui = new SpigotAutoPickupGUI(p); + gui.open(); + return; + } + afConfig.setFeature(AutoFeatures.autoPickupEnabled, !enabled); + saveConfigAutoFeatures(e,p); + } + + if (buttonname.equalsIgnoreCase("AutoSmelt")){ + if (e.isLeftClick()){ + SpigotAutoSmeltGUI gui = new SpigotAutoSmeltGUI(p); + gui.open(); + return; + } + afConfig.setFeature(AutoFeatures.autoSmeltEnabled, !enabled); + saveConfigAutoFeatures(e,p); + } + + if (buttonname.equalsIgnoreCase("AutoBlock")){ + if (e.isLeftClick()){ + SpigotAutoBlockGUI gui = new SpigotAutoBlockGUI(p); + gui.open(); + return; + } + afConfig.setFeature(AutoFeatures.autoBlockEnabled, !enabled); + saveConfigAutoFeatures(e,p); + } + } + } + + private void AutoPickupGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Get the config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + // Output finally the buttonname and the mode explicit out of the array + String buttonname = parts[0]; + String mode = parts[1]; + + boolean enabled = mode.equalsIgnoreCase("Enabled"); + + // Check the click and do the actions, also the buttonName + if ( enabled && e.isRightClick() && e.isShiftClick() || + !enabled && e.isRightClick() ){ + + if (buttonname.equalsIgnoreCase("All_Blocks")){ + afConfig.setFeature( AutoFeatures.autoPickupAllBlocks, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Cobblestone")){ + afConfig.setFeature(AutoFeatures.autoPickupCobbleStone, !enabled); + saveConfigPickup(e,p); + } + + if (buttonname.equalsIgnoreCase("Gold_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupGoldOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Iron_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupIronOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Coal_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupCoalOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Diamond_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupDiamondOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Redstone_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupRedStoneOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Emerald_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupEmeraldOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Quartz_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupQuartzOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Lapis_Ore")){ + afConfig.setFeature( AutoFeatures.autoPickupLapisOre, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Snow_Ball")){ + afConfig.setFeature( AutoFeatures.autoPickupSnowBall, !enabled ); + saveConfigPickup(e, p); + } + + if (buttonname.equalsIgnoreCase("Glowstone_Dust")){ + afConfig.setFeature( AutoFeatures.autoPickupGlowstoneDust, !enabled ); + saveConfigPickup(e, p); + } + } + + + } + + private void AutoSmeltGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Get the config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + // Output finally the buttonname and the mode explicit out of the array + String buttonname = parts[0]; + String mode = parts[1]; + + boolean enabled = mode.equalsIgnoreCase("Enabled"); + + // Check the clickType and do the actions + if ( enabled && e.isRightClick() && e.isShiftClick() || + !enabled && e.isRightClick()){ + + if (buttonname.equalsIgnoreCase("Gold_Ore")){ + afConfig.setFeature( AutoFeatures.autoSmeltGoldOre, !enabled ); + saveConfigSmelt(e, p); + } + + if (buttonname.equalsIgnoreCase("Iron_Ore")){ + afConfig.setFeature( AutoFeatures.autoSmeltIronOre, !enabled ); + saveConfigSmelt(e, p); + } + + if (buttonname.equalsIgnoreCase("All_Ores")){ + afConfig.setFeature( AutoFeatures.autoSmeltAllBlocks, !enabled ); + saveConfigSmelt(e, p); + } + + } + } + + private void AutoBlockGUI(InventoryClickEvent e, Player p, String[] parts) { + + // Get the config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + // Output finally the buttonname and the mode explicit out of the array + String buttonname = parts[0]; + String mode = parts[1]; + + boolean enabled = mode.equalsIgnoreCase("Enabled"); + + // Check the clickType and do the actions + if ( enabled && e.isRightClick() && e.isShiftClick() || + !enabled && e.isRightClick()){ + + if (buttonname.equalsIgnoreCase("Gold_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockGoldBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Iron_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockIronBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Coal_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockCoalBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Diamond_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockDiamondBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Redstone_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockRedstoneBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Emerald_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockEmeraldBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Quartz_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockQuartzBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Prismarine_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockPrismarineBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Lapis_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockLapisBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Snow_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockSnowBlock, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("Glowstone_Block")){ + afConfig.setFeature( AutoFeatures.autoBlockGlowstone, !enabled ); + saveConfigBlock(e, p); + } + + if (buttonname.equalsIgnoreCase("All_Blocks")){ + afConfig.setFeature( AutoFeatures.autoBlockAllBlocks, !enabled ); + saveConfigBlock(e, p); + } + + } + + } + + + /** + * Save the auto features, and then cancel the event and close the inventory. + * + * @param e + * @param player + */ + private boolean saveAutoFeatures( InventoryClickEvent e, Player player ) { + boolean success = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig().saveConf(); + e.setCancelled(true); + player.closeInventory(); + return success; + } + + + private boolean saveConfigBlock(InventoryClickEvent e, Player p) { + boolean success = saveAutoFeatures( e, p ); + SpigotAutoBlockGUI gui = new SpigotAutoBlockGUI(p); + gui.open(); + return success; + } + + private boolean saveConfigSmelt(InventoryClickEvent e, Player p) { + boolean success = saveAutoFeatures( e, p ); + SpigotAutoSmeltGUI gui = new SpigotAutoSmeltGUI(p); + gui.open(); + return success; + } + + private boolean saveConfigPickup(InventoryClickEvent e, Player p) { + boolean success = saveAutoFeatures( e, p ); + SpigotAutoPickupGUI gui = new SpigotAutoPickupGUI(p); + gui.open(); + return success; + } + + private boolean saveConfigAutoFeatures(InventoryClickEvent e, Player p) { + boolean success = saveAutoFeatures( e, p ); + SpigotAutoFeaturesGUI gui = new SpigotAutoFeaturesGUI(p); + gui.open(); + return success; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUI.java index 296cd6621..515496635 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUI.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUI.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ import tech.mcprison.prison.gui.Button; import tech.mcprison.prison.gui.GUI; import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.spigot.SpigotUtil; import tech.mcprison.prison.spigot.inventory.SpigotInventory; import java.util.HashMap; @@ -34,6 +35,7 @@ /** * @author Faizaan A. Datoo */ +// From GABRYCA, I don't know if this's still needed, I won't remove it for now, but might be in the future public class SpigotGUI implements GUI { private Map buttons; @@ -66,10 +68,9 @@ public SpigotGUI(String title, int numSlots) { return this; } - @SuppressWarnings( "deprecation" ) private ItemStack buttonToItemStack(Button button) { - ItemStack stack = - new ItemStack(button.getItem().getLegacyId(), 1, button.getItem().getData()); + ItemStack stack = SpigotUtil.getItemStack( button.getItem(), 1 ); +// new ItemStack(button.getItem().getLegacyId(), 1, button.getItem().getData()); ItemMeta meta = stack.getItemMeta(); meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&r" + button.getName())); meta.setLore(button.getLore()); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUIComponents.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUIComponents.java new file mode 100644 index 000000000..08725924a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotGUIComponents.java @@ -0,0 +1,57 @@ +package tech.mcprison.prison.spigot.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.spigot.SpigotPrison; + +/** + * @author rbluer RoyalBlueRanger + * @author GABRYCA + */ +public abstract class SpigotGUIComponents { + + // createButton method (create a button for the GUI - item) + protected ItemStack createButton(Material id, int amount, List lore, String display) { + + ItemStack item = new ItemStack(id, amount); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(display); + try { + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + } catch (NoClassDefFoundError ignored){} + meta.setLore(lore); + item.setItemMeta(meta); + + return item; + } + + // createLore method (create a lore for the button) + protected List createLore( String... lores ) { + List results = new ArrayList<>(); + for ( String lore : lores ) { + results.add( SpigotPrison.format(lore) ); + } + return results; + } + + // checkRanks method (check if the ranks module's enabled with success or disabled) + protected boolean checkRanks(Player p){ + Module module = Prison.get().getModuleManager().getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + if(!(module instanceof PrisonRanks)){ + p.sendMessage(SpigotPrison.format("&c[ERROR] The GUI can't open because the &3Ranks module &cisn't loaded")); + p.closeInventory(); + } + return module instanceof PrisonRanks; + } + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotPrisonGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotPrisonGUI.java new file mode 100644 index 000000000..ab77720fe --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/SpigotPrisonGUI.java @@ -0,0 +1,96 @@ +package tech.mcprison.prison.spigot.gui; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; + +/** + * @author GABRYCA + */ +public class SpigotPrisonGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotPrisonGUI(Player p){ + this.p = p; + } + + public void open() { + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3PrisonManager")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Lore of the button + List rankslore = createLore( + guiConfig.getString("Gui.Lore.RanksButton"), + guiConfig.getString("Gui.Lore.ClickToOpen")); + + // Lore of the button + List prisontaskslore = createLore( + guiConfig.getString("Gui.Lore.PrisonTasksButton"), + guiConfig.getString("Gui.Lore.ClickToOpen")); + + // Lore of the button + List mineslore = createLore( + guiConfig.getString("Gui.Lore.MinesButton"), + guiConfig.getString("Gui.Lore.ClickToOpen")); + + // Lore of the button + List sellallLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen")); + + // Create the button, set up the material, amount, lore and name + ItemStack ranks = createButton(Material.TRIPWIRE_HOOK, 1, rankslore, SpigotPrison.format("&3" + "Ranks")); + + // Create the button, set up the material, amount, lore and name + ItemStack prisontasks = createButton(Material.IRON_PICKAXE, 1, prisontaskslore, SpigotPrison.format("&3" + "AutoManager")); + + // Create the button, set up the material, amount, lore and name + ItemStack mines = createButton(Material.DIAMOND_ORE, 1, mineslore, SpigotPrison.format("&3" + "Mines")); + + // Create the button, set up the material, amount, lore and name + ItemStack sellall = createButton(Material.CHEST, 1 , sellallLore, SpigotPrison.format("&3" + "SellAll")); + + //Position of the button + inv.setItem(10, ranks); + + //Position of the button + inv.setItem(12, prisontasks); + + //Position of the button + inv.setItem(14, mines); + + //Position of the button + inv.setItem(16, sellall); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoBlockGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoBlockGUI.java new file mode 100644 index 000000000..4ea903bec --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoBlockGUI.java @@ -0,0 +1,165 @@ +package tech.mcprison.prison.spigot.gui.autofeatures; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.GUIListener; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotAutoBlockGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotAutoBlockGUI(Player p){ + this.p = p; + } + + public void open() { + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3AutoFeatures -> AutoBlock")); + + // Config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + if (guiBuilder(GuiConfig, inv, afConfig)) return; + + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, AutoFeaturesFileConfig afConfig) { + try { + buttonsSetup(guiConfig, inv, afConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, AutoFeaturesFileConfig afConfig) { + List enabledLore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable") + ); + + List disabledLore = createLore( + guiConfig.getString("Gui.Lore.RightClickToEnable") + ); + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockAllBlocks ) ) { + ItemStack Enabled = createButton(Material.EMERALD_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "All_Blocks Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.REDSTONE_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "All_Blocks Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockGoldBlock ) ) { + ItemStack Enabled = createButton(Material.GOLD_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Gold_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.GOLD_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Gold_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockIronBlock ) ) { + ItemStack Enabled = createButton(Material.IRON_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Iron_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.IRON_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Iron_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockCoalBlock ) ) { + ItemStack Enabled = createButton(Material.COAL_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Coal_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.COAL_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Coal_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockDiamondBlock ) ) { + ItemStack Enabled = createButton(Material.DIAMOND_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Diamond_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.DIAMOND_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Diamond_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockRedstoneBlock ) ) { + ItemStack Enabled = createButton(Material.REDSTONE_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Redstone_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.REDSTONE_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Redstone_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockEmeraldBlock ) ) { + ItemStack Enabled = createButton(Material.EMERALD_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Emerald_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.EMERALD_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Emerald_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockQuartzBlock ) ) { + ItemStack Enabled = createButton(Material.QUARTZ_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Quartz_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.QUARTZ_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Quartz_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockPrismarineBlock ) ) { + ItemStack Enabled = createButton(Material.PRISMARINE, 1, enabledLore, SpigotPrison.format("&a" + "Prismarine_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.PRISMARINE, 1, disabledLore, SpigotPrison.format("&c" + "Prismarine_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockLapisBlock ) ) { + ItemStack Enabled = createButton(Material.LAPIS_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Lapis_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.LAPIS_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Lapis_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockSnowBlock ) ) { + ItemStack Enabled = createButton(Material.SNOW_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "Snow_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.SNOW_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "Snow_Block Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockGlowstone ) ) { + ItemStack Enabled = createButton(Material.GLOWSTONE, 1, enabledLore, SpigotPrison.format("&a" + "Glowstone_Block Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.GLOWSTONE, 1, disabledLore, SpigotPrison.format("&c" + "Glowstone_Block Disabled")); + inv.addItem(Disabled); + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoFeaturesGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoFeaturesGUI.java new file mode 100644 index 000000000..43d4227e4 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoFeaturesGUI.java @@ -0,0 +1,194 @@ +package tech.mcprison.prison.spigot.gui.autofeatures; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotAutoFeaturesGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotAutoFeaturesGUI(Player p){ + this.p = p; + } + + public void open() { + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3PrisonManager -> AutoFeatures")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + if (guiBuilder(inv, GuiConfig, afConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + try { + buttonsSetup(inv, guiConfig, afConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + + // Declare buttons + ItemStack autoPickup; + ItemStack autoSmelt; + ItemStack autoBlock; + ItemStack enabledOrDisabled; + ItemStack playSound; + ItemStack hologram; + + if ( afConfig.isFeatureBoolean( AutoFeatures.playSoundIfInventoryIsFull ) ){ + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.FullSoundEnabled"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable")); + playSound = createButton(Material.EMERALD_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&a" + "Full_Inv_Play_Sound Enabled")); + + } else { + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.FullSoundDisabled"), + guiConfig.getString("Gui.Lore.RightClickToEnable")); + playSound = createButton(Material.REDSTONE_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&c" + "Full_Inv_Play_Sound Disabled")); + + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.hologramIfInventoryIsFull ) ){ + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.FullHologramEnabled"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable")); + hologram = createButton(Material.EMERALD_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&a" + "Full_Inv_Hologram Enabled")); + + } else { + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.FullHologramDisabled"), + guiConfig.getString("Gui.Lore.RightClickToEnable")); + hologram = createButton(Material.REDSTONE_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&c" + "Full_Inv_Hologram Disabled")); + + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.isAutoManagerEnabled ) ){ + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.EnabledAll"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable")); + enabledOrDisabled = createButton(Material.EMERALD_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&a" + "All Enabled")); + + } else { + + List EnabledOrDisabledLore = createLore( + guiConfig.getString("Gui.Lore.DisabledAll"), + guiConfig.getString("Gui.Lore.RightClickToEnable")); + enabledOrDisabled = createButton(Material.REDSTONE_BLOCK, 1, EnabledOrDisabledLore, SpigotPrison.format("&c" + "All Disabled")); + + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupEnabled ) ) { + // Lore of the button + List autoPickupLore = createLore( + guiConfig.getString("Gui.Lore.AutoPickupGuiManager"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoPickup = createButton(Material.EMERALD_BLOCK, 1, autoPickupLore, SpigotPrison.format("&3" + "AutoPickup Enabled")); + } else { + // Lore of the button + List autoPickupLore = createLore( + guiConfig.getString("Gui.Lore.AutoPickupGuiManager"), + guiConfig.getString("Gui.Lore.RightClickToEnable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoPickup = createButton(Material.REDSTONE_BLOCK, 1, autoPickupLore, SpigotPrison.format("&c" + "AutoPickup Disabled")); + } + + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoSmeltEnabled ) ) { + // Lore of the button + List autoSmeltLore = createLore( + guiConfig.getString("Gui.Lore.AutoSmeltGuiManager"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoSmelt = createButton(Material.EMERALD_BLOCK, 1, autoSmeltLore, SpigotPrison.format("&3" + "AutoSmelt Enabled")); + } else { + // Lore of the button + List autoSmeltLore = createLore( + guiConfig.getString("Gui.Lore.AutoSmeltGuiManager"), + guiConfig.getString("Gui.Lore.RightClickToEnable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoSmelt = createButton(Material.REDSTONE_BLOCK, 1, autoSmeltLore, SpigotPrison.format("&c" + "AutoSmelt Disabled")); + } + + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoBlockEnabled ) ) { + // Lore of the button + List autoBlockLore = createLore( + guiConfig.getString("Gui.Lore.AutoBlockGuiManager"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoBlock = createButton(Material.EMERALD_BLOCK, 1, autoBlockLore, SpigotPrison.format("&3" + "AutoBlock Enabled")); + + } else { + // Lore of the button + List autoBlockLore = createLore( + guiConfig.getString("Gui.Lore.AutoBlockGuiManager"), + guiConfig.getString("Gui.Lore.RightClickToEnable"), + guiConfig.getString("Gui.Lore.LeftClickToOpen")); + autoBlock = createButton(Material.REDSTONE_BLOCK, 1, autoBlockLore, SpigotPrison.format("&c" + "AutoBlock Disabled")); + } + + //Position of the button + inv.setItem(2, playSound); + + //Position of the button + inv.setItem(6, hologram); + + //Position of the button + inv.setItem(10, autoPickup); + + //Position of the button + inv.setItem(13, autoSmelt); + + //Position of the button + inv.setItem(16, autoBlock); + + //Position of the button + inv.setItem(19, enabledOrDisabled); + + //Position of the button + inv.setItem(22, enabledOrDisabled); + + //Position of the button + inv.setItem(25, enabledOrDisabled); + } + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoPickupGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoPickupGUI.java new file mode 100644 index 000000000..ad07e8aff --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoPickupGUI.java @@ -0,0 +1,181 @@ +package tech.mcprison.prison.spigot.gui.autofeatures; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotAutoPickupGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotAutoPickupGUI(Player p){ + this.p = p; + } + + public void open() { + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3AutoFeatures -> AutoPickup")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + if (guiBuilder(inv, GuiConfig, afConfig)) return; + + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + try { + buttonsSetup(inv, guiConfig, afConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + + List enabledLore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable") + ); + + List disabledLore = createLore( + guiConfig.getString("Gui.Lore.RightClickToEnable") + ); + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupAllBlocks ) ) { + ItemStack Enabled = createButton(Material.EMERALD_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "All_Blocks Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.REDSTONE_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "All_Blocks Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupCobbleStone ) ) { + ItemStack Enabled = createButton(Material.COBBLESTONE, 1, enabledLore, SpigotPrison.format("&a" + "Cobblestone Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.COBBLESTONE, 1, disabledLore, SpigotPrison.format("&c" + "Cobblestone Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupStone ) ) { + ItemStack Enabled = createButton(Material.STONE, 1, enabledLore, SpigotPrison.format("&a" + "Stone Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.STONE, 1, disabledLore, SpigotPrison.format("&c" + "Stone Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupGoldOre ) ) { + ItemStack Enabled = createButton(Material.GOLD_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Gold_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.GOLD_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Gold_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupIronOre ) ) { + ItemStack Enabled = createButton(Material.IRON_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Iron_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.IRON_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Iron_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupCoalOre ) ) { + ItemStack Enabled = createButton(Material.COAL_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Coal_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.COAL_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Coal_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupDiamondOre ) ) { + ItemStack Enabled = createButton(Material.DIAMOND_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Diamond_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.DIAMOND_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Diamond_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupRedStoneOre ) ) { + ItemStack Enabled = createButton(Material.REDSTONE_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Redstone_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.REDSTONE_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Redstone_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupEmeraldOre ) ) { + ItemStack Enabled = createButton(Material.EMERALD_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Emerald_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.EMERALD_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Emerald_Ore Disabled")); + inv.addItem(Disabled); + } + + Material quartzOre = Material.matchMaterial( "quartz_ore" ); + if ( quartzOre == null ) { + quartzOre = Material.matchMaterial( "nether_quartz_ore" ); + } + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupQuartzOre ) ) { + ItemStack Enabled = createButton(quartzOre, 1, enabledLore, SpigotPrison.format("&a" + "Quartz_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(quartzOre, 1, disabledLore, SpigotPrison.format("&c" + "Quartz_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupLapisOre ) ) { + ItemStack Enabled = createButton(Material.LAPIS_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Lapis_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.LAPIS_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Lapis_Ore Disabled")); + inv.addItem(Disabled); + } + + Material snowBall = Material.matchMaterial( "snow_ball" ); + if ( snowBall == null ) { + snowBall = Material.matchMaterial( "snowball" ); + } + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupSnowBall ) ) { + ItemStack Enabled = createButton(snowBall, 1, enabledLore, SpigotPrison.format("&a" + "Snow_Ball Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(snowBall, 1, disabledLore, SpigotPrison.format("&c" + "Snow_Ball Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoPickupGlowstoneDust ) ) { + ItemStack Enabled = createButton(Material.GLOWSTONE_DUST, 1, enabledLore, SpigotPrison.format("&a" + "Glowstone_Dust Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.GLOWSTONE_DUST, 1, disabledLore, SpigotPrison.format("&c" + "Glowstone_Dust Disabled")); + inv.addItem(Disabled); + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoSmeltGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoSmeltGUI.java new file mode 100644 index 000000000..86970674e --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/autofeatures/SpigotAutoSmeltGUI.java @@ -0,0 +1,92 @@ +package tech.mcprison.prison.spigot.gui.autofeatures; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotAutoSmeltGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotAutoSmeltGUI(Player p){ + this.p = p; + } + + public void open() { + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3AutoFeatures -> AutoSmelt")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Config + AutoFeaturesFileConfig afConfig = SpigotPrison.getInstance().getAutoFeatures().getAutoFeaturesConfig(); + + if (guiBuilder(inv, GuiConfig, afConfig)) return; + + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + try { + buttonsSetup(inv, guiConfig, afConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig, AutoFeaturesFileConfig afConfig) { + List enabledLore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDisable") + ); + + List disabledLore = createLore( + guiConfig.getString("Gui.Lore.RightClickToEnable") + ); + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoSmeltAllBlocks ) ) { + ItemStack Enabled = createButton(Material.EMERALD_BLOCK, 1, enabledLore, SpigotPrison.format("&a" + "All_Ores Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.REDSTONE_BLOCK, 1, disabledLore, SpigotPrison.format("&c" + "All_Ores Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoSmeltGoldOre ) ) { + ItemStack Enabled = createButton(Material.GOLD_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Gold_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.GOLD_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Gold_Ore Disabled")); + inv.addItem(Disabled); + } + + if ( afConfig.isFeatureBoolean( AutoFeatures.autoSmeltIronOre ) ) { + ItemStack Enabled = createButton(Material.IRON_ORE, 1, enabledLore, SpigotPrison.format("&a" + "Iron_Ore Enabled")); + inv.addItem(Enabled); + } else { + ItemStack Disabled = createButton(Material.IRON_ORE, 1, disabledLore, SpigotPrison.format("&c" + "Iron_Ore Disabled")); + inv.addItem(Disabled); + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineBlockPercentageGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineBlockPercentageGUI.java new file mode 100644 index 000000000..2e51bde5d --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineBlockPercentageGUI.java @@ -0,0 +1,131 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotMineBlockPercentageGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + private final Double val; + private final String blockName; + + public SpigotMineBlockPercentageGUI(Player p, Double val, String mineName, String blockName){ + this.p = p; + this.val = val; + this.mineName = mineName; + this.blockName = blockName; + } + + public void open() { + + // Create a new inventory + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MineInfo -> BlockPercentage")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create a new lore + List changeDecreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToDecrease") + ); + + // Create a new lore + List confirmButtonLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToConfirm"), + guiConfig.getString("Gui.Lore.Percentage") + val, + guiConfig.getString("Gui.Lore.RightClickToCancel") + ); + + // Create a new lore + List changeIncreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToIncrease") + ); + + + // Decrease button + ItemStack decreaseOf1 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " - 1" )); + inv.setItem(1, decreaseOf1); + + // Decrease button + ItemStack decreaseOf5 = createButton(Material.REDSTONE_BLOCK, 5, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " - 5")); + inv.setItem(10, decreaseOf5); + + // Decrease button + ItemStack decreaseOf10 = createButton(Material.REDSTONE_BLOCK, 10, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " - 10")); + inv.setItem(19, decreaseOf10); + + // Decrease button + ItemStack decreaseOf50 = createButton(Material.REDSTONE_BLOCK, 50, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " - 50")); + inv.setItem(28, decreaseOf50); + + // Decrease button + ItemStack decreaseOf100 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " - 100")); + inv.setItem(37, decreaseOf100); + + + // Create a button and set the position + Material watch = Material.matchMaterial( "watch" ); + if ( watch == null ) { + watch = Material.matchMaterial( "legacy_watch" ); + } if ( watch == null ) { + watch = Material.matchMaterial( "clock" ); + } + ItemStack confirmButton = createButton(watch, 1, confirmButtonLore, SpigotPrison.format("&3" + "Confirm: " + mineName + " " + blockName + " " + val)); + inv.setItem(22, confirmButton); + + + // Increase button + ItemStack increseOf1 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " + 1" )); + inv.setItem(7, increseOf1); + + // Increase button + ItemStack increaseOf5 = createButton(Material.EMERALD_BLOCK, 5, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " + 5")); + inv.setItem(16, increaseOf5); + + // Increase button + ItemStack increaseOf10 = createButton(Material.EMERALD_BLOCK, 10, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " + 10")); + inv.setItem(25, increaseOf10); + + // Increase button + ItemStack increaseOf50 = createButton(Material.EMERALD_BLOCK, 50, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " + 50")); + inv.setItem(34, increaseOf50); + + // Increase button + ItemStack increaseOf100 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + blockName + " " + val + " + 100")); + inv.setItem(43, increaseOf100); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineInfoGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineInfoGUI.java new file mode 100644 index 000000000..b47837d73 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineInfoGUI.java @@ -0,0 +1,150 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotMineInfoGUI extends SpigotGUIComponents { + + private final Player p; + private final Mine mine; + private final String mineName; + + public SpigotMineInfoGUI(Player p, Mine mine, String mineName){ + this.p = p; + this.mine = mine; + this.mineName = mineName; + } + + public void open(){ + + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3Mines -> MineInfo")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Opens the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // The Reset Mine button and lore + List resetminelore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToReset"), + "", + guiConfig.getString("Gui.Lore.RightClickToToggle"), + guiConfig.getString("Gui.Lore.SkipReset1"), + guiConfig.getString("Gui.Lore.SkipReset2"), + guiConfig.getString("Gui.Lore.SkipReset3"), + "", + guiConfig.getString("Gui.Lore.ShiftAndRightClickToToggle"), + guiConfig.getString("Gui.Message.ZeroBlocksReset1"), + guiConfig.getString("Gui.Message.ZeroBlocksReset2"), + guiConfig.getString("Gui.Message.ZeroBlocksReset3") + ); + + // Set the Mine spawn at your location + List MineSpawnlore = createLore( + guiConfig.getString("Gui.Lore.ClickToUse"), + guiConfig.getString("Gui.Lore.SpawnPoint2") + ); + + // Lore and button + List MinesNotificationsLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + guiConfig.getString("Gui.Lore.Notifications") + ); + + // Lore and button + List MinesTpLore = createLore( + guiConfig.getString("Gui.Lore.ClickToTeleport"), + guiConfig.getString("Gui.Lore.Tp") + ); + + // Blocks of the mine button and lore + List blocksoftheminelore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + guiConfig.getString("Gui.Lore.Blocks2")); + + // Blocks of the mine button and lore + List mineResetTimeLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + guiConfig.getString("Gui.Lore.ManageResetTime"), + guiConfig.getString("Gui.Lore.ResetTime") + mine.getResetTime()); + + // Create the button, set up the material, amount, lore and name + ItemStack resetmine = createButton(Material.EMERALD_BLOCK, 1, resetminelore, SpigotPrison.format("&3" + "Reset_Mine: " + mineName)); + + // Create the button + ItemStack MineSpawn = createButton(Material.COMPASS, 1, MineSpawnlore, SpigotPrison.format("&3" + "Mine_Spawn: " + mineName)); + + // Create the button + ItemStack MinesNotifications = createButton(Material.SIGN, 1, MinesNotificationsLore, SpigotPrison.format("&3" + "Mine_notifications: " + mineName)); + + // Create the button + Material bed = Material.matchMaterial( "bed" ); + if ( bed == null ) { + bed = Material.matchMaterial( "Red_Bed" ); + } + ItemStack MinesTP = createButton(bed, 1, MinesTpLore, SpigotPrison.format("&3" + "TP_to_the_Mine: " + mineName)); + + // Create the button, set up the material, amount, lore and name + ItemStack blocksofthemine = createButton(Material.COAL_ORE, 1, blocksoftheminelore, SpigotPrison.format("&3" + "Blocks_of_the_Mine: " + mineName)); + + // Create the button, set up the material, amount, lore and name + Material watch = Material.matchMaterial( "watch" ); + if ( watch == null ) { + watch = Material.matchMaterial( "legacy_watch" ); + } if ( watch == null ) { + watch = Material.matchMaterial( "clock" ); + } + ItemStack mineResetTime = createButton(watch, 1, mineResetTimeLore, SpigotPrison.format("&3" + "Reset_Time: " + mineName)); + + // Position of the button + inv.setItem(10, resetmine); + + // Position of the button + inv.setItem(13, MineSpawn); + + // Position of the button + inv.setItem(16, MinesNotifications); + + // Position of the button + inv.setItem(28, MinesTP); + + // Position of the button + inv.setItem(31, blocksofthemine); + + // Position of the button + inv.setItem(34, mineResetTime); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationRadiusGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationRadiusGUI.java new file mode 100644 index 000000000..fa0b375b9 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationRadiusGUI.java @@ -0,0 +1,130 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotMineNotificationRadiusGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + private final long val; + private final String typeNotification; + + public SpigotMineNotificationRadiusGUI(Player p, Long val, String typeNotification, String mineName){ + this.p = p; + this.val = val; + this.mineName = mineName; + this.typeNotification = typeNotification; + } + + public void open() { + + // Create a new inventory + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MineNotifications -> Radius")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create new lore + List changeDecreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToDecrease") + ); + + // Create a new lore + List confirmButtonLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToConfirm"), + guiConfig.getString("Gui.Lore.Radius") + val, + guiConfig.getString("Gui.Lore.RightClickToCancel") + ); + + // Create a new lore + List changeIncreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToIncrease") + ); + + // Decrease buttons + ItemStack decreaseOf1 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 1 " + typeNotification )); + inv.setItem(1, decreaseOf1); + + // Decrease buttons + ItemStack decreaseOf5 = createButton(Material.REDSTONE_BLOCK, 5, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 5 " + typeNotification)); + inv.setItem(10, decreaseOf5); + + // Decrease buttons + ItemStack decreaseOf10 = createButton(Material.REDSTONE_BLOCK, 10, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 10 " + typeNotification)); + inv.setItem(19, decreaseOf10); + + // Decrease buttons + ItemStack decreaseOf50 = createButton(Material.REDSTONE_BLOCK, 50, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 50 " + typeNotification)); + inv.setItem(28, decreaseOf50); + + // Decrease buttons + ItemStack decreaseOf100 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 100 " + typeNotification)); + inv.setItem(37, decreaseOf100); + + + // Create a button and set the position of it + Material watch = Material.matchMaterial( "watch" ); + if ( watch == null ) { + watch = Material.matchMaterial( "legacy_watch" ); + } if ( watch == null ) { + watch = Material.matchMaterial( "clock" ); + } + ItemStack confirmButton = createButton(watch, 1, confirmButtonLore, SpigotPrison.format("&3" + "Confirm: " + mineName + " " + val + " " + typeNotification)); + inv.setItem(22, confirmButton); + + + // Increase buttons + ItemStack increseOf1 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 1 " + typeNotification)); + inv.setItem(7, increseOf1); + + // Increase buttons + ItemStack increaseOf5 = createButton(Material.EMERALD_BLOCK, 5, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 5 " + typeNotification)); + inv.setItem(16, increaseOf5); + + // Increase buttons + ItemStack increaseOf10 = createButton(Material.EMERALD_BLOCK, 10, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 10 " + typeNotification)); + inv.setItem(25, increaseOf10); + + // Increase buttons + ItemStack increaseOf50 = createButton(Material.EMERALD_BLOCK, 50, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 50 " + typeNotification)); + inv.setItem(34, increaseOf50); + + // Increase buttons + ItemStack increaseOf100 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 100 " + typeNotification)); + inv.setItem(43, increaseOf100); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationsGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationsGUI.java new file mode 100644 index 000000000..c915e09d7 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineNotificationsGUI.java @@ -0,0 +1,152 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotMineNotificationsGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + + public SpigotMineNotificationsGUI(Player p, String mineName){ + this.p = p; + this.mineName = mineName; + } + + public void open() { + + // Create a new inventory + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MineInfo -> MineNotifications")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Init variables + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + String enabledOrDisabled = m.getNotificationMode().name(); + + if (guiBuilder(inv, GuiConfig, enabledOrDisabled)) return; + + // Opens the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig, String enabledOrDisabled) { + try { + buttonsSetup(inv, guiConfig, enabledOrDisabled); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig, String enabledOrDisabled) { + // Create a new lore + List modeWithinLore = createLore( + guiConfig.getString("Gui.Lore.ClickToChoose"), + guiConfig.getString("Gui.Lore.ActivateWithinMode")); + + // Create a new lore + List modeRadiusLore = createLore( + guiConfig.getString("Gui.Lore.ClickToChoose"), + guiConfig.getString("Gui.Lore.ActivateRadiusMode")); + + // Create a new lore + List disabledModeLore = createLore( + guiConfig.getString("Gui.Lore.ClickToChoose"), + guiConfig.getString("Gui.Lore.DisableNotifications")); + + // Add the selected lore to the mode used + if (enabledOrDisabled.equalsIgnoreCase("disabled")){ + + // Add the selected lore + disabledModeLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Selected"))); + + } else if (enabledOrDisabled.equalsIgnoreCase("within")){ + + // Add the selected lore + modeWithinLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Selected"))); + + } else if (enabledOrDisabled.equalsIgnoreCase("radius")){ + + // Add the selected lore + modeRadiusLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Selected"))); + + } + + // Create a button + ItemStack modeWithin = createButton(Material.IRON_DOOR, 1, modeWithinLore, SpigotPrison.format("&3Within_Mode: " + mineName)); + + // Create a button + Material fence = Material.matchMaterial( "fence" ); + if ( fence == null ) { + fence = Material.matchMaterial( "oak_fence" ); + } + ItemStack radiusMode = createButton(fence, 1, modeRadiusLore, SpigotPrison.format("&3Radius_Mode: " + mineName)); + + // Create a button + ItemStack disabledMode = createButton(Material.REDSTONE_BLOCK, 1, disabledModeLore, SpigotPrison.format("&3Disabled_Mode: " + mineName)); + + // Check which buttons should be added, based on the mode already in use of the Mine Notifications + if (enabledOrDisabled.equalsIgnoreCase("disabled")){ + + // Add a button to the inventory + inv.setItem( 11, modeWithin); + + // Add a button to the inventory + inv.setItem(13, radiusMode); + + // Add a button to the inventory + disabledMode.addUnsafeEnchantment(Enchantment.LUCK, 1); + inv.setItem(15, disabledMode); + + // Check which buttons should be added, based on the mode already in use of the Mine Notifications + } else if (enabledOrDisabled.equalsIgnoreCase("within")){ + + // Add a button to the inventory + modeWithin.addUnsafeEnchantment(Enchantment.LUCK, 1); + inv.setItem(11, modeWithin); + + // Add a button to the inventory + inv.setItem(13, radiusMode); + + // Add a button to the inventory + inv.setItem(15, disabledMode); + + // Check which buttons should be added, based on the mode already in use of the Mine Notifications + } else if (enabledOrDisabled.equalsIgnoreCase("radius")){ + + // Add a button to the inventory + inv.setItem( 11, modeWithin); + + // Add a button to the inventory + radiusMode.addUnsafeEnchantment(Enchantment.LUCK, 1); + inv.setItem( 13, radiusMode); + + // Add a button to the inventory + inv.setItem(15, disabledMode); + + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineResetTimeGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineResetTimeGUI.java new file mode 100644 index 000000000..007cbc818 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMineResetTimeGUI.java @@ -0,0 +1,129 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotMineResetTimeGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + private final Integer val; + + public SpigotMineResetTimeGUI(Player p, Integer val, String mineName){ + this.p = p; + this.val = val; + this.mineName = mineName; + } + + public void open() { + + // Create a new inventory + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MineInfo -> ResetTime")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create a new lore + List changeDecreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToDecrease") + ); + + // Create a new lore + List confirmButtonLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToConfirm"), + guiConfig.getString("Gui.Lore.Time") + val, + guiConfig.getString("Gui.Lore.RightClickToCancel") + ); + + // Create a new lore + List changeIncreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToIncrease") + ); + + + // Decrease button + ItemStack decreaseOf1 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 1" )); + inv.setItem(1, decreaseOf1); + + // Decrease button + ItemStack decreaseOf5 = createButton(Material.REDSTONE_BLOCK, 5, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 5")); + inv.setItem(10, decreaseOf5); + + // Decrease button + ItemStack decreaseOf10 = createButton(Material.REDSTONE_BLOCK, 10, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 10")); + inv.setItem(19, decreaseOf10); + + // Decrease button + ItemStack decreaseOf50 = createButton(Material.REDSTONE_BLOCK, 50, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 50")); + inv.setItem(28, decreaseOf50); + + // Decrease button + ItemStack decreaseOf100 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " - 100")); + inv.setItem(37, decreaseOf100); + + + // Create a button and set the position + Material watch = Material.matchMaterial( "watch" ); + if ( watch == null ) { + watch = Material.matchMaterial( "legacy_watch" ); + } if ( watch == null ) { + watch = Material.matchMaterial( "clock" ); + } + ItemStack confirmButton = createButton(watch, 1, confirmButtonLore, SpigotPrison.format("&3" + "Confirm: " + mineName + " " + val)); + inv.setItem(22, confirmButton); + + + // Increase button + ItemStack increseOf1 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 1" )); + inv.setItem(7, increseOf1); + + // Increase button + ItemStack increaseOf5 = createButton(Material.EMERALD_BLOCK, 5, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 5")); + inv.setItem(16, increaseOf5); + + // Increase button + ItemStack increaseOf10 = createButton(Material.EMERALD_BLOCK, 10, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 10")); + inv.setItem(25, increaseOf10); + + // Increase button + ItemStack increaseOf50 = createButton(Material.EMERALD_BLOCK, 50, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 50")); + inv.setItem(34, increaseOf50); + + // Increase button + ItemStack increaseOf100 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + mineName + " " + val + " + 100")); + inv.setItem(43, increaseOf100); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesBlocksGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesBlocksGUI.java new file mode 100644 index 000000000..552f6e680 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesBlocksGUI.java @@ -0,0 +1,210 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.data.Block; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotMinesBlocksGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + + public SpigotMinesBlocksGUI(Player p, String mineName){ + this.p = p; + this.mineName = mineName; + } + + public void open(){ + + // Get the variables + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); + + // Get the dimensions and if needed increases them + int dimension; + + boolean useNewBlockModel = Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ); + + if ( useNewBlockModel ) { + dimension = (int) Math.ceil(m.getPrisonBlocks().size() / 9D) * 9; + } + else { + dimension = (int) Math.ceil(m.getBlocks().size() / 9D) * 9; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoBlocksMine"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyBlocks"))); + p.closeInventory(); + return; + } + + // Create the inventory + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MineInfo -> Blocks")); + + if ( useNewBlockModel ) { + + + // For every block makes a button + for (PrisonBlock block : m.getPrisonBlocks()) { + + // Get the block material as a string + String blockmaterial = block.getBlockName(); + + // Display title of the item + String blockmaterialdisplay = blockmaterial; + + // Check if a block's air and changed the item of it to BARRIER + if (blockmaterial.equalsIgnoreCase("air")){ + blockmaterial = "BARRIER"; + blockmaterialdisplay = blockmaterial; + } + + if (guiBuilder(GuiConfig, inv, block, blockmaterial, blockmaterialdisplay)) return; + + } + } + else { + + // For every block makes a button + for (Block block : m.getBlocks()) { + + // Get the block material as a string + String blockmaterial = block.getType().name(); + + // Display title of the item + String blockmaterialdisplay = blockmaterial; + + // Check if a block's air and changed the item of it to BARRIER + if (blockmaterial.equalsIgnoreCase("air")){ + blockmaterial = "BARRIER"; + blockmaterialdisplay = blockmaterial; + } + + if (guiBuilder(GuiConfig, inv, block, blockmaterial, blockmaterialdisplay)) return; + + } + } + + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, PrisonBlock block, String blockmaterial, String blockmaterialdisplay) { + try { + buttonsSetup(guiConfig, inv, block, blockmaterial, blockmaterialdisplay); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, Block block, String blockmaterial, String blockmaterialdisplay) { + try { + buttonsSetup(guiConfig, inv, block, blockmaterial, blockmaterialdisplay); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, PrisonBlock block, String blockmaterial, String blockmaterialdisplay) { + // Create the lore + List blockslore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete"), + guiConfig.getString("Gui.Lore.ClickToEditBlock"), + "", + guiConfig.getString("Gui.Lore.Info")); + + + boolean isEnum = true; + try { + Material.valueOf(blockmaterial); + } catch (Exception e) { + isEnum = false; + } + + if (!(isEnum)) { + blockmaterial = "BARRIER"; + } + + // Add a lore + blockslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Chance") + block.getChance() + "%")); + + // Add a lore + blockslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.BlockType") + blockmaterial)); + + // Make the item + ItemStack block1 = createButton(Material.valueOf(blockmaterial), 1, blockslore, SpigotPrison.format("&3" + blockmaterialdisplay + " " + mineName + " " + block.getChance())); + + // Add the item to the inventory + inv.addItem(block1); + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, Block block, String blockmaterial, String blockmaterialdisplay) { + // Create the lore + List blockslore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete"), + guiConfig.getString("Gui.Lore.ClickToEditBlock"), + "", + guiConfig.getString("Gui.Lore.Info")); + + + boolean isEnum = true; + try { + Material.valueOf(blockmaterial); + } catch (Exception e) { + isEnum = false; + } + + if (!(isEnum)) { + blockmaterial = "BARRIER"; + } + + // Add a lore + blockslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Chance") + block.getChance() + "%")); + + // Add a lore + blockslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.BlockType") + blockmaterial)); + + // Make the item + ItemStack block1 = createButton(Material.valueOf(blockmaterial), 1, blockslore, SpigotPrison.format("&3" + blockmaterialdisplay + " " + mineName + " " + block.getChance())); + + // Add the item to the inventory + inv.addItem(block1); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesConfirmGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesConfirmGUI.java new file mode 100644 index 000000000..2b3d0a9f1 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesConfirmGUI.java @@ -0,0 +1,77 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotMinesConfirmGUI extends SpigotGUIComponents { + + private final Player p; + private final String mineName; + + public SpigotMinesConfirmGUI(Player p, String mineName) { + this.p = p; + this.mineName = mineName; + } + + public void open(){ + + // Create the inventory + int dimension = 9; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3Mines -> Delete")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Blocks of the mine + List confirmlore = createLore( + guiConfig.getString("Gui.Lore.ClickToConfirm")); + + // Blocks of the mine + List cancelore = createLore( + guiConfig.getString("Gui.Lore.ClickToCancel")); + + // Create the button, set up the material, amount, lore and name + ItemStack confirm = createButton(Material.EMERALD_BLOCK, 1, confirmlore, SpigotPrison.format("&3" + "Confirm: " + mineName)); + + // Create the button, set up the material, amount, lore and name + ItemStack cancel = createButton(Material.REDSTONE_BLOCK, 1, cancelore, SpigotPrison.format("&3" + "Cancel: " + mineName)); + + // Position of the button + inv.setItem(2, confirm); + + // Position of the button + inv.setItem(6, cancel); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesGUI.java new file mode 100644 index 000000000..722ab7f89 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotMinesGUI.java @@ -0,0 +1,159 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.internal.block.PrisonBlock; +import tech.mcprison.prison.mines.data.Block; +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.mines.data.PrisonSortableMines; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotMinesGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotMinesGUI(Player p) { + this.p = p; + } + + public void open(){ + + // Init the ItemStack + // ItemStack itemMines; + + // Get the mines + Set mines = new PrisonSortableMines().getSortedSet(); + // PrisonMines pMines = PrisonMines.getInstance(); + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(mines.size() / 9D) * 9; + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoMines"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyMines"))); + p.closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3MinesManager -> Mines")); + + // Make the buttons for every Mine with info + for (Mine m : mines ) { + + if (guiBuilder(GuiConfig, inv, m)) return; + + } + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, Mine m) { + try { + buttonsSetup(guiConfig, inv, m); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, Mine m) { + ItemStack itemMines; + // Init the lore array with default values for ladders + List minesLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToOpen"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete"), + "", + guiConfig.getString("Gui.Lore.Info")); + + // Add a lore + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.World") + m.getWorldName())); + + // Init a variable and add it to the lore + String spawnPoint = m.getSpawn() != null ? m.getSpawn().toBlockCoordinates() : "&cnot set"; + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.SpawnPoint") + spawnPoint)); + + // Add a lore + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.ResetTime") + m.getResetTime())); + + // Add a lore + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.SizeOfMine") + m.getBounds().getDimensions())); + + // Add a lore + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Volume") + m.getBounds().getTotalBlockCount())); + + // Add a lore + minesLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Blocks"))); + + // Init some variables and do the actions + DecimalFormat dFmt = new DecimalFormat("##0.00"); + double totalChance = 0.0d; + + boolean useNewBlockModel = Prison.get().getPlatform().getConfigBooleanFalse( "use-new-prison-block-model" ); + + if ( useNewBlockModel ) { + + for (PrisonBlock block : m.getPrisonBlocks()) { + double chance = Math.round(block.getChance() * 100.0d) / 100.0d; + totalChance += chance; + + String blockName = + StringUtils.capitalize(block.getBlockName().replaceAll("_", " ").toLowerCase()); + minesLore.add(SpigotPrison.format("&7" + chance + "% - " + block.getBlockName() + " (" + blockName + ")")); + } + } + else { + + for (Block block : m.getBlocks()) { + double chance = Math.round(block.getChance() * 100.0d) / 100.0d; + totalChance += chance; + + String blockName = + StringUtils.capitalize(block.getType().name().replaceAll("_", " ").toLowerCase()); + minesLore.add(SpigotPrison.format("&7" + chance + "% - " + block.getType().name() + " (" + blockName + ")")); + } + } + + + if (totalChance < 100.0d) { + minesLore.add(SpigotPrison.format("&e " + dFmt.format(100.0d - totalChance) + "% - Air")); + } + + // Create the button + itemMines = createButton(Material.COAL_ORE, 1, minesLore, SpigotPrison.format("&3" + m.getName())); + + // Add the button to the inventory + inv.addItem(itemMines); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotPlayerMinesGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotPlayerMinesGUI.java new file mode 100644 index 000000000..fe1fd0312 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/mine/SpigotPlayerMinesGUI.java @@ -0,0 +1,107 @@ +package tech.mcprison.prison.spigot.gui.mine; + +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.mines.data.Mine; +import tech.mcprison.prison.mines.data.PrisonSortableMines; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +public class SpigotPlayerMinesGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotPlayerMinesGUI(Player p) { + this.p = p; + } + + public void open(){ + + // Init the ItemStack + // ItemStack itemMines; + + // Get the mines + Set mines = new PrisonSortableMines().getSortedSet(); + //PrisonMines pMines = PrisonMines.getInstance(); + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(mines.size() / 9D) * 9; + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoMines"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyMines"))); + p.closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3Mines -> PlayerMines")); + + // Make the buttons for every Mine with info + for (Mine m : mines) { + + // Init the lore array with default values for ladders + List minesLore = createLore( + ); + + if (guiBuilder(GuiConfig, inv, m, minesLore)) return; + + } + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, Mine m, List mineslore) { + try { + buttonsSetup(guiConfig, inv, m, mineslore); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, Mine m, List mineslore) { + ItemStack itemMines; + Material material; + String permission = SpigotPrison.format(guiConfig.getString("Options.Mines.PermissionWarpPlugin")); + + if (p.hasPermission(permission + m.getName()) || p.hasPermission(permission.substring(0, permission.length() - 1))){ + material = Material.COAL_ORE; + mineslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.StatusUnlockedMine"))); + mineslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.ClickToTeleport"))); + } else { + material = Material.REDSTONE_BLOCK; + mineslore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.StatusLockedMine"))); + } + + // Create the button + itemMines = createButton(material, 1, mineslore, SpigotPrison.format("&3" + m.getName())); + + // Add the button to the inventory + inv.addItem(itemMines); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotConfirmPrestigeGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotConfirmPrestigeGUI.java new file mode 100644 index 000000000..bf1d9576b --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotConfirmPrestigeGUI.java @@ -0,0 +1,76 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +public class SpigotConfirmPrestigeGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotConfirmPrestigeGUI(Player p) { + this.p = p; + } + + public void open(){ + + // Create the inventory + int dimension = 9; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3Prestige -> Confirmation")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Blocks of the mine + List confirmLore = createLore( + guiConfig.getString("Gui.Lore.ClickToConfirm"), + guiConfig.getString("Gui.Lore.PrestigeWarning"), + guiConfig.getString("Gui.Lore.PrestigeWarning2"), + guiConfig.getString("Gui.Lore.PrestigeWarning3") + ); + + // Blocks of the mine + List cancelLore = createLore( + guiConfig.getString("Gui.Lore.ClickToCancel")); + + // Create the button, set up the material, amount, lore and name + ItemStack confirm = createButton(Material.EMERALD_BLOCK, 1, confirmLore, SpigotPrison.format("&3" + "Confirm: Prestige")); + + // Create the button, set up the material, amount, lore and name + ItemStack cancel = createButton(Material.REDSTONE_BLOCK, 1, cancelLore, SpigotPrison.format("&3" + "Cancel: Don't Prestige")); + + // Position of the button + inv.setItem(2, confirm); + + // Position of the button + inv.setItem(6, cancel); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotLaddersGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotLaddersGUI.java new file mode 100644 index 000000000..8c55a4fd0 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotLaddersGUI.java @@ -0,0 +1,103 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.managers.LadderManager; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotLaddersGUI extends SpigotGUIComponents { + + private final Player p; + + public SpigotLaddersGUI(Player p){ + this.p = p; + } + + public void open(){ + + // Init the ItemStack + // ItemStack itemLadder; + + // Check if Ranks are enabled + if (!(checkRanks(p))){ + return; + } + + // Init variable + LadderManager lm = PrisonRanks.getInstance().getLadderManager(); + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(lm.getLadders().size() / 9D) * 9; + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoLadders"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyLadders"))); + p.closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3RanksManager -> Ladders")); + + // Make for every ladder a button + for (RankLadder ladder : lm.getLadders()){ + + if (guiBuilder(GuiConfig, inv, ladder)) return; + + } + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, RankLadder ladder) { + try { + buttonsSetup(guiConfig, inv, ladder); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, RankLadder ladder) { + ItemStack itemLadder; + // Init the lore array with default values for ladders + List laddersLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete")); + + // Create the button + itemLadder = createButton(Material.LADDER, 1, laddersLore, SpigotPrison.format("&3" + ladder.name)); + + // Add the button to the inventory + inv.addItem(itemLadder); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerPrestigesGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerPrestigesGUI.java new file mode 100644 index 000000000..baa4cc22e --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerPrestigesGUI.java @@ -0,0 +1,212 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.configuration.Configuration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleManager; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.managers.LadderManager; +import tech.mcprison.prison.ranks.managers.PlayerManager; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * @author GABRYCA + */ +public class SpigotPlayerPrestigesGUI extends SpigotGUIComponents { + + private final Player player; + + private PrisonRanks rankPlugin; + private RankPlayer rankPlayer; + + public SpigotPlayerPrestigesGUI(Player player) { + this.player = player; + + // If you need to get a SpigotPlayer: + // SpigotPlayer sPlayer = new SpigotPlayer(p); + + Server server = SpigotPrison.getInstance().getServer(); + + PrisonRanks rankPlugin; + RankPlayer rPlayer; + + ModuleManager modMan = Prison.get().getModuleManager(); + Module module = modMan == null ? null : modMan.getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + + rankPlugin = (PrisonRanks) module; + + if (rankPlugin == null){ + player.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cLooks like the Ranks module's disabled")); + return; + } + + if (rankPlugin.getPlayerManager() == null) { + return; + } + + PlayerManager playerManager = rankPlugin.getPlayerManager(); + + rPlayer = playerManager.getPlayer( player.getUniqueId(), player.getName() ).orElse( null ); + LadderManager lm = rankPlugin.getLadderManager(); + + for ( RankLadder ladderData : lm.getLadders() ) { +// Rank playerRank = rPlayer == null ? null : rPlayer.getRank( ladderData ).orElse( null ); + Rank rank = ladderData.getLowestRank().orElse( null ); + + while ( rank != null ) { +// boolean playerHasThisRank = playerRank != null && playerRank.equals( rank ); + + rank = rank.rankNext; + } + } + + Plugin plugin = server.getPluginManager().getPlugin( PrisonRanks.MODULE_NAME ); + if (plugin instanceof PrisonRanks) { + rankPlugin = (PrisonRanks) plugin; + Optional oPlayer = rankPlugin.getPlayerManager(). + getPlayer( getPlayer().getUniqueId(), getPlayer().getName() ); + if ( oPlayer.isPresent() ) { + rPlayer = oPlayer.get(); + } + } + this.rankPlugin = rankPlugin; + this.rankPlayer = rPlayer; + + } + + public Player getPlayer() { + return player; + } + + public PrisonRanks getRankPlugin() { + return rankPlugin; + } + + public RankPlayer getRankPlayer() { + return rankPlayer; + } + + public void open() { + + // First ensure the ranks module is enabled: + if ( getRankPlugin() == null ) { + // Error? Cannot open if Rank module is not loaded. + getPlayer().closeInventory(); + return; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + LadderManager lm = getRankPlugin().getLadderManager(); + Optional ladder = lm.getLadder("prestiges"); + + // Ensure ladder is present and that it has a rank: + if ( !ladder.isPresent() || !ladder.get().getLowestRank().isPresent() ){ + getPlayer().closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = (int) (Math.ceil(ladder.get().ranks.size() / 9D) * 9) + 9; + + // Create an inventory + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3" + "Prestiges -> PlayerPrestiges")); + + // guiBuilder and validation + if (guiBuilder(GuiConfig, ladder, dimension, inv)) return; + + // Open the inventory + getPlayer().openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(getPlayer()); + } + + private boolean guiBuilder(Configuration guiConfig, Optional ladder, int dimension, Inventory inv) { + try { + buttonsSetup(guiConfig, ladder, dimension, inv); + } catch (NullPointerException ex){ + getPlayer().sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Optional ladder, int dimension, Inventory inv) { + + if (!ladder.isPresent()){ + player.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.LadderPrestigesNotFound"))); + return; + } + + RankLadder ladderData = ladder.get(); + + if (!ladderData.getLowestRank().isPresent()){ + player.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.NoRanksPrestigesLadder"))); + return; + } + + Rank rank = ladderData.getLowestRank().get(); + + Rank playerRank = getRankPlayer().getRank( ladderData ).orElse( null ); + + // Not sure how you want to represent this: + Material materialHas = Material.getMaterial(Objects.requireNonNull(guiConfig.getString("Options.Ranks.Item_gotten_rank"))); + Material materialHasNot = Material.getMaterial(Objects.requireNonNull(guiConfig.getString("Options.Ranks.Item_not_gotten_rank"))); + + boolean playerHasThisRank = true; + int hackyCounterEnchant = 0; + + int amount = 1; + while ( rank != null ) { + + List ranksLore = createLore( + guiConfig.getString("Gui.Lore.Info"), + guiConfig.getString("Gui.Lore.Price3") + rank.cost + ); + ItemStack itemrank = createButton( + (playerHasThisRank ? materialHas : materialHasNot), + amount++, ranksLore, SpigotPrison.format(rank.tag)); + if (playerRank != null && playerRank.equals( rank )){ + playerHasThisRank = false; + } + if (!(playerHasThisRank)){ + if (hackyCounterEnchant <= 0) { + hackyCounterEnchant++; + if (Objects.requireNonNull(guiConfig.getString("Options.Ranks.Enchantment_effect_current_rank")).equalsIgnoreCase("true")) { + itemrank.addUnsafeEnchantment(Enchantment.LUCK, 1); + } + } + } + inv.addItem(itemrank); + + rank = rank.rankNext; + } + + List rankupLore = createLore( + guiConfig.getString("Gui.Lore.IfYouHaveEnoughMoney"), + guiConfig.getString("Gui.Lore.ClickToRankup") + ); + + ItemStack rankupButton = createButton(Material.EMERALD_BLOCK, 1, rankupLore, SpigotPrison.format("&aPrestige")); + inv.setItem(dimension - 5, rankupButton); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerRanksGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerRanksGUI.java new file mode 100644 index 000000000..18dbafc67 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotPlayerRanksGUI.java @@ -0,0 +1,204 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.configuration.Configuration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleManager; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.ranks.managers.LadderManager; +import tech.mcprison.prison.ranks.managers.PlayerManager; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotPlayerRanksGUI extends SpigotGUIComponents { + + private final Player player; + + private PrisonRanks rankPlugin; + private RankPlayer rankPlayer; + + public SpigotPlayerRanksGUI(Player player) { + this.player = player; + + // If you need to get a SpigotPlayer: + // SpigotPlayer sPlayer = new SpigotPlayer(p); + + Server server = SpigotPrison.getInstance().getServer(); + + PrisonRanks rankPlugin; + RankPlayer rPlayer; + + ModuleManager modMan = Prison.get().getModuleManager(); + Module module = modMan == null ? null : modMan.getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + + // Check + if (!(checkRanks(player))){ + return; + } + + rankPlugin = (PrisonRanks) module; + + if (rankPlugin == null){ + player.sendMessage(SpigotPrison.format("&cError: rankPlugin == null")); + return; + } + + if (rankPlugin.getPlayerManager() == null) { + player.sendMessage(SpigotPrison.format("&cError: rankPlugin.getPlayerManager() == null")); + return; + } + + PlayerManager playerManager = rankPlugin.getPlayerManager(); + + rPlayer = playerManager.getPlayer( player.getUniqueId(), player.getName() ).orElse( null ); + + Plugin plugin = server.getPluginManager().getPlugin( PrisonRanks.MODULE_NAME ); + if (plugin instanceof PrisonRanks) { + rankPlugin = (PrisonRanks) plugin; + Optional oPlayer = rankPlugin.getPlayerManager(). + getPlayer( getPlayer().getUniqueId(), getPlayer().getName() ); + if ( oPlayer.isPresent() ) { + rPlayer = oPlayer.get(); + } + } + this.rankPlugin = rankPlugin; + this.rankPlayer = rPlayer; + + } + + public Player getPlayer() { + return player; + } + + public PrisonRanks getRankPlugin() { + return rankPlugin; + } + + public RankPlayer getRankPlayer() { + return rankPlayer; + } + + public void open() { + + // First ensure the ranks module is enabled: + if ( getRankPlugin() == null ) { + // Error? Cannot open if Rank module is not loaded. + getPlayer().closeInventory(); + return; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + LadderManager lm = getRankPlugin().getLadderManager(); + Optional ladder = lm.getLadder(GuiConfig.getString("Options.Ranks.Ladder")); + + // Ensure ladder is present and that it has a rank: + if ( !ladder.isPresent() || !ladder.get().getLowestRank().isPresent() ){ + getPlayer().sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoRanksFoundHelp1") + GuiConfig.getString("Options.Ranks.Ladder") + GuiConfig.getString("Gui.Message.NoRanksFoundHelp2"))); + getPlayer().closeInventory(); + return; + } + + // Get the dimensions and if needed increases them + if (ladder.get().ranks.size() == 0) { + getPlayer().sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoRanksFound"))); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = (int) (Math.ceil(ladder.get().ranks.size() / 9D) * 9) + 9; + + Configuration guiConfig = SpigotPrison.getGuiConfig(); + + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3" + "Ranks -> PlayerRanks")); + + RankLadder ladderData = ladder.get(); + + Rank rank = ladderData.getLowestRank().get(); + + Rank playerRank = getRankPlayer().getRank( ladderData ).orElse( null ); + + if (guiBuilder(GuiConfig, dimension, guiConfig, inv, rank, playerRank)) return; + + // Open the inventory + getPlayer().openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(getPlayer()); + } + + private boolean guiBuilder(Configuration guiConfig, int dimension, Configuration guiConfig2, Inventory inv, Rank rank, Rank playerRank) { + try { + buttonsSetup(guiConfig, dimension, guiConfig2, inv, rank, playerRank); + } catch (NullPointerException ex){ + getPlayer().sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, int dimension, Configuration guiConfig2, Inventory inv, Rank rank, Rank playerRank) { + // Not sure how you want to represent this: + Material materialHas; + materialHas = Material.getMaterial(Objects.requireNonNull(guiConfig.getString("Options.Ranks.Item_gotten_rank"))); + Material materialHasNot = Material.getMaterial(Objects.requireNonNull(guiConfig.getString("Options.Ranks.Item_not_gotten_rank"))); + + boolean playerHasThisRank = true; + int hackyCounterEnchant = 0; + + int amount = 1; + while ( rank != null ) { + + List ranksLore = createLore( + guiConfig2.getString("Gui.Lore.Info"), + guiConfig2.getString("Gui.Lore.Price3") + rank.cost + ); + ItemStack itemRank = createButton( + (playerHasThisRank ? materialHas : materialHasNot), + amount++, ranksLore, SpigotPrison.format(rank.tag)); + if (playerRank != null && playerRank.equals(rank)){ + playerHasThisRank = false; + } + if (!(playerHasThisRank)){ + if (hackyCounterEnchant <= 0) { + hackyCounterEnchant++; + if (Objects.requireNonNull(guiConfig2.getString("Options.Ranks.Enchantment_effect_current_rank")).equalsIgnoreCase("true")) { + itemRank.addUnsafeEnchantment(Enchantment.LUCK, 1); + } + } + } + inv.addItem(itemRank); + + rank = rank.rankNext; + } + + List rankupLore = createLore( + guiConfig.getString("Gui.Lore.IfYouHaveEnoughMoney"), + guiConfig.getString("Gui.Lore.ClickToRankup") + ); + + ItemStack rankupButton = createButton(Material.EMERALD_BLOCK, 1, rankupLore, SpigotPrison.format(guiConfig.getString("Gui.Lore.Rankup"))); + inv.setItem(dimension - 5, rankupButton); + } + +} \ No newline at end of file diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankManagerGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankManagerGUI.java new file mode 100644 index 000000000..27e1525c6 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankManagerGUI.java @@ -0,0 +1,111 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotRankManagerGUI extends SpigotGUIComponents { + + private final Player p; + private final Rank rank; + + public SpigotRankManagerGUI(Player p, Rank rank) { + this.p = p; + this.rank = rank; + } + + public void open() { + + // Check if Ranks are enabled + if (!(checkRanks(p))){ + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + int dimension = 27; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3" + "Ranks -> RankManager")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create the lore + List rankupCommandsLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + "", + guiConfig.getString("Gui.Lore.Info") + ); + + SpigotRanksGUI.getCommands(rankupCommandsLore, rank); + + // Create the lore + List editPriceLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + "", + guiConfig.getString("Gui.Lore.Info"), + guiConfig.getString("Gui.Lore.Price") + rank.cost + ); + + // Create the lore + List editTagLore = createLore( + guiConfig.getString("Gui.Lore.ClickToOpen"), + "", + guiConfig.getString("Gui.Lore.Info"), + guiConfig.getString("Gui.Lore.Tag") + rank.tag + ); + + // Create the button + Material commandMinecart = Material.matchMaterial( "command_minecart" ); + if ( commandMinecart == null ) { + commandMinecart = Material.matchMaterial( "command_block_minecart" ); + } + + ItemStack rankupCommands = createButton(commandMinecart, 1, rankupCommandsLore, SpigotPrison.format("&3" + "RankupCommands" + " " + rank.name)); + + // Create the button + ItemStack rankPrice = createButton(Material.GOLD_NUGGET, 1, editPriceLore, SpigotPrison.format("&3" + "RankPrice" + " " + rank.name)); + + // Create the button + ItemStack rankTag = createButton(Material.NAME_TAG, 1, editTagLore, SpigotPrison.format("&3" + "RankTag" + " " + rank.name)); + + // Set the position and add it to the inventory + inv.setItem(10, rankupCommands); + + // Set the position and add it to the inventory + inv.setItem(13, rankPrice); + + // Set the position and add it to the inventory + inv.setItem(16, rankTag); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankPriceGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankPriceGUI.java new file mode 100644 index 000000000..7df41130a --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankPriceGUI.java @@ -0,0 +1,130 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SpigotRankPriceGUI extends SpigotGUIComponents { + + private final Player p; + private final String rankName; + private final Integer val; + + public SpigotRankPriceGUI(Player p, Integer val, String rankname){ + this.p = p; + this.val = val; + this.rankName = rankname; + } + + + + public void open() { + + // Check if Ranks are enabled + if (!(checkRanks(p))){ + return; + } + + // Create a new inventory + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3RankManager -> RankPrice")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create a new lore + List changeDecreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToDecrease") + ); + + // Create a new lore + List confirmButtonLore; + confirmButtonLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToConfirm"), + guiConfig.getString("Gui.Lore.Price2") + val, + guiConfig.getString("Gui.Lore.RightClickToCancel") + ); + + // Create a new lore + List changeIncreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToIncrease") + ); + + // Decrease button + ItemStack decreaseOf1 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " - 1" )); + inv.setItem(1, decreaseOf1); + + // Decrease button + ItemStack decreaseOf5 = createButton(Material.REDSTONE_BLOCK, 10, changeDecreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " - 10")); + inv.setItem(10, decreaseOf5); + + // Decrease button + ItemStack decreaseOf10 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " - 100")); + inv.setItem(19, decreaseOf10); + + // Decrease button + ItemStack decreaseOf50 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " - 1000")); + inv.setItem(28, decreaseOf50); + + // Decrease button + ItemStack decreaseOf100 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " - 10000")); + inv.setItem(37, decreaseOf100); + + + // Create a button and set the position + ItemStack confirmButton = createButton(Material.TRIPWIRE_HOOK, 1, confirmButtonLore, SpigotPrison.format("&3" + "Confirm: " + rankName + " " + val)); + inv.setItem(22, confirmButton); + + + // Increase button + ItemStack increaseOf1 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " + 1" )); + inv.setItem(7, increaseOf1); + + // Increase button + ItemStack increaseOf5 = createButton(Material.EMERALD_BLOCK, 10, changeIncreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " + 10")); + inv.setItem(16, increaseOf5); + + // Increase button + ItemStack increaseOf10 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " + 100")); + inv.setItem(25, increaseOf10); + + // Increase button + ItemStack increaseOf50 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " + 1000")); + inv.setItem(34, increaseOf50); + + // Increase button + ItemStack increaseOf100 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + rankName + " " + val + " + 10000")); + inv.setItem(43, increaseOf100); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankUPCommandsGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankUPCommandsGUI.java new file mode 100644 index 000000000..2127c1817 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRankUPCommandsGUI.java @@ -0,0 +1,110 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotRankUPCommandsGUI extends SpigotGUIComponents { + + private final Player p; + private final Rank rank; + + public SpigotRankUPCommandsGUI(Player p, Rank rank) { + this.p = p; + this.rank = rank; + } + + public void open() { + + // Init the ItemStack + // ItemStack itemCommand; + + // Check if Ranks are enabled + if (!(checkRanks(p))){ + return; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (rank.rankUpCommands.size() == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoRankupCommands"))); + return; + } + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(rank.rankUpCommands.size() / 9D) * 9; + + + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.EmptyGui"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyRankupCommands"))); + p.closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3RankManager -> RankUPCommands")); + + // For every command make a button + for (String command : rank.rankUpCommands) { + + if (guiBuilder(GuiConfig, inv, command)) return; + + } + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, String command) { + try { + buttonsSetup(guiConfig, inv, command); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, String command) { + ItemStack itemCommand; + // Init the lore array with default values for ladders + List commandsLore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete"), + "", + guiConfig.getString("Gui.Lore.Info")); + + // Adding a lore + commandsLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Command") + command)); + + // Make the button with materials, amount, lore and name + itemCommand = createButton(Material.TRIPWIRE_HOOK, 1, commandsLore, SpigotPrison.format("&3" + rank.name + " " + command)); + + // Add the button to the inventory + inv.addItem(itemCommand); + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRanksGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRanksGUI.java new file mode 100644 index 000000000..a1fd17606 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/rank/SpigotRanksGUI.java @@ -0,0 +1,168 @@ +package tech.mcprison.prison.spigot.gui.rank; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.Rank; +import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.data.RankPlayer; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +/** + * @author GABRYCA + */ +public class SpigotRanksGUI extends SpigotGUIComponents { + + private final Player p; + private final Optional ladder; + + public SpigotRanksGUI(Player p, Optional ladder) { + this.p = p; + this.ladder = ladder; + } + + public void open() { + + // Init the ItemStack + // ItemStack itemRank; + + int dimension = 27; + + // Check if Ranks are enabled + if (!(checkRanks(p))){ + return; + } + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + // Get the dimensions and if needed increases them + if (ladder.isPresent() && !(ladder.get().ranks.size() == 0)) { + dimension = (int) Math.ceil(ladder.get().ranks.size() / 9D) * 9; + } else { + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.NoRanksFoundAdmin"))); + return; + } + + // If the inventory is empty + if (dimension == 0){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.EmptyGui"))); + p.closeInventory(); + return; + } + + // If the dimension's too big, don't open the GUI + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(GuiConfig.getString("Gui.Message.TooManyRanks"))); + p.closeInventory(); + return; + } + + // Create the inventory and set up the owner, dimensions or number of slots, and title + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3" + "Ladders -> Ranks")); + + // For every rank make a button + for (RankLadder.PositionRank pos : ladder.get().ranks) { + Optional rankOptional = ladder.get().getByPosition(pos.getPosition()); + + // Well... check if the rank is null probably + if (!rankOptional.isPresent()) { + continue; // Skip it + } + + if (guiBuilder(GuiConfig, inv, rankOptional)) return; + + } + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Configuration guiConfig, Inventory inv, Optional rankOptional) { + try { + buttonsSetup(guiConfig, inv, rankOptional); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Configuration guiConfig, Inventory inv, Optional rankOptional) { + ItemStack itemRank; + // Init the lore array with default values for ladders + List ranksLore = createLore( + guiConfig.getString("Gui.Lore.ShiftAndRightClickToDelete"), + guiConfig.getString("Gui.Lore.ClickToManageRank"), + "", + guiConfig.getString("Gui.Lore.Info")); + + if (!rankOptional.isPresent()){ + p.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.CantGetRanksAdmin"))); + return; + } + + // Get the specific rank + Rank rank = rankOptional.get(); + + // Add the RankID Lore + ranksLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Id") + rank.id)); + + // Add the RankName lore + ranksLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Name") + rank.name)); + + // Add the Rank Tag lore + ranksLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Tag2") + ChatColor.translateAlternateColorCodes('&', rank.tag))); + + // Add the Price lore + ranksLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.Price3") + rank.cost)); + + // Init a variable + List players = + PrisonRanks.getInstance().getPlayerManager().getPlayers().stream() + .filter(rankPlayer -> rankPlayer.getRanks().containsValue(rankOptional.get())) + .collect(Collectors.toList()); + + // Add the number of players with this rank + ranksLore.add(SpigotPrison.format(guiConfig.getString("Gui.Lore.PlayersWithTheRank") + players.size())); + + // RankUpCommands info lore + ranksLore.add(""); + getCommands(ranksLore, rank); + + // Make the button with materials, amount, lore and name + itemRank = createButton(Material.TRIPWIRE_HOOK, 1, ranksLore, SpigotPrison.format("&3" + rank.name)); + + // Add the button to the inventory + inv.addItem(itemRank); + } + + static void getCommands(List ranksLore, Rank rank) { + + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (rank.rankUpCommands == null || rank.rankUpCommands.size() == 0) { + ranksLore.add(SpigotPrison.format(GuiConfig.getString("Gui.Lore.ContainsTheRank") + rank.name + GuiConfig.getString("Gui.Lore.ContainsNoCommands"))); + } else { + ranksLore.add(SpigotPrison.format(GuiConfig.getString("Gui.Lore.LadderThereAre") + rank.rankUpCommands.size() + GuiConfig.getString("Gui.Lore.LadderCommands"))); + for (String command : rank.rankUpCommands) { + ranksLore.add(SpigotPrison.format(GuiConfig.getString("Gui.Lore.RankupCommands") + command)); + } + ranksLore.add(SpigotPrison.format(GuiConfig.getString("Gui.Lore.ClickToManageCommands"))); + } + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllAdminGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllAdminGUI.java new file mode 100644 index 000000000..48bd83234 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllAdminGUI.java @@ -0,0 +1,100 @@ +package tech.mcprison.prison.spigot.gui.sellall; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; +import java.util.Objects; +import java.util.Set; + + +/** + * @author GABRYCA + */ +public class SellAllAdminGUI extends SpigotGUIComponents { + + private final Player p; + + public SellAllAdminGUI(Player p){ + this.p = p; + } + + public void open() { + + // Load configs + Configuration conf = SpigotPrison.getSellAllConfig(); + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + Inventory inv; + + if (guiBuilder(conf, GuiConfig)) return; + + inv = buttonsSetup(conf, GuiConfig); + if (inv == null) return; + + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private Inventory buttonsSetup(Configuration conf, Configuration guiConfig) { + + boolean emptyInv = false; + + try { + if (conf.getConfigurationSection("Items") == null) { + emptyInv = true; + } + } catch (NullPointerException e){ + emptyInv = true; + } + + if (emptyInv){ + p.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.NoSellAllItems"))); + p.closeInventory(); + return null; + } + + // Get the Items config section + Set items = Objects.requireNonNull(conf.getConfigurationSection("Items")).getKeys(false); + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(items.size() / 9D) * 9; + + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.TooManySellAllItems"))); + return null; + } + + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3PrisonManager -> SellAll-Admin")); + + for (String key : items) { + List itemsLore = createLore( + "&cRight-Click to delete", + "&aLeft-Click to edit value", + "&3value: &a$" + conf.getString("Items." + key + ".ITEM_VALUE") + ); + ItemStack item = createButton(Material.valueOf(conf.getString("Items." + key + ".ITEM_ID")), 1, itemsLore, SpigotPrison.format("&3" + conf.getString("Items." + key + ".ITEM_ID"))); + inv.addItem(item); + } + return inv; + } + + private boolean guiBuilder(Configuration conf, Configuration guiConfig) { + try { + buttonsSetup(conf, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPlayerGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPlayerGUI.java new file mode 100644 index 000000000..7b3332c2c --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPlayerGUI.java @@ -0,0 +1,99 @@ +package tech.mcprison.prison.spigot.gui.sellall; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * @author GABRYCA + */ +public class SellAllPlayerGUI extends SpigotGUIComponents { + + private final Player p; + + public SellAllPlayerGUI(Player p){ + this.p = p; + } + + public void open() { + + // Load configs + Configuration conf = SpigotPrison.getSellAllConfig(); + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + Inventory inv; + + if (guiBuilder(conf, GuiConfig)) return; + + inv = buttonsSetup(conf, GuiConfig); + if (inv == null) return; + + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private Inventory buttonsSetup(Configuration conf, Configuration guiConfig) { + + Inventory inv; + + boolean emptyInv = false; + + try { + if (conf.getConfigurationSection("Items") == null) { + emptyInv = true; + } + } catch (NullPointerException e){ + emptyInv = true; + } + + if (emptyInv){ + p.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.NoSellAllItems"))); + p.closeInventory(); + return null; + } + + // Get the Items config section + Set items = Objects.requireNonNull(conf.getConfigurationSection("Items")).getKeys(false); + + // Get the dimensions and if needed increases them + int dimension = (int) Math.ceil(items.size() / 9D) * 9; + + if (dimension > 54){ + p.sendMessage(SpigotPrison.format(guiConfig.getString("Gui.Message.TooManySellAllItems"))); + return null; + } + + inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3PrisonManager -> SellAll-Player")); + + for (String key : items) { + List itemsLore = createLore( + guiConfig.getString("Gui.Lore.Value") + conf.getString("Items." + key + ".ITEM_VALUE") + ); + ItemStack item = createButton(Material.valueOf(conf.getString("Items." + key + ".ITEM_ID")), 1, itemsLore, SpigotPrison.format("&3" + conf.getString("Items." + key + ".ITEM_ID"))); + inv.addItem(item); + } + return inv; + } + + private boolean guiBuilder(Configuration conf, Configuration guiConfig) { + try { + buttonsSetup(conf, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPriceGUI.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPriceGUI.java new file mode 100644 index 000000000..a9f6fa236 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/sellall/SellAllPriceGUI.java @@ -0,0 +1,124 @@ +package tech.mcprison.prison.spigot.gui.sellall; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.Configuration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.gui.ListenersPrisonManager; +import tech.mcprison.prison.spigot.gui.SpigotGUIComponents; + +import java.util.List; + +/** + * @author GABRYCA + */ +public class SellAllPriceGUI extends SpigotGUIComponents { + + private final Player p; + private final String itemID; + private final Double val; + + public SellAllPriceGUI(Player p, Double val, String itemID){ + this.p = p; + this.val = val; + this.itemID = itemID; + } + + public void open() { + + // Create a new inventory + int dimension = 45; + Inventory inv = Bukkit.createInventory(null, dimension, SpigotPrison.format("&3SellAll -> ItemValue")); + + // Load config + Configuration GuiConfig = SpigotPrison.getGuiConfig(); + + if (guiBuilder(inv, GuiConfig)) return; + + // Open the inventory + this.p.openInventory(inv); + ListenersPrisonManager.get().addToGUIBlocker(p); + } + + private boolean guiBuilder(Inventory inv, Configuration guiConfig) { + try { + buttonsSetup(inv, guiConfig); + } catch (NullPointerException ex){ + p.sendMessage(SpigotPrison.format("&cThere's a null value in the GuiConfig.yml [broken]")); + ex.printStackTrace(); + return true; + } + return false; + } + + private void buttonsSetup(Inventory inv, Configuration guiConfig) { + // Create a new lore + List changeDecreaseValueLore; + changeDecreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToDecrease") + ); + + // Create a new lore + List confirmButtonLore = createLore( + guiConfig.getString("Gui.Lore.LeftClickToConfirm"), + guiConfig.getString("Gui.Lore.Price2") + val, + guiConfig.getString("Gui.Lore.RightClickToCancel") + ); + + // Create a new lore + List changeIncreaseValueLore = createLore( + guiConfig.getString("Gui.Lore.ClickToIncrease") + ); + + + // Decrease button + ItemStack decreaseOf1 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " - 1" )); + inv.setItem(1, decreaseOf1); + + // Decrease button + ItemStack decreaseOf5 = createButton(Material.REDSTONE_BLOCK, 10, changeDecreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " - 10")); + inv.setItem(10, decreaseOf5); + + // Decrease button + ItemStack decreaseOf10 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " - 100")); + inv.setItem(19, decreaseOf10); + + // Decrease button + ItemStack decreaseOf50 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " - 1000")); + inv.setItem(28, decreaseOf50); + + // Decrease button + ItemStack decreaseOf100 = createButton(Material.REDSTONE_BLOCK, 1, changeDecreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " - 10000")); + inv.setItem(37, decreaseOf100); + + + // Create a button and set the position + ItemStack confirmButton = createButton(Material.TRIPWIRE_HOOK, 1, confirmButtonLore, SpigotPrison.format("&3" + "Confirm: " + itemID + " " + val)); + inv.setItem(22, confirmButton); + + + // Increase button + ItemStack increseOf1 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " + 1" )); + inv.setItem(7, increseOf1); + + // Increase button + ItemStack increaseOf5 = createButton(Material.EMERALD_BLOCK, 10, changeIncreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " + 10")); + inv.setItem(16, increaseOf5); + + // Increase button + ItemStack increaseOf10 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " + 100")); + inv.setItem(25, increaseOf10); + + // Increase button + ItemStack increaseOf50 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " + 1000")); + inv.setItem(34, increaseOf50); + + // Increase button + ItemStack increaseOf100 = createButton(Material.EMERALD_BLOCK, 1, changeIncreaseValueLore, SpigotPrison.format("&3" + itemID + " " + val + " + 10000")); + inv.setItem(43, increaseOf100); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotFurnaceRecipe.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotFurnaceRecipe.java index 36da0b622..f34f9cfd1 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotFurnaceRecipe.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotFurnaceRecipe.java @@ -18,6 +18,8 @@ package tech.mcprison.prison.spigot.inventory; +import org.bukkit.Material; + import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.inventory.FurnaceRecipe; import tech.mcprison.prison.spigot.SpigotUtil; @@ -38,8 +40,9 @@ public SpigotFurnaceRecipe(org.bukkit.inventory.FurnaceRecipe wrapper) { } @Override public FurnaceRecipe setInput(BlockType input) { + Material material = SpigotUtil.getMaterial( input ); ((org.bukkit.inventory.FurnaceRecipe) getWrapper()) - .setInput(SpigotUtil.blockTypeToMaterial(input)); + .setInput(material); return this; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotInventory.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotInventory.java index 7c8bb3652..39e1878c5 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotInventory.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotInventory.java @@ -18,10 +18,16 @@ package tech.mcprison.prison.spigot.inventory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.BeaconInventory; import org.bukkit.inventory.BrewerInventory; -import org.bukkit.material.MaterialData; + import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.inventory.Inventory; @@ -31,8 +37,6 @@ import tech.mcprison.prison.spigot.game.SpigotPlayer; import tech.mcprison.prison.util.BlockType; -import java.util.*; - /** * Created by DMP9 on 03/02/2017. */ @@ -87,7 +91,8 @@ public org.bukkit.inventory.Inventory getWrapper() { return wrapper; } - @Override public String getTitle() { + @SuppressWarnings( "deprecation" ) + @Override public String getTitle() { return wrapper.getTitle(); } @@ -110,7 +115,8 @@ public org.bukkit.inventory.Inventory getWrapper() { wrapper.setMaxStackSize(size); } - @Override public String getName() { + @SuppressWarnings( "deprecation" ) + @Override public String getName() { return wrapper.getName(); } @@ -123,11 +129,13 @@ public org.bukkit.inventory.Inventory getWrapper() { } @Override public boolean contains(BlockType type) { - MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); - org.bukkit.inventory.ItemStack stack = - new org.bukkit.inventory.ItemStack(materialData.getItemType()); - stack.setData(materialData); - return wrapper.contains(stack); + org.bukkit.inventory.ItemStack bukkitStack = SpigotUtil.getItemStack( type, 1 ); + return wrapper.contains(bukkitStack); +// MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); +// org.bukkit.inventory.ItemStack stack = +// new org.bukkit.inventory.ItemStack(materialData.getItemType()); +// stack.setData(materialData); +// return wrapper.contains(stack); } @Override public Iterator getIterator() { @@ -198,11 +206,13 @@ public org.bukkit.inventory.Inventory getWrapper() { } @Override public void clear(BlockType type) { - MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); - org.bukkit.inventory.ItemStack stack = - new org.bukkit.inventory.ItemStack(materialData.getItemType()); - stack.setData(materialData); - wrapper.remove(stack); + org.bukkit.inventory.ItemStack bukkitStack = SpigotUtil.getItemStack( type, 1 ); + wrapper.remove( bukkitStack ); +// MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); +// org.bukkit.inventory.ItemStack stack = +// new org.bukkit.inventory.ItemStack(materialData.getItemType()); +// stack.setData(materialData); +// wrapper.remove(stack); } @Override public void clear(ItemStack stack) { @@ -214,11 +224,13 @@ public org.bukkit.inventory.Inventory getWrapper() { } @Override public int first(BlockType type) { - MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); - org.bukkit.inventory.ItemStack stack = - new org.bukkit.inventory.ItemStack(materialData.getItemType()); - stack.setData(materialData); - return wrapper.first(stack); + org.bukkit.inventory.ItemStack bukkitStack = SpigotUtil.getItemStack( type, 1 ); + return wrapper.first(bukkitStack); +// MaterialData materialData = SpigotUtil.blockTypeToMaterial(type); +// org.bukkit.inventory.ItemStack stack = +// new org.bukkit.inventory.ItemStack(materialData.getItemType()); +// stack.setData(materialData); +// return wrapper.first(stack); } @Override public int firstEmpty() { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapedRecipe.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapedRecipe.java index ab4ec0468..7bb070f6a 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapedRecipe.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapedRecipe.java @@ -18,14 +18,16 @@ package tech.mcprison.prison.spigot.inventory; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Material; + import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.inventory.ShapedRecipe; import tech.mcprison.prison.spigot.SpigotUtil; import tech.mcprison.prison.util.BlockType; -import java.util.HashMap; -import java.util.Map; - /** * Created by DMP9 on 04/02/2017. */ @@ -48,8 +50,10 @@ public SpigotShapedRecipe(org.bukkit.inventory.ShapedRecipe wrapper) { } @Override public ShapedRecipe setIngredient(char key, BlockType ingredient) { + Material mat = SpigotUtil.getMaterial( ingredient ); + ((org.bukkit.inventory.ShapedRecipe) getWrapper()) - .setIngredient(key, SpigotUtil.blockTypeToMaterial(ingredient)); + .setIngredient(key, mat); return this; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapelessRecipe.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapelessRecipe.java index 2a64a19c9..12a9bb7cb 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapelessRecipe.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/inventory/SpigotShapelessRecipe.java @@ -18,14 +18,16 @@ package tech.mcprison.prison.spigot.inventory; +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; + import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.inventory.ShapelessRecipe; import tech.mcprison.prison.spigot.SpigotUtil; import tech.mcprison.prison.util.BlockType; -import java.util.ArrayList; -import java.util.List; - /** * Created by DMP9 on 04/02/2017. */ @@ -36,14 +38,16 @@ public SpigotShapelessRecipe(org.bukkit.inventory.ShapelessRecipe wrapper) { } @Override public ShapelessRecipe addIngredient(int count, BlockType ingredient) { + Material mat = SpigotUtil.getMaterial( ingredient ); ((org.bukkit.inventory.ShapelessRecipe) getWrapper()) - .addIngredient(count, SpigotUtil.blockTypeToMaterial(ingredient)); + .addIngredient(count, mat); return this; } @Override public ShapelessRecipe addIngredient(BlockType ingredient) { + Material mat = SpigotUtil.getMaterial( ingredient ); ((org.bukkit.inventory.ShapelessRecipe) getWrapper()) - .addIngredient(SpigotUtil.blockTypeToMaterial(ingredient)); + .addIngredient(mat); return this; } @@ -56,14 +60,16 @@ public SpigotShapelessRecipe(org.bukkit.inventory.ShapelessRecipe wrapper) { } @Override public ShapelessRecipe removeIngredient(int count, BlockType ingredient) { + Material mat = SpigotUtil.getMaterial( ingredient ); ((org.bukkit.inventory.ShapelessRecipe) getWrapper()) - .removeIngredient(count, SpigotUtil.blockTypeToMaterial(ingredient)); + .removeIngredient(count, mat); return this; } @Override public ShapelessRecipe removeIngredient(BlockType ingredient) { + Material mat = SpigotUtil.getMaterial( ingredient ); ((org.bukkit.inventory.ShapelessRecipe) getWrapper()) - .removeIngredient(SpigotUtil.blockTypeToMaterial(ingredient)); + .removeIngredient(mat); return this; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissions.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissions.java index 7710bf5aa..600b6b67b 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissions.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissions.java @@ -1,81 +1,82 @@ package tech.mcprison.prison.spigot.permissions; -import java.util.UUID; import me.lucko.luckperms.LuckPerms; -import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.LuckPermsApi; -import me.lucko.luckperms.api.Node; -import me.lucko.luckperms.api.User; import tech.mcprison.prison.integration.PermissionIntegration; import tech.mcprison.prison.internal.Player; /** + *

    This class supports pre version 5 of LuckPerms only. But that said, + * it can be used with v5 with their legacy jar file. See URL below. + *

    + * + *

    Note that v5 uses different package names and class names, so it is + * not compatible directly with this + * older version of LuckPerms. They have an extension that will continue + * to work with this older api format but server owners must install it on + * their end. Over all it is not recommended since this plugin now does + * support v5, but still, some servers may need to use it for other plugins, + * so not sure what kind of conflict that will cause. + * https://github.com/lucko/LuckPerms/wiki/Upgrading-from-v4-to-v5 + *

    + + *

    + * It should be noted that v5.x of LuckPerm supports all versions of minecraft and + * spigot from versions 1.8.8 to newest. + *

    + * * @author Faizaan A. Datoo */ -public class LuckPermissions implements PermissionIntegration { +public class LuckPermissions + extends PermissionIntegration { - private LuckPermsApi api; + private LuckPermissionsWrapper permsWrapper; public LuckPermissions() { - api = LuckPerms.getApi(); + super( "LuckPerms-Legacy", "LuckPerms" ); } + + @Override + public void integrate() { + if ( isRegistered()) { + try { + LuckPermsApi lp = LuckPerms.getApi(); + + if (lp != null) { + permsWrapper = new LuckPermissionsWrapper(); + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } @Override public void addPermission(Player holder, String permission) { - editPermission(holder.getUUID(), permission, true); + if ( permsWrapper != null ) { + permsWrapper.addPermission( holder, permission ); + } } @Override public void removePermission(Player holder, String permission) { - editPermission(holder.getUUID(), permission, false); + if ( permsWrapper != null ) { + permsWrapper.removePermission( holder, permission ); + } } - - private void editPermission(UUID uuid, String permission, boolean add) { - // get the user - User user = api.getUser(uuid); - if (user == null) { - return; // user not loaded - } - - // build the permission node - Node node = api.getNodeFactory().newBuilder(permission).build(); - - // set the permission - DataMutateResult result; - if(add) { - result = user.setPermission(node); - } else { - result = user.unsetPermission(node); - } - - // wasn't successful. - // they most likely already have (or didn't have if add = false) the permission - if (result != DataMutateResult.SUCCESS) { - return; - } - - // now, before we return, we need to have the user to storage. - // this method will save the user, then run the callback once complete. - api.getStorage().saveUser(user) - .thenAcceptAsync(wasSuccessful -> { - if (!wasSuccessful) { - return; - } - - // refresh the user's permissions, so the change is "live" - user.refreshCachedData(); - - }, api.getStorage().getAsyncExecutor()); - } - - @Override - public String getProviderName() { - return "LuckPerms"; - } - + @Override public boolean hasIntegrated() { - return api != null; + return (permsWrapper != null); } + + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-plugin.28140/history"; + } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissionsWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissionsWrapper.java new file mode 100644 index 000000000..06b7f42d8 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPermissionsWrapper.java @@ -0,0 +1,74 @@ +package tech.mcprison.prison.spigot.permissions; + +import java.util.UUID; + +import me.lucko.luckperms.LuckPerms; +import me.lucko.luckperms.api.DataMutateResult; +import me.lucko.luckperms.api.LuckPermsApi; +import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.User; +import tech.mcprison.prison.internal.Player; + +public class LuckPermissionsWrapper +{ + private LuckPermsApi api; + + public LuckPermissionsWrapper() { + try { + api = LuckPerms.getApi(); + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // If a NoClassDefFoundError happens, then that basically means the LuckPerms + // plugin has not been loaded. Basically since this is the legacy support, + // the v5 may be the one that was registered with the server api. + + // Ignore for now... maybe log the exception if in debug mode? + } + } + + protected void addPermission(Player holder, String permission) { + editPermission(holder.getUUID(), permission, true); + } + protected void removePermission(Player holder, String permission) { + editPermission(holder.getUUID(), permission, false); + } + + protected void editPermission(UUID uuid, String permission, boolean add) { + // get the user + User user = api.getUser(uuid); + if (user == null) { + return; // user not loaded + } + + // build the permission node + Node node = api.getNodeFactory().newBuilder(permission).build(); + + // set the permission + DataMutateResult result; + if(add) { + result = user.setPermission(node); + } else { + result = user.unsetPermission(node); + } + + // wasn't successful. + // they most likely already have (or didn't have if add = false) the permission + if (result != DataMutateResult.SUCCESS) { + return; + } + + // now, before we return, we need to have the user to storage. + // this method will save the user, then run the callback once complete. + api.getStorage().saveUser(user) + .thenAcceptAsync(wasSuccessful -> { + if (!wasSuccessful) { + return; + } + + // refresh the user's permissions, so the change is "live" + user.refreshCachedData(); + + }, api.getStorage().getAsyncExecutor()); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5.java new file mode 100644 index 000000000..c6832b7d1 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5.java @@ -0,0 +1,75 @@ +package tech.mcprison.prison.spigot.permissions; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; + +import net.luckperms.api.LuckPerms; +import tech.mcprison.prison.integration.PermissionIntegration; +import tech.mcprison.prison.internal.Player; + +/** + *

    This provides support for the integration of LuckPerms v5.x. + *

    + * + *

    Additional information can be found on the following topics: + *

    + *
      + *
    • LuckPerms PlaceHolders - + * - Note if using MVdWPlaceholderAPI - + * - https://github.com/lucko/LuckPerms/wiki/Placeholders
    • + *
    + * + */ +public class LuckPerms5 + extends PermissionIntegration { + + private LuckPerms5Wrapper permsWrapper; + + public LuckPerms5() { + super( "LuckPermsV5", "LuckPerms" ); + } + + @Override + public void integrate() { + if ( isRegistered()) { + try { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); + if (provider != null) { + this.permsWrapper = new LuckPerms5Wrapper(provider); + } + } + catch ( java.lang.NoClassDefFoundError | IllegalStateException e ) { + // ignore since this just means v4.x or lower is being used and not v5 + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } + + @Override + public void addPermission( Player holder, String permission ) { + if ( permsWrapper != null ) { + permsWrapper.addPermission( holder, permission ); + } + } + + + @Override + public void removePermission( Player holder, String permission ) { + if ( permsWrapper != null ) { + permsWrapper.removePermission( holder, permission ); + } + } + + @Override + public boolean hasIntegrated() { + return (permsWrapper != null); + } + + @Override + public String getPluginSourceURL() { + return "https://luckperms.net/"; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5Wrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5Wrapper.java new file mode 100644 index 000000000..233cc6816 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/LuckPerms5Wrapper.java @@ -0,0 +1,101 @@ +/** + * + */ +package tech.mcprison.prison.spigot.permissions; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import org.bukkit.plugin.RegisteredServiceProvider; + +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.data.DataMutateResult; +import net.luckperms.api.model.user.User; +import net.luckperms.api.model.user.UserManager; +import net.luckperms.api.node.types.PermissionNode; +import tech.mcprison.prison.internal.Player; + +/** + *

    This wrapper provides support for LuckPerms v5.x. Both v5.x and legacy + * use the same provider name, so additional checking must be performed to see + * which is actually registered. + *

    + * + */ +public class LuckPerms5Wrapper +{ + private LuckPerms api = null; + + public LuckPerms5Wrapper(RegisteredServiceProvider provider) { + super(); + +// LuckPerms api = provider.getProvider(); + api = provider.getProvider(); + } + + public void addPermission( Player holder, String permission ) { + editPermission(holder.getUUID(), permission, true); + } + + public void removePermission( Player holder, String permission ) { + editPermission(holder.getUUID(), permission, false); + } + + /** + *

    This function does not return anything, nor is there anything waiting for its + * completion. Therefore it can be ran async, which is the correct way to do this. + *

    + * + *

    Instead of using lambda functions, with inline callbacks, methods are being + * used instead. The biggest reason for this is that if things go bad, error messages + * and/or stack traces are clearer and more to the point on what exactly when wrong and + * where. I've seen too many vague entries in the logs, and not knowing who may be trying + * to fix future issues, then it makes more sense to keep things understandable instead + * of cryptic. Besides, the optimizers within the compliers will be able to work with + * either technique just as well, so performance is not an issue. + *

    + * + * @param uuid + * @param permission + * @param add + */ + private void editPermission(UUID uuid, String permission, boolean add) { + if ( api != null ) { + + UserManager um = api.getUserManager(); + User user = um.getUser(uuid); + CompletableFuture userFuture = (user == null ? + um.loadUser(uuid) : CompletableFuture.completedFuture(user)); + + userFuture + .thenAcceptAsync(lpUser -> { + changePermission(lpUser, permission, add); + }); + } + + } + + private void changePermission( User user, String permission, boolean add ) { + boolean dirty = false; + + // build a permission node now so we don't have to pass around + // the raw components, just this node. + PermissionNode newNode = PermissionNode.builder(permission).build(); + + // Try to remove the perm if it exists. Even if adding a perm, try to remove it + // to prevent possible duplicates. + if ( user.data().remove( newNode ) == DataMutateResult.SUCCESS ) { + dirty = true; + } + + if ( add && user.data().add( newNode ) == DataMutateResult.SUCCESS ) { + dirty = true; + } + + if ( dirty ) { + api.getUserManager().saveUser( user ); + } + } + +} + diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/VaultPermissions.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/VaultPermissions.java index c252a309d..1872eb325 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/VaultPermissions.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/permissions/VaultPermissions.java @@ -20,6 +20,7 @@ import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; + import tech.mcprison.prison.integration.PermissionIntegration; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.spigot.game.SpigotPlayer; @@ -27,20 +28,36 @@ /** * @author Faizaan A. Datoo */ -public class VaultPermissions implements PermissionIntegration { +public class VaultPermissions + extends PermissionIntegration { private net.milkbowl.vault.permission.Permission permissions = null; public VaultPermissions() { - RegisteredServiceProvider permissionProvider = - Bukkit.getServer().getServicesManager() - .getRegistration(net.milkbowl.vault.permission.Permission.class); - if (permissionProvider != null) { - permissions = permissionProvider.getProvider(); - } + super( "Vault", "Vault" ); } + + @Override + public void integrate() { + if ( isRegistered()) { + try { + RegisteredServiceProvider permissionProvider = + Bukkit.getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.permission.Permission.class); + if (permissionProvider != null) { + permissions = permissionProvider.getProvider(); + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } - @Override public void addPermission(Player holder, String permission) { + @Override public void addPermission(Player holder, String permission) { SpigotPlayer player = (SpigotPlayer) holder; this.permissions.playerAdd(player.getWrapper(), permission); } @@ -50,10 +67,11 @@ public VaultPermissions() { this.permissions.playerRemove(player.getWrapper(), permission); } - @Override public String getProviderName() { - return permissions.getName() + " (Vault)"; + @Override + public String getDisplayName() { + return (permissions == null ? "Vault permissons" : permissions.getName()) + " (Vault)"; } - + @Override public boolean hasIntegrated() { return permissions != null; } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegration.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegration.java index 164b7a94d..757b586dc 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegration.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegration.java @@ -1,35 +1,129 @@ package tech.mcprison.prison.spigot.placeholder; -import be.maximvdw.placeholderapi.PlaceholderAPI; +import java.util.List; +import java.util.function.Function; + import org.bukkit.Bukkit; + +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.PlaceHolderKey; import tech.mcprison.prison.integration.PlaceholderIntegration; import tech.mcprison.prison.internal.Player; -import tech.mcprison.prison.spigot.game.SpigotPlayer; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.managers.MineManager; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.managers.PlayerManager; +import tech.mcprison.prison.util.Text; -import java.util.function.Function; - -public class MVdWPlaceholderIntegration implements PlaceholderIntegration { - - private boolean pluginInstalled; +/** + *

    This hooks up the registration when the Prison plugin starts to run. + * The MVdWPlaceholderIntegrationWrapper sets up the registrations. It should + * be noted that the registrations used to occur within the ChatHandler, but + * that did not make sense since these should be self-contained with the + * registration process if these plugins are active. + *

    + * + *

    LuckPerms v5 documentation notes on MVdWPlaceholderAPI: + * + * To use the LuckPerms placeholders in plugins which support Maximvdw's MVdWPlaceholderAPI, + * you need to install the LuckPerms placeholder hook plugin. + * https://github.com/lucko/LuckPerms/wiki/Placeholders + *

    + * + */ +public class MVdWPlaceholderIntegration + extends PlaceholderIntegration { + private MVdWPlaceholderIntegrationWrapper placeholderWrapper; + public MVdWPlaceholderIntegration() { - pluginInstalled = Bukkit.getPluginManager().isPluginEnabled("MVdWPlaceholderAPI"); + super( "MVdWPlaceholderAPI", "MVdWPlaceholderAPI" ); } - + + @Override + public void integrate() { + if ( isRegistered()) { + try { + if ( Bukkit.getPluginManager().isPluginEnabled(getProviderName())) { + placeholderWrapper = new MVdWPlaceholderIntegrationWrapper(getProviderName()); + + PrisonAPI.getIntegrationManager().addDeferredInitialization( this ); + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } + + + /** + *

    Register both the player and mines placeholders with the MVdW plugin. + *

    + */ @Override + public void deferredInitialization() + { + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + List placeholderPlayerKeys = pm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderPlayerKeys ) { + if ( !placeHolderKey.getPlaceholder().isSuppressed() ) { + registerPlaceholder(placeHolderKey.getKey(), + player -> Text.translateAmpColorCodes( + pm.getTranslatePlayerPlaceHolder( + player.getUUID(), player.getName(), placeHolderKey ) + )); + } + } + } + } + + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + if ( mm != null ) { + List placeholderMinesKeys = mm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderMinesKeys ) { + if ( !placeHolderKey.getPlaceholder().isSuppressed() ) { + registerPlaceholder(placeHolderKey.getKey(), + player -> Text.translateAmpColorCodes( + mm.getTranslateMinesPlaceHolder( placeHolderKey ) + )); + } + } + } + } + } + + + @Override public void registerPlaceholder(String placeholder, Function action) { - if (!hasIntegrated()) return; - PlaceholderAPI.registerPlaceholder(Bukkit.getPluginManager().getPlugin("Prison"), placeholder, e -> action.apply(new SpigotPlayer(e.getPlayer()))); + if (placeholderWrapper != null) { + placeholderWrapper.registerPlaceholder( placeholder, action ); + } } - + @Override - public String getProviderName() { - return "MVdWPlaceholderAPI"; + public boolean hasIntegrated() { + return (placeholderWrapper != null); } - + @Override - public boolean hasIntegrated() { - return pluginInstalled; + public String getAlternativeInformation() { + return null; } + + @Override + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/mvdwplaceholderapi.11182/"; + } + } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegrationWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegrationWrapper.java new file mode 100644 index 000000000..98606f626 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/MVdWPlaceholderIntegrationWrapper.java @@ -0,0 +1,27 @@ +package tech.mcprison.prison.spigot.placeholder; + +import java.util.function.Function; + +import org.bukkit.Bukkit; + +import be.maximvdw.placeholderapi.PlaceholderAPI; +import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.spigot.game.SpigotPlayer; + +/** + * Register the non-suppressed place holders for prison. + */ +public class MVdWPlaceholderIntegrationWrapper +{ + + public MVdWPlaceholderIntegrationWrapper(String providerName) { + super(); + } + + public void registerPlaceholder(String placeholder, Function action) { + PlaceholderAPI.registerPlaceholder( + Bukkit.getPluginManager().getPlugin("Prison"), placeholder, + e -> action.apply(new SpigotPlayer(e.getPlayer()))); + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegration.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegration.java index 51e83b559..7ef475edd 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegration.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegration.java @@ -1,145 +1,67 @@ package tech.mcprison.prison.spigot.placeholder; -import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import java.util.function.Function; + import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import tech.mcprison.prison.PrisonAPI; + import tech.mcprison.prison.integration.PlaceholderIntegration; import tech.mcprison.prison.internal.Player; -import tech.mcprison.prison.ranks.PrisonRanks; -import tech.mcprison.prison.ranks.data.Rank; -import tech.mcprison.prison.ranks.data.RankLadder; -import tech.mcprison.prison.ranks.data.RankPlayer; - -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Function; - -public class PlaceHolderAPIIntegration extends PlaceholderExpansion implements PlaceholderIntegration { - private boolean pluginInstalled; +public class PlaceHolderAPIIntegration + extends PlaceholderIntegration { + + private PlaceHolderAPIIntegrationWrapper placeHolderWrapper; public PlaceHolderAPIIntegration() { - pluginInstalled = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); + super( "PlaceholderAPI", "PlaceholderAPI" ); } - + @Override - public void registerPlaceholder(String placeholder, Function action) { - this.register(); - } - - @Override - public String getProviderName() { - return "PlaceHolderAPI"; + public void integrate() { + if ( isRegistered()) { + try { + if ( Bukkit.getPluginManager().isPluginEnabled(getProviderName()) ) { + this.placeHolderWrapper = new PlaceHolderAPIIntegrationWrapper(); + this.placeHolderWrapper.register(); + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } } - + + /** + *

    For PlaceHolderAPI this function is not needed since it is registered when + * integrate() is called since it is a simple integration. + *

    + */ @Override - public boolean hasIntegrated() { - return pluginInstalled; + public void registerPlaceholder(String placeholder, Function action) { +// if ( placeHolderWrapper != null ) { +// placeHolderWrapper.registerPlaceholder( placeholder, action ); +// } } - + @Override - public String getAuthor(){ - return "PrisonTeam"; + public void deferredInitialization() { } - + @Override - public String getIdentifier(){ - return "Prison"; + public boolean hasIntegrated() { + return (placeHolderWrapper != null); } - + @Override - public String getVersion(){ - return PrisonAPI.getPluginVersion(); - } + public String getAlternativeInformation() { + return null; + } @Override - public String onRequest(OfflinePlayer player, String identifier){ - - if(identifier.equals("rank")){ - return getNames(player.getUniqueId()); - }else if(identifier.equals("rankup_cost")){ - return getNextCost(player.getUniqueId()); - }else if(identifier.equals("rankup_rank")) { - return getNextName(player.getUniqueId()); - } - - return null; - } - - private String getNames(UUID uid) { - - Optional player = - PrisonRanks.getInstance().getPlayerManager().getPlayer(uid); - String prefix = ""; - - if (player.isPresent() && !player.get().getRanks().isEmpty()) { - StringBuilder builder = new StringBuilder(); - for (Map.Entry entry : player.get().getRanks().entrySet()) { - builder.append(entry.getValue().name); - builder.append(", "); - } - prefix = builder.toString(); - - } - - if(prefix.length() > 0) { - return prefix.substring(0, prefix.length() - 2); - }else{ - return ""; - } - + public String getPluginSourceURL() { + return "https://www.spigotmc.org/resources/placeholderapi.6245/"; } - - private String getNextCost(UUID uid) { - - Optional player = - PrisonRanks.getInstance().getPlayerManager().getPlayer(uid); - String prefix = ""; - - if (player.isPresent() && !player.get().getRanks().isEmpty()) { - StringBuilder builder = new StringBuilder(); - for (Map.Entry entry : player.get().getRanks().entrySet()) { - if(entry.getKey().getNext(entry.getKey().getPositionOfRank(entry.getValue())).isPresent()) { - builder.append(entry.getKey().getNext(entry.getKey().getPositionOfRank(entry.getValue())).get().cost); - builder.append(", "); - } - } - prefix = builder.toString(); - } - - if(prefix.length() > 0) { - return prefix.substring(0, prefix.length() - 2); - }else{ - return ""; - } - - } - - private String getNextName(UUID uid) { - - Optional player = - PrisonRanks.getInstance().getPlayerManager().getPlayer(uid); - String prefix = ""; - - if (player.isPresent() && !player.get().getRanks().isEmpty()) { - StringBuilder builder = new StringBuilder(); - for (Map.Entry entry : player.get().getRanks().entrySet()) { - if(entry.getKey().getNext(entry.getKey().getPositionOfRank(entry.getValue())).isPresent()) { - builder.append(entry.getKey().getNext(entry.getKey().getPositionOfRank(entry.getValue())).get().name); - builder.append(", "); - } - } - prefix = builder.toString(); - } - - if(prefix.length() > 0) { - return prefix.substring(0, prefix.length() - 2); - }else{ - return ""; - } - - } - -} \ No newline at end of file +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationWrapper.java new file mode 100644 index 000000000..feda452b6 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationWrapper.java @@ -0,0 +1,83 @@ +package tech.mcprison.prison.spigot.placeholder; + +import java.util.UUID; + +import org.bukkit.OfflinePlayer; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.IntegrationManager; + +public class PlaceHolderAPIIntegrationWrapper + extends PlaceholderExpansion +{ + public PlaceHolderAPIIntegrationWrapper() { + super(); + } + + +// public void registerPlaceholder(String placeholder, Function action) { +// this.register(); +// } + + /** + * This method should always return true unless we + * have a dependency we need to make sure is on the server + * for our placeholders to work! + * + * @return always true since we do not have any dependencies. + */ + @Override + public boolean canRegister(){ + return true; + } + + @Override + public String getAuthor(){ + return "PrisonTeam"; + } + + /** + *

    It should be noted that this tells PlaceholderAPI that all identifiers, or + * place holders, that begin with "prison" should use this API call under this + * onRequest(). This is very important to understand, since all prison related + * place holders MUST start with prison_ and cannot be something like "rankup_rank" or + * "rank" since those will never work. + *

    + * + */ + @Override + public String getIdentifier(){ + return IntegrationManager.PRISON_PLACEHOLDER_PREFIX; + } + + @Override + public String getVersion(){ + return PrisonAPI.getPluginVersion(); + } + + /** + * Return a null if no hits. + */ + @Override + public String onRequest(OfflinePlayer player, String identifier) { + + UUID playerUuid = player.getUniqueId(); + String results = Prison.get().getPlatform().getPlaceholders() + .placeholderTranslate( playerUuid, player.getName(), identifier ); + +// PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); +// if ( pm != null ) { +// results = pm.getTranslatePlayerPlaceHolder( playerUuid, identifier ); +// } +// +// // If it did not match on a player placeholder, then try mines: +// if ( results == null ) { +// MineManager mm = PrisonMines.getInstance().getMineManager(); +// results = mm.getTranslateMinesPlaceHolder( identifier ); +// } + + return results; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/SpigotPlaceholders.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/SpigotPlaceholders.java new file mode 100644 index 000000000..f7afb4aa9 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/SpigotPlaceholders.java @@ -0,0 +1,378 @@ +package tech.mcprison.prison.spigot.placeholder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.integration.IntegrationManager.PlaceHolderFlags; +import tech.mcprison.prison.integration.IntegrationManager.PrisonPlaceHolders; +import tech.mcprison.prison.integration.PlaceHolderKey; +import tech.mcprison.prison.integration.Placeholders; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.mines.managers.MineManager; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.managers.PlayerManager; +import tech.mcprison.prison.spigot.SpigotPrison; + +public class SpigotPlaceholders + implements Placeholders { + + + @Override + public Map getPlaceholderDetailCounts() { + Map placeholderDetails = new TreeMap<>(); + + List placeholders = new ArrayList<>(); + + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + placeholders.addAll( pm.getTranslatedPlaceHolderKeys() ); + } + } + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + if ( mm != null ) { + placeholders.addAll( mm.getTranslatedPlaceHolderKeys() ); + } + } + + for ( PlaceHolderKey phKey : placeholders ) { + for ( PlaceHolderFlags flag : phKey.getPlaceholder().getFlags() ) { + + int count = 0; + + if ( placeholderDetails.containsKey( flag ) ) { + count = placeholderDetails.get( flag ); + } + + placeholderDetails.put( flag, new Integer(count + 1) ); + } + } + + return placeholderDetails; + } + + @Override + public int getPlaceholderCount() { + int placeholdersRawCount = 0; + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + List placeholderPlayerKeys = pm.getTranslatedPlaceHolderKeys(); + placeholdersRawCount += placeholderPlayerKeys.size(); + + } + } + + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + if ( mm != null ) { + List placeholderMinesKeys = mm.getTranslatedPlaceHolderKeys(); + placeholdersRawCount += placeholderMinesKeys.size(); + + } + + } + + return placeholdersRawCount; + } + + @Override + public int getPlaceholderRegistrationCount() { + int placeholdersRegistered = 0; + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + List placeholderPlayerKeys = pm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderPlayerKeys ) { + if ( !placeHolderKey.getPlaceholder().isSuppressed() ) { + placeholdersRegistered++; + } + } + } + } + + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + if ( mm != null ) { + List placeholderMinesKeys = mm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderMinesKeys ) { + if ( !placeHolderKey.getPlaceholder().isSuppressed() ) { + placeholdersRegistered++; + } + } + } + + } + + return placeholdersRegistered; + } + + + /** + *

    Provides placeholder translation for any placeholder identifier + * that is provided. This not the full text with one or more placeholders, + * but it is strictly just the placeholder. + *

    + * + *

    This is a centrally located placeholder translator that is able + * to access both the PlayerManager (ranks) and the MineManager (mines). + *

    + * + *

    This function is used with: + * tech.mcprison.prison.spigot.placeholder.PlaceHolderAPIIntegrationWrapper.onRequest() + *

    + * + *

    Note: MVdWPlaceholder integration is using something else. Probably should be + * converted over to use this to standardize and simplify the code. + *

    + * + */ + @Override + public String placeholderTranslate(UUID playerUuid, String playerName, String identifier) { + String results = null; + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() && + playerUuid != null ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + results = pm.getTranslatePlayerPlaceHolder( playerUuid, playerName, identifier ); + } + } + + // If it did not match on a player placeholder, then try mines: + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() && + results == null ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + results = mm.getTranslateMinesPlaceHolder( identifier ); + } + + return results; + } + + /** + *

    This function is used in this class's placeholderTranslateText() and + * also in tech.mcprison.prison.mines.MinesChatHandler.onPlayerChat(). + *

    + * + */ + @Override + public String placeholderTranslateText( String text) { + String results = text; + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + + List placeholderKeys = mm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderKeys ) { + String key = "{" + placeHolderKey.getKey() + "}"; + if ( results.contains( key )) { + results = results.replace(key, + mm.getTranslateMinesPlaceHolder( placeHolderKey ) ); + } + } + } + + return results; + } + + /** + *

    Since a player UUID is provided, first translate for any possible + * player specific placeholder, then try to translate for any mine + * related placeholder. + *

    + * + *

    This function is used with the command: /prison placeholders test + *

    + * + * @param playerUuid + * @param text + * @return + */ + @Override + public String placeholderTranslateText( UUID playerUuid, String playerName, String text) { + String results = text; + + // First the player specific placeholder, which must have a UUID: + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() && + playerUuid != null ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + + List placeholderKeys = pm.getTranslatedPlaceHolderKeys(); + + for ( PlaceHolderKey placeHolderKey : placeholderKeys ) { + String key = "{" + placeHolderKey.getKey() + "}"; + if ( results.contains( key )) { + results = results.replace(key, + pm.getTranslatePlayerPlaceHolder( playerUuid, playerName, placeHolderKey.getKey() ) ); + } + } + } + + // Then translate any remaining non-player (mine) related placeholders: + results = placeholderTranslateText( results); + + return results; + } + + @Override + public List placeholderSearch( UUID playerUuid, String playerName, String[] patterns ) + { + List results = new ArrayList<>(); + + TreeMap placeholderKeys = new TreeMap<>(); + + MineManager mm = null; + PlayerManager pm = null; + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + pm = PrisonRanks.getInstance().getPlayerManager(); + addAllPlaceHolderKeys( placeholderKeys, pm.getTranslatedPlaceHolderKeys() ); + } + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + mm = PrisonMines.getInstance().getMineManager(); + addAllPlaceHolderKeys( placeholderKeys, mm.getTranslatedPlaceHolderKeys() ); + } + + Set keys = placeholderKeys.keySet(); + for ( String key : keys) { + PlaceHolderKey placeHolderKey = placeholderKeys.get( key ); + if ( placeHolderKey.isPrimary() && + placeholderKeyContains(placeHolderKey, patterns) ) { + String placeholder = "{" + placeHolderKey.getKey() + "}"; + String value = null; + + if ( mm != null && (placeHolderKey.getPlaceholder().hasFlag( PlaceHolderFlags.MINES ) || + placeHolderKey.getPlaceholder().hasFlag( PlaceHolderFlags.PLAYERMINES ))) { + value = mm.getTranslateMinesPlaceHolder( placeHolderKey ); + } + else if ( pm != null && (placeHolderKey.getPlaceholder().hasFlag( PlaceHolderFlags.PLAYER ) || + placeHolderKey.getPlaceholder().hasFlag( PlaceHolderFlags.LADDERS ))) { + value = pm.getTranslatePlayerPlaceHolder( playerUuid, playerName, placeHolderKey ); + } + + String placeholderAlias = ( placeHolderKey.getAliasName() == null ? null : + "{" + placeHolderKey.getAliasName() + "}"); + + String placeholderResults = String.format( " &7%s: &3%s", + placeholder, + (value == null ? "" : value) ); + results.add( placeholderResults ); + + if ( placeholderAlias != null ) { + String placeholderResultsAlias = String.format( " &7Alias: &7(&b%s&7)", placeholderAlias ); + results.add( placeholderResultsAlias ); + } + } + + } + + return results; + } + + private void addAllPlaceHolderKeys( TreeMap placeholderKeys, + List translatedPlaceHolderKeys ) { + for ( PlaceHolderKey placeHolderKey : translatedPlaceHolderKeys ) { + placeholderKeys.put( placeHolderKey.getKey(), placeHolderKey ); + } + } + + + private boolean placeholderKeyContains( PlaceHolderKey placeHolderKey, String... patterns ) { + PrisonPlaceHolders ph = placeHolderKey.getPlaceholder(); + PrisonPlaceHolders phAlias = placeHolderKey.getPlaceholder().getAlias(); + + return !ph.isAlias() && !ph.isSuppressed() && + placeholderKeyContains( ph, placeHolderKey.getKey(), patterns) || + phAlias != null && !phAlias.isSuppressed() && + placeholderKeyContains( phAlias, placeHolderKey.getAliasName(), patterns) + ; + + } + + private boolean placeholderKeyContains( PrisonPlaceHolders placeholder, String key, String... patterns ) { + boolean results = key != null && patterns != null && patterns.length > 0; + + if ( results ) { + for ( String pattern : patterns ) { + if ( pattern == null || !key.contains( pattern ) ) { + results = false; + break; + } + } + } + return results; + } + + /** + * This forces the PlayerManager to reload the internal mapped placeholder listing. + * This forces the MineManager to reload the internal mapped placeholder listing. + * + */ + public void reloadPlaceholders() { + + // Must force Prison to reload the config.yml file: + Prison.get().getPlatform().reloadConfig(); + + + Prison.get().getIntegrationManager().reloadPlaceholderBarConfig(); + + if ( PrisonRanks.getInstance() != null && PrisonRanks.getInstance().isEnabled() ) { + PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); + if ( pm != null ) { + pm.reloadPlaceholders(); + } + } + + if ( PrisonMines.getInstance() != null && PrisonMines.getInstance().isEnabled() ) { + MineManager mm = PrisonMines.getInstance().getMineManager(); + if ( mm != null ) { + mm.reloadPlaceholders(); + } + } + + // Force the re-registration of the placeholder integrations: + SpigotPrison.getInstance().reloadIntegrationsPlaceholders(); + + + // Finally, print the placeholder stats: + printPlaceholderStats(); + + } + + + @Override + public void printPlaceholderStats() { + + Output.get().logInfo( "Total placeholders generated: %d", + getPlaceholderCount() ); + + Map phDetails = getPlaceholderDetailCounts(); + for ( PlaceHolderFlags key : phDetails.keySet() ) { + Output.get().logInfo( " %s: %d", + key.name(), phDetails.get( key ) ); + + } + + Output.get().logInfo( "Total placeholders available to be Registered: %d", + getPlaceholderRegistrationCount() ); + + + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventData.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventData.java new file mode 100644 index 000000000..8606096de --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventData.java @@ -0,0 +1,232 @@ +package tech.mcprison.prison.spigot.player; + +import java.text.DecimalFormat; + +import org.bukkit.entity.Player; + +import tech.mcprison.prison.util.Text; + +public class SlimeBlockFunEventData { + + private Long playerUUIDLSB; + private long timestamp; + + private double initialY = 0.0; + private double maxY = 0.0; + private double boost = 0.0; + private int boostCount = 0; + private double velocityY = 0.0; + private boolean jumping = false; + + private double recordHeight = 0.0; + private double recordY = 0.0; + private double recordBoost = 0.0; + private double recordVelocity = 0.0; + + private DecimalFormat sFmt = new DecimalFormat("#,##0.0"); + private DecimalFormat dFmt = new DecimalFormat("#,##0.00"); + + public SlimeBlockFunEventData( Long playerUUIDLSB, double y ) { + super(); + + this.playerUUIDLSB = playerUUIDLSB; + this.timestamp = System.currentTimeMillis(); + + this.initialY = y; + this.maxY = y; + this.jumping = false; + this.recordHeight = 0.0; + this.recordY = 0.0; + } + + public void addJumpEvent( double y, double boost, double velocityY ) { + + long currentTime = System.currentTimeMillis(); + if ( !isJumping() ) { + this.boostCount = 0; + this.jumping = true; + + this.initialY = y; + } + + this.timestamp = currentTime; + + this.maxY = y; + this.boost = boost; + this.velocityY = velocityY; + + this.boostCount++; + } + + public void inAir( double currentY, Player player ) { + if ( isJumping() ) { + + if ( currentY > getMaxY() ) { + // Going up! Keep recording height! + setMaxY( currentY ); + } + else if ( currentY < getMaxY() ) { + + if ( currentY >= 255 ) { + player.sendMessage( "SlimeFun: You jumped out of the world! " + + " y= " + dFmt.format( currentY )); + } + + // Just starting to go back down!! + // Stop recording the jump: + atTopOfJump( player ); + } + } + } + + private void atTopOfJump( Player player ) + { + double height = getMaxY() - getInitialY(); + + boolean recY = ( getMaxY() > getRecordY() ); + boolean recH = ( height > getRecordHeight() ); + boolean recB = ( getBoost() > getRecordBoost() ); + boolean recV = ( getVelocityY() > getRecordVelocity() ); + + if ( recY ) { + setRecordY( getMaxY() ); + } + if ( recH ) { + setRecordHeight( height ); + } + if ( recB ) { + setRecordBoost( getBoost() ); + } + if ( recV ) { + setRecordVelocity( getVelocityY() ); + } + + String message1 = Text.translateAmpColorCodes( + String.format("&a%s &3Height: &7%s &a%s &3maxY: &7%s &a%s", + (recY || recH || recB || recV ? "&6.-=New=-." : "__Slime__"), + sFmt.format(height), + (recH ? "&6" : "" ) + sFmt.format( getRecordHeight() ), + sFmt.format(getMaxY()), + (recY ? "&6" : "" ) + sFmt.format( getRecordY()) + )); + + String message2 = Text.translateAmpColorCodes( + String.format("&a%s &3Boost: &7%s &b%s &a%s &3Velocity: &7%s &a%s", + (recY || recH || recB || recV ? "&6-Record!-" : "__Fun!!__"), + + dFmt.format(getBoost()), Integer.toString( getBoostCount()), + (recB ? "&6" : "" ) + dFmt.format( getRecordBoost() ), + dFmt.format(getVelocityY()), + (recV ? "&6" : "" ) + dFmt.format(getRecordVelocity()) + )); + player.sendMessage( message1 ); + player.sendMessage( message2 ); + + // Reset key values: + setJumping( false ); + setBoostCount( 0 ); + setMaxY( getInitialY() ); + } + + public boolean hasLanded(Player p) { + boolean wasJumping = false; + + long inAir = System.currentTimeMillis() - getTimestamp(); + + if ( inAir <= (16 * 1000) ) { + wasJumping = true; + + setJumping( false ); + setBoostCount( 0 ); + setBoost( 1.0 ); + //setTimestamp( 0 ); + } + + return wasJumping; + } + + public Long getPlayerUUIDLSB() { + return playerUUIDLSB; + } + public void setPlayerUUIDLSB( Long playerUUIDLSB ) { + this.playerUUIDLSB = playerUUIDLSB; + } + + public long getTimestamp() { + return timestamp; + } + public void setTimestamp( long timestamp ) { + this.timestamp = timestamp; + } + + public double getInitialY() { + return initialY; + } + public void setInitialY( double initialY ) { + this.initialY = initialY; + } + + public double getMaxY() { + return maxY; + } + public void setMaxY( double maxY ) { + this.maxY = maxY; + } + + public double getBoost() { + return boost; + } + public void setBoost( double boost ) { + this.boost = boost; + } + + public int getBoostCount() { + return boostCount; + } + public void setBoostCount( int boostCount ) { + this.boostCount = boostCount; + } + + public double getVelocityY() { + return velocityY; + } + public void setVelocityY( double velocityY ) { + this.velocityY = velocityY; + } + + public boolean isJumping() { + return jumping; + } + public void setJumping( boolean jumping ) { + this.jumping = jumping; + } + + public double getRecordHeight() { + return recordHeight; + } + public void setRecordHeight( double recordHeight ) { + this.recordHeight = recordHeight; + } + + public double getRecordBoost() { + return recordBoost; + } + public void setRecordBoost( double recordBoost ) { + this.recordBoost = recordBoost; + } + + public double getRecordY() { + return recordY; + } + public void setRecordY( double recordY ) { + this.recordY = recordY; + } + + public double getRecordVelocity() { + return recordVelocity; + } + public void setRecordVelocity( double recordVelocity ) { + this.recordVelocity = recordVelocity; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventListener.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventListener.java new file mode 100644 index 000000000..44c3d1399 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/player/SlimeBlockFunEventListener.java @@ -0,0 +1,241 @@ +package tech.mcprison.prison.spigot.player; + +import java.text.DecimalFormat; +import java.util.TreeMap; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import tech.mcprison.prison.spigot.SpigotPrison; + +public class SlimeBlockFunEventListener + implements Listener { + + private boolean enabled = false; + + private final TreeMap playerSlimeJumpCache; + + + public SlimeBlockFunEventListener() { + super(); + + this.playerSlimeJumpCache = new TreeMap<>(); + + this.enabled = SpigotPrison.getInstance().getConfig().getBoolean("slime-fun"); + + } + + @EventHandler(priority=EventPriority.LOW) + public void onPlayerJumpOnSlime(PlayerMoveEvent e ) { + if ( !isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + + Location loc = player.getLocation(); + Location loc1 = loc.clone(); + loc1.setY( loc.getY() - 1 ); + Block onBlock1 = loc1.getBlock(); + + if ( onBlock1.getType() == Material.AIR || + onBlock1.getType() == Material.SLIME_BLOCK + ) { + + Vector velocity = player.getVelocity(); + double velY = velocity.getY(); + + // Record player's System time stamp on Jump Boost: + Long playerUUIDLSB = Long.valueOf( e.getPlayer().getUniqueId().getLeastSignificantBits() ); + + if ( onBlock1.getType() == Material.AIR && + getPlayerSlimeJumpCache().containsKey( playerUUIDLSB ) ) { + + SlimeBlockFunEventData moveEventData = getPlayerSlimeJumpCache().get( playerUUIDLSB ); + + if ( moveEventData != null ) { + moveEventData.inAir( loc1.getY(), player ); + } + + } + else if ( velY > 0.2 ) { + + // checking and boosting over two blocks below feet caused server to crash due to too much boost! :) + // Too much boost, multiplied by the vector will crash the server if it exceeds 1024, of which + // is prevented... so don't worry about it. :) +// Location loc2 = loc1.clone(); +// loc2.setY( loc2.getY() - 1 ); +// Block onBlock2 = loc2.getBlock(); + + if ( onBlock1.getType() == Material.SLIME_BLOCK +// || onBlock2.getType() == Material.SLIME_BLOCK + ) { + + ItemStack itemInHand = SpigotPrison.getInstance().getCompatibility().getItemInMainHand( player ); + + double boost = getBoost( itemInHand ); + + + // Record player's System time stamp on Jump Boost: + // Add player jump data to the cache: + if ( !getPlayerSlimeJumpCache().containsKey( playerUUIDLSB ) ) { + SlimeBlockFunEventData moveEventData = new SlimeBlockFunEventData( playerUUIDLSB, loc.getY() ); + getPlayerSlimeJumpCache().put( playerUUIDLSB, moveEventData ); + + player.sendMessage( "SlimeFun: Use at your own risk. Jumpping out of the " + + "world may crash the server." ); + } + + getPlayerSlimeJumpCache().get( playerUUIDLSB ) + .addJumpEvent( loc1.getY(), boost, velY ); + + Vector newVelocity = calculateVelocityY( boost, velocity, player ); + + player.setVelocity( newVelocity ); + + } + } + + } + } + + /** + *

    If max velocity exceeds 1024 then the server could crash. + * This function makes sure that the calculated velocity for y does not + * exceed 1024; if it does, then it is set to 1024. + *

    + * + * @param boost + * @param velocityOriginal + * @param player + * @return + */ + private Vector calculateVelocityY( double boost, Vector velocityOriginal, Player player ) { + Vector newVelocity = velocityOriginal.clone(); + double velocityY = newVelocity.getY() * boost; + + if ( velocityY > 1024.0 ) { + DecimalFormat f4Fmt = new DecimalFormat("#,##0.0000"); + + player.sendMessage( "SlimeFun: Exceeded max velocity!! velY:" + + f4Fmt.format( velocityY ) ); + + velocityY = 1024.0; + } + + newVelocity.setY( velocityY ); + + return newVelocity; + } + + private double getBoost( ItemStack itemInHand ) + { + double boost = 1.27d; + + + // Due to variations with gold and wood PickAxe need to use a dynamic + // Material name selection which will fit for the version of MC that is + // being ran. + Material holding = itemInHand.getType(); + + if ( holding == Material.DIAMOND_PICKAXE ) { + boost *= 3.0; + } + else if ( holding == Material.matchMaterial( "GOLD_PICKAXE" ) || + holding == Material.IRON_PICKAXE || + holding == Material.STONE_PICKAXE || + holding == Material.matchMaterial( "WOOD_PICKAXE" ) ) { + boost *= 2.85; + } + else if ( holding == Material.DIAMOND_BLOCK ) { + boost *= 1.65; + } + else if ( holding == Material.GOLD_BLOCK ) { + boost *= 1.45; + } + else if ( holding == Material.IRON_BLOCK ) { + boost *= 1.20; + } + + +// switch ( itemInHand.getType() ) +// { +// case DIAMOND_PICKAXE: +// boost *= 2.0; +// break; +// +// case GOLD_PICKAXE: +// case IRON_PICKAXE: +// case STONE_PICKAXE: +// case WOOD_PICKAXE: +// boost *= 2.85; +// break; +// +// case DIAMOND_BLOCK: +// boost *= 1.65; +// break; +// +// case GOLD_BLOCK: +// boost *= 1.45; +// break; +// +// case IRON_BLOCK: +// boost *= 1.20; +// break; +// +// +// default: +// break; +// } + return boost; + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onEntityDamageEvent(final EntityDamageEvent e) { + if (!isEnabled() || !(e.getEntity() instanceof Player)) { + return; + } + + if (e.getCause() == DamageCause.FALL) { + + Player p = (Player) e.getEntity(); + + // Record player's System time stamp on Jump Boost: + Long playerUUIDLSB = Long.valueOf( p.getUniqueId().getLeastSignificantBits() ); + if ( getPlayerSlimeJumpCache().containsKey( playerUUIDLSB )) { + SlimeBlockFunEventData moveEventData = getPlayerSlimeJumpCache().get( playerUUIDLSB ); + + // If player jumped on slime within 16 seconds, cancel fall damage: + if ( moveEventData.hasLanded(p) ) { + + e.setCancelled( true ); + p.sendMessage( "By the grace of the Great Slime Block you survive!" ); + } + } + + } + } + + public boolean isEnabled() { + return enabled; + } + public void setEnabled( boolean enabled ) { + this.enabled = enabled; + } + + public TreeMap getPlayerSlimeJumpCache() + { + return playerSlimeJumpCache; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/scoreboard/SpigotScore.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/scoreboard/SpigotScore.java index e2afc7308..4ee392035 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/scoreboard/SpigotScore.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/scoreboard/SpigotScore.java @@ -1,6 +1,6 @@ /* * Prison is a Minecraft plugin for the prison game mode. - * Copyright (C) 2017 The Prison Team + * Copyright (C) 2017-2020 The Prison Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllCommands.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllCommands.java new file mode 100644 index 000000000..b6f4a82dc --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllCommands.java @@ -0,0 +1,447 @@ +package tech.mcprison.prison.spigot.sellall; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.integration.EconomyIntegration; +import tech.mcprison.prison.integration.IntegrationType; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.modules.ModuleManager; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.spigot.SpigotPrison; +import tech.mcprison.prison.spigot.game.SpigotPlayer; +import tech.mcprison.prison.spigot.gui.sellall.SellAllAdminGUI; +import tech.mcprison.prison.spigot.gui.sellall.SellAllPlayerGUI; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; +import java.util.Set; + +/** + * @author GABRYCA + */ +public class SellAllCommands implements CommandExecutor { + + // Check if the SellAll's enabled + public static boolean isEnabled() { + return Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("sellall")).equalsIgnoreCase("true"); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + // Check if the sellall feature's enabled once again + if (!isEnabled()){ + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR]&c Sorry but the SellAll Feature's disabled in the config.yml")); + return true; + } + + // Get the config and file + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/SellAllConfig.yml"); + FileConfiguration conf = YamlConfiguration.loadConfiguration(file); + + // If the args are 0 and the player's a prison admin or OP he'll get an help message, else will be like a shortcut of the /sellall sell command + if (args.length == 0){ + if (sender.hasPermission("prison.admin") || sender.isOp()) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Please use a command like /sellall sell-gui-add-delete-multiplier-setdefault")); + } else { + return sellallCommandSell(sender, conf); + } + return true; + } + + // Open the GUI + if (args[0].equalsIgnoreCase("gui")){ + + return sellallCommandGUI(sender, conf); + + // sellall sell + } else if (args[0].equalsIgnoreCase("sell")){ + + return sellallCommandSell(sender, conf); + + // sellall add + } else if (args[0].equalsIgnoreCase("add")){ + + return sellallCommandAdd(sender, args, file, conf); + + // sellall delete + } else if (args[0].equalsIgnoreCase("delete")){ + + return sellallCommandDelete(sender, args, file, conf); + + // sellall edit + } else if (args[0].equalsIgnoreCase("edit")){ + + return sellallCommandEdit(sender, args, file, conf, "] edited with success!"); + + // sellall multiplier add/delete + } else if (args[0].equalsIgnoreCase("multiplier")){ + + return sellAllMultipliers(sender, args, file, conf); + + // sellall setdefault will set default values for some default Prison servers blocks + } else if (args[0].equalsIgnoreCase("setdefault")){ + + values(sender); + + return true; + + } + + // sellall will give this error message + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] The first argument's not found, please try /sellall for a list of them!")); + + return true; + } + + private void valueSaver(String material, int value, CommandSender sender){ + Material materialM = Material.matchMaterial(material); + if (materialM == null){ + return; + } else { + material = materialM.name(); + } + Bukkit.dispatchCommand(sender, "sellall add " + material + " " + value); + } + + private void values(CommandSender sender) { + valueSaver("COBBLESTONE", 50, sender); + valueSaver("STONE", 50, sender); + valueSaver("COAL",50, sender); + valueSaver("COAL_BLOCK", 450, sender); + valueSaver("IRON_INGOT",75, sender); + valueSaver("IRON_BLOCK",600, sender); + valueSaver("REDSTONE", 101, sender); + valueSaver("REDSTONE_BLOCK", 909, sender); + valueSaver("GOLD_INGOT", 122, sender); + valueSaver("GOLD_BLOCK", 1100, sender); + valueSaver("DIAMOND", 200, sender); + valueSaver("DIAMOND_BLOCK", 1800, sender); + valueSaver("EMERALD", 300, sender); + valueSaver("EMERALD_BLOCK", 2700, sender); + valueSaver("LAPIS_LAZULI", 70, sender); + valueSaver("LAPIS_BLOCK", 630, sender); + } + + + + private boolean sellAllMultipliers(CommandSender sender, String[] args, File file, FileConfiguration conf) { + if (!(Objects.requireNonNull(conf.getString("Options.Multiplier_Enabled")).equalsIgnoreCase("true"))){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cMultipliers are disabled in the SellAll config!")); + return true; + } + + if (args.length < 3){ + sender.sendMessage(SpigotPrison.format("&c[PRISON WARN] &cWrong format, please use /sellall multiplier add/delete ")); + return true; + } + + if (Objects.requireNonNull(conf.getString("Options.Multiplier_Command_Permission_Enabled")).equalsIgnoreCase("true")){ + if (!(sender.hasPermission(Objects.requireNonNull(conf.getString("Options.Multiplier_Command_Permission"))))){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cSorry, but you don't have the permission [" + conf.getString("Options.Multiplier_Command_Permission") + "]")); + return true; + } + } + + if (args[1].equalsIgnoreCase("add")) { + // Add check if the Prestige is present + // Add to the SellAllCommandSell the check for Player prestige, if prestiges are enabled and if player have a prestige or use default, also if there're multipliers for that prestige or use default + + ModuleManager modMan = Prison.get().getModuleManager(); + Module module = modMan == null ? null : modMan.getModule(PrisonRanks.MODULE_NAME).orElse(null); + + PrisonRanks rankPlugin = (PrisonRanks) module; + if (rankPlugin == null) { + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR] &cThe Ranks module's disabled or not found!")); + return true; + } + + boolean isPrestigeLadder = rankPlugin.getLadderManager().getLadder("prestiges").isPresent(); + + if (!isPrestigeLadder) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cCan't find a -prestiges- ladder, they might be disabled in the config.yml.")); + return true; + } + + boolean isARank = rankPlugin.getRankManager().getRank(args[1]).isPresent(); + + if (!isARank) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cCan't find the Prestige/Rank: " + args[2])); + return true; + } + + boolean isInPrestigeLadder = rankPlugin.getLadderManager().getLadder("prestiges").get().containsRank(rankPlugin.getRankManager().getRank(args[1]).get().id); + + if (!isInPrestigeLadder) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cThe -prestiges- ladder doesn't contains the Rank: " + args[2])); + return true; + } + + double multiplier; + try { + multiplier = Double.parseDouble(args[3]); + } catch (NumberFormatException ex) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry but the multiplier isn't a number [/sellall multiplier add " + args[2] + " Here-> " + args[2] + " <-")); + return true; + } + + conf.set("Multiplier." + args[2] + ".PRESTIGE_NAME", args[2]); + conf.set("Multiplier." + args[2] + ".MULTIPLIER", multiplier); + try { + conf.save(file); + } catch (IOException e) { + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR] &cSorry, the config didn't save with success!")); + return true; + } + + sender.sendMessage(SpigotPrison.format("&3[PRISON] &aMultiplier got added or edited with success!")); + + return true; + } else if (args[1].equalsIgnoreCase("delete")){ + + if (args.length != 3){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cPlease use a format like /sellall multiplier delete ")); + return true; + } + + if (conf.getConfigurationSection("Multiplier." + args[2]) == null){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cCan't find the Multiplier of the prestige " + args[2] + " in the sellallconfig.yml")); + return true; + } + + conf.set("Multiplier." + args[2], null); + try { + conf.save(file); + } catch (IOException e) { + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR] &cSorry, the config didn't save with success!")); + return true; + } + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &aMultiplier deleted with success!")); + + return true; + } + + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN] &cThe format's wrong, try again /sellall for info.")); + return true; + } + + + + private boolean sellallCommandEdit(CommandSender sender, String[] args, File file, FileConfiguration conf, String s) { + + if (Objects.requireNonNull(conf.getString("Options.Add_Permission_Enabled")).equalsIgnoreCase("true")) { + if (!sender.hasPermission("Options.Add_Permission")) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry, but you're missing the permission [" + conf.getString("Options.Add_Permission") + "]")); + return true; + } + } + + if (args.length < 2) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Please add an ITEM_ID [example: /sellall add COAL_ORE ]")); + return true; + } + if (args.length < 3) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Please add a price or value for the item [example: /sellall add COAL_ORE 100]")); + return true; + } + if (Material.getMaterial(args[1]) == null) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry but the ITEM_ID's wrong, please check it [/sellall " + args[0] + " Here-> " + args[1] + " <- " + args[2] + "]")); + return true; + } + + double value; + try { + value = Double.parseDouble(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry but the value isn't a number [/sellall " + args[0] + " " + args[1] + " Here-> " + args[2] + " <-]")); + return true; + } + + conf.set("Items." + args[1] + ".ITEM_ID", args[1]); + conf.set("Items." + args[1] + ".ITEM_VALUE", value); + try { + conf.save(file); + } catch (IOException e) { + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR]&c Sorry, an error occurred while saving the config")); + e.printStackTrace(); + } + + sender.sendMessage(SpigotPrison.format("&3[PRISON]&a ITEM [" + args[1] + s)); + + return true; + } + + + + private boolean sellallCommandDelete(CommandSender sender, String[] args, File file, FileConfiguration conf) { + if (Objects.requireNonNull(conf.getString("Options.Delete_Permission_Enabled")).equalsIgnoreCase("true")){ + if (!sender.hasPermission("Options.Delete_Permission")){ + return true; + } + } + + if (args.length < 2){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Please add an ITEM_ID [example: /sellall delete COAL_ORE]")); + return true; + } + + if (conf.getConfigurationSection("Items." + args[1]) == null){ + sender.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c " + args[1] + " not found in the config or got already deleted")); + return true; + } + + conf.set("Items." + args[1] + ".ITEM_ID", null); + conf.set("Items." + args[1] + ".ITEM_VALUE", null); + conf.set("Items." + args[1], null); + try { + conf.save(file); + } catch (IOException e) { + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR]&c Sorry, an error occurred while saving the config")); + e.printStackTrace(); + } + + sender.sendMessage(SpigotPrison.format("&3[PRISON]&a " + args[1] + " Deleted with success!")); + return true; + } + + + + // Essentially an edited shortcut + private boolean sellallCommandAdd(CommandSender sender, String[] args, File file, FileConfiguration conf) { + return sellallCommandEdit(sender, args, file, conf, "] added with success!"); + } + + + + private boolean sellallCommandSell(CommandSender sender, FileConfiguration conf) { + + if (!(sender instanceof Player)){ + sender.sendMessage(SpigotPrison.format("&3[PRISON ERROR]&c You aren't a player")); + return true; + } + + Player p = (Player) sender; + + if (Objects.requireNonNull(conf.getString("Options.Sell_Permission_Enabled")).equalsIgnoreCase("true")){ + if (!p.hasPermission("Options.Sell_Permission")){ + p.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry, but you're missing the permission [" + conf.getString("Options.Sell_Permission") + "]")); + return true; + } + } + + if (!(conf.getConfigurationSection("Items.") == null)){ + + // Get the Items config section + Set items = Objects.requireNonNull(conf.getConfigurationSection("Items")).getKeys(false); + + double moneyToGive = 0; + for (String key : items) { + double amount = 0; + while (p.getInventory().contains(Material.valueOf(conf.getString("Items." + key + ".ITEM_ID")))){ + p.getInventory().removeItem(new ItemStack(Material.valueOf(conf.getString("Items." + key + ".ITEM_ID")),1)); + amount++; + } + moneyToGive = moneyToGive + (Double.parseDouble(Objects.requireNonNull(conf.getString("Items." + key + ".ITEM_VALUE"))) * amount); + } + + // Get Spigot Player + SpigotPlayer sPlayer = new SpigotPlayer(p); + + ModuleManager modMan = Prison.get().getModuleManager(); + Module module = modMan == null ? null : modMan.getModule( PrisonRanks.MODULE_NAME ).orElse( null ); + + PrisonRanks rankPlugin = (PrisonRanks) module; + + if (Objects.requireNonNull(conf.getString("Options.Multiplier_Enabled")).equalsIgnoreCase("true")) { + + boolean hasPlayerPrestige = false; + double multiplier = Double.parseDouble(Objects.requireNonNull(conf.getString("Options.Multiplier_Default"))); + + if (rankPlugin != null) { + if (rankPlugin.getPlayerManager().getPlayer(sPlayer.getUUID(), sPlayer.getName()).isPresent()) { + String playerRankName; + + try { + playerRankName = rankPlugin.getPlayerManager().getPlayer(sPlayer.getUUID(), sPlayer.getName()).get().getRank("prestiges").name; + } catch (NullPointerException ex){ + playerRankName = null; + } + + if (playerRankName != null) { + hasPlayerPrestige = true; + multiplier = Double.parseDouble(Objects.requireNonNull(conf.getString("Multiplier." + playerRankName + ".MULTIPLIER"))); + moneyToGive = moneyToGive * multiplier; + } + } + } + + if (!hasPlayerPrestige) { + moneyToGive = moneyToGive * multiplier; + } + } + + // Get economy + EconomyIntegration economy = (EconomyIntegration) PrisonAPI.getIntegrationManager().getForType(IntegrationType.ECONOMY).orElseThrow(IllegalStateException::new); + // Add balance + economy.addBalance(sPlayer, moneyToGive); + if (moneyToGive<0.001){ + sender.sendMessage(SpigotPrison.format("&3[PRISON]&c You have nothing to sell!")); + } else { + sender.sendMessage(SpigotPrison.format("&3[PRISON]&a You got $" + moneyToGive)); + } + } + + return true; + } + + + + private boolean sellallCommandGUI(CommandSender sender, FileConfiguration conf) { + + if (!(sender instanceof Player)){ + sender.sendMessage(SpigotPrison.format("&c[PRISON ERROR] You aren't a player")); + return true; + } + + Player p = (Player) sender; + + if (!Objects.requireNonNull(conf.getString("Options.GUI_Enabled")).equalsIgnoreCase("true")){ + if (p.isOp() || p.hasPermission("prison.admin")) { + sender.sendMessage(SpigotPrison.format("&c[PRISON ERROR] Sorry but the GUI's disabled in the SellAllConfig.yml")); + return true; + } + } + + if (Objects.requireNonNull(conf.getString("Options.GUI_Permission_Enabled")).equalsIgnoreCase("true")){ + if (!p.hasPermission(Objects.requireNonNull(conf.getString("Options.GUI_Permission")))){ + p.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry, but you're missing the permission [" + conf.getString("Options.GUI_Permission") + "]")); + return true; + } else if (Objects.requireNonNull(conf.getString("Options.Player_GUI_Enabled")).equalsIgnoreCase("true")){ + if (Objects.requireNonNull(conf.getString("Options.Player_GUI_Permission_Enabled")).equalsIgnoreCase("true")) { + if (!p.hasPermission(Objects.requireNonNull(conf.getString("Options.Player_GUI_Permission")))){ + p.sendMessage(SpigotPrison.format("&3[PRISON WARN]&c Sorry, but you're missing the permission [" + conf.getString("Options.Player_GUI_Permission") + "]")); + return true; + } + } + SellAllPlayerGUI gui = new SellAllPlayerGUI(p); + gui.open(); + return true; + } + } + + SellAllAdminGUI gui = new SellAllAdminGUI(p); + gui.open(); + + return true; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllConfig.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllConfig.java new file mode 100644 index 000000000..9c1766690 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllConfig.java @@ -0,0 +1,93 @@ +package tech.mcprison.prison.spigot.sellall; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import tech.mcprison.prison.spigot.SpigotPrison; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +/** + * @author GABRYCA + */ +public class SellAllConfig { + + private FileConfiguration conf; + + public SellAllConfig(){ + + if (!Objects.requireNonNull(SpigotPrison.getInstance().getConfig().getString("sellall")).equalsIgnoreCase("true")){ + return; + } + + // Filepath + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/SellAllConfig.yml"); + + // Everything's here + values(); + + // Get the final config + conf = YamlConfiguration.loadConfiguration(file); + } + + private void dataConfig(String path, String string){ + + // Filepath + File file = new File(SpigotPrison.getInstance().getDataFolder() + "/SellAllConfig.yml"); + + // Check if the config exists + if(!file.exists()){ + try { + file.createNewFile(); + conf = YamlConfiguration.loadConfiguration(file); + conf.set(path, SpigotPrison.format(string)); + conf.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + boolean newValue = false; + conf = YamlConfiguration.loadConfiguration(file); + if (getFileSellAllConfig().getString(path) == null){ + conf.set(path, SpigotPrison.format(string)); + newValue = true; + } + if (newValue) { + conf.save(file); + } + } catch (IOException e2){ + e2.printStackTrace(); + } + } + + // Get the final config + conf = YamlConfiguration.loadConfiguration(file); + + } + + private void values(){ + dataConfig("Options.GUI_Enabled", "true"); + dataConfig("Options.GUI_Permission_Enabled", "true"); + dataConfig("Options.GUI_Permission","prison.admin"); + dataConfig("Options.Sell_Permission_Enabled","false"); + dataConfig("Options.Sell_Permission","prison.admin"); + dataConfig("Options.Add_Permission_Enabled","true"); + dataConfig("Options.Add_Permission","prison.admin"); + dataConfig("Options.Delete_Permission_Enabled","true"); + dataConfig("Options.Delete_Permission","prison.admin"); + dataConfig("Options.Player_GUI_Enabled","true"); + dataConfig("Options.Player_GUI_Permission_Enabled","false"); + dataConfig("Options.Player_GUI_Permission","prison.sellall.playergui"); + dataConfig("Options.Multiplier_Enabled", "false"); + dataConfig("Options.Multiplier_Default", "1"); + dataConfig("Options.Multiplier_Command_Permission_Enabled", "true"); + dataConfig("Options.Multiplier_Command_Permission", "prison.admin"); + } + + public FileConfiguration getFileSellAllConfig(){ + return conf; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionData.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionData.java new file mode 100644 index 000000000..03bc8df3d --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionData.java @@ -0,0 +1,282 @@ +package tech.mcprison.prison.spigot.spiget; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

    This class provides a real comparator for semantic versioning + * for spiget. This addresses issues seen with incorrect notifications. + * Extensive unit tests back the correct functionality of this code base. + *

    + * + *

    To provide a product that actually works with real semVers, the web site + * https://semver.org/ was used as the + * "standard" to base functionality upon, and to which our tests should be + * measured against. + *

    + * + *

    The architecture of a solution is based upon leveraging preexisting industry + * solutions to solve this problem, of which happens to be named regular expressions. + * Through the use of these standards, as provided by semver.org, they take the + * guess work out of re-engineering a solution, and more importantly, they eliminate + * the possible bugs that may result from incorrect interpretation of those given + * standards. + *

    + * + *

    The two regular expressions in the code below (one is commented out) come + * from the following web site: + * https://semver.org/. + * Another website that is + * useful in understanding the named pattern regular expression is: + * https://regex101.com/r/Ly7O1x/3/ + *

    + * + * @author RoyalBlueRanger 2020-01-28 + */ +public class BluesSemanticVersionData + implements Comparable +{ + private final String patternNamed = + "^(?0|[1-9]\\d*)\\." + // major + "(?0|[1-9]\\d*)\\." + // minor + "(?0|[1-9]\\d*)" + // patch + "(?:-(?(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)" + + "(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" + // prerelease + "(?:\\+(?[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" // buildmetadata + ; + +// private final String pattern = +// "^(0|[1-9]\\d*)\\." + // major +// "(0|[1-9]\\d*)\\." + // minor +// "(0|[1-9]\\d*)" + // patch +// "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)" + +// "(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))" + // prerelease +// "?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"; // buildmetadata + + private String original; + private boolean valid = false; + + private Integer major = null; + private Integer minor = null; + private Integer patch = null; + private String prerelease = null; + private String buildmetadata = null; + + private List messages; + + public enum GroupNames { + major, + minor, + patch, + prerelease, + buildmetadata; + } + + public BluesSemanticVersionData( String semVerStr ) { + this.original = semVerStr; + this.messages = new ArrayList<>(); + + Pattern r = Pattern.compile(patternNamed); + // Pattern r = Pattern.compile(pattern); + Matcher m = r.matcher(semVerStr); + + this.valid = m.find(); + + if ( isValid() ) { + this.major = parseInt(m, GroupNames.major); + this.minor = parseInt(m, GroupNames.minor); + this.patch = parseInt(m, GroupNames.patch); + + this.prerelease = parseString(m, GroupNames.prerelease); + this.buildmetadata = parseString(m, GroupNames.buildmetadata); + } + } + + /** + *

    This toString function generates meaningful representation of the original + * semantic version String, but it does not fully try to reproduce it in its + * entirety. There may be important differences, but it should be understood that + * the primary purpose of this class is to establish a precedence, not to reproduce + * a String value. That said, this should be able to reproduce it fairly well. + *

    + * + *

    This function also indicates if the parsing of the source was successful or not, + * plus it always includes the original for comparison. Generally, this function + * is seen to be most useful during live debugging. It's also used in some + * unit tests, but that is mostly to help catch slight abnormalities if they + * should arise, but should not be depended upon. + *

    + * + */ + public String toString() { + return + (getMajor() == null ? "" : getMajor()) + "." + + (getMinor() == null ? "" : getMinor()) + "." + + (getPatch() == null ? "" : getPatch()) + + (getPrerelease() == null ? "" : "-" + getPrerelease() ) + + (getBuildmetadata() == null ? "" : "+" + getBuildmetadata()) + + " (" + (isValid() ? "valid" : "invalid") + ")" + + " [" + getOriginal() + "]"; + } + + /** + *

    Based upon the provided groupName, this function will convert the + * extracted text to an Integer value. If the monitored exception is + * caught, then the error message will be added to the message collection. + *

    + * + * @param m + * @param groupName + * @return an Integer if the value is an integer, otherwise it will be a null + */ + private Integer parseInt( Matcher m, GroupNames groupName ) + { + Integer result = null; + + String grp = parseString(m, groupName); + if ( grp != null ) { + + try { + result = Integer.parseInt( grp ); + } + catch ( NumberFormatException e ) { + // Should be an integer.... let the null be returned, record in messages: + getMessages().add( "### BluesSemanticVersionData.parseInt :: group " + groupName.name() + " = " + + grp + " ### Error Parsing int value ### " ); + } + } + + return result; + } + + /** + *

    Based upon the provided groupName, extract that group from the matcher. + * If a monitored exception is caught, then the error message will be added to the + * messages collection. + *

    + * + * @param m + * @param groupName + * @return The String value of the selected groupName, otherwise a null if there is an error. + */ + private String parseString( Matcher m, GroupNames groupName ) { + String result = null; + + try { + result = m.group( groupName.name() ); + } + catch ( IllegalArgumentException e ) { + getMessages().add( "### BluesSemanticVersionData.parseString :: group " + groupName.name() + " = ?? " + + " ### Error Capture Group Doesn't Exist in the Regular Expression ### " ); + } + + return result; + } + + + /** + *

    The precedence is specified as defined by + * https://semver.org/#spec-item-11 + * in their specification + * number 11. For sake of Java structures, invalid semantic versions will have the + * lowest order. It should also be noted that if an object has the same major, minor, + * and patch as another object, but yet the object has a prerelease, then it has the + * lower order. If both have prerelease and their major, minor, and patch match, then + * the prereleases should be compared as regular string values to determine order. + *

    + * + *

    It should also be noted that buildmetadata values never factor in to the precedence. + *

    + * + */ + @Override + public int compareTo( BluesSemanticVersionData arg0 ) + { + int result = 0; + + if ( !isValid() || !arg0.isValid() ) { + result = (isValid() ? 1 : arg0.isValid() ? -1 : -1000 ); + } else { + result = getMajor() - arg0.getMajor(); + + if ( result == 0 ) { + result = getMinor() - arg0.getMinor(); + + if ( result == 0 ) { + result = getPatch() - arg0.getPatch(); + + if ( result == 0 ) { + if ( getPrerelease() == null && arg0.getPrerelease() != null ) { + result = 1; + } else if ( getPrerelease() != null && arg0.getPrerelease() == null ) { + result = -1; + } else if ( getPrerelease() != null && arg0.getPrerelease() != null ) { + result = getPrerelease().compareTo( arg0.getPrerelease() ); + } + } + } + } + } + + return result; + } + + + public String getOriginal() { + return original; + } + public void setOriginal( String original ) { + this.original = original; + } + + public boolean isValid() { + return valid; + } + public void setValid( boolean valid ) { + this.valid = valid; + } + + public Integer getMajor() { + return major; + } + public void setMajor( Integer major ) { + this.major = major; + } + + public Integer getMinor() { + return minor; + } + public void setMinor( Integer minor ) { + this.minor = minor; + } + + public Integer getPatch() { + return patch; + } + public void setPatch( Integer patch ) { + this.patch = patch; + } + + public String getPrerelease() { + return prerelease; + } + public void setPrerelease( String prerelease ) { + this.prerelease = prerelease; + } + + public String getBuildmetadata() { + return buildmetadata; + } + public void setBuildmetadata( String buildmetadata ) { + this.buildmetadata = buildmetadata; + } + + public List getMessages() { + return messages; + } + public void setMessages( List messages ) { + this.messages = messages; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparator.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparator.java new file mode 100644 index 000000000..0e9a02d46 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparator.java @@ -0,0 +1,138 @@ +package tech.mcprison.prison.spigot.spiget; + +import org.bukkit.Bukkit; +import org.inventivetalent.update.spiget.comparator.VersionComparator; + +/** + *

    This class provides a real comparator for semantic versioning + * for spiget. This addresses issues seen with incorrect notifications. + * Extensive unit tests back the correct functionality of this code base. + *

    + * + *

    Spiget's versions have totally failed on so many levels that they + * should not imply it has anything to do with semVer at all. + * The inherent problems with the solutions provided by spiget, is that they may work + * some of the time, but then fail once certain incorrect assumptions about semVers + * are realized. Intermittent failures are to be expected. For example, if someone + * is using version 2.3.11 and 2.4.0 is released, spiget's SEM_VER + * comparator will think the older version is newer than the current release. + * Why does that fail? Because they just remove the periods and take the remainder + * characters and then parse them all as one integer, then they compare integers. The + * context of major, minor, and patch is completely lost, plus it does not factor + * in prerelease tagging as semVer supports. + *

    + * + *

    To provide a product that actually works with real semVers, the website + * https://semver.org/ was used as the + * "standard" to base this functionality upon, and to which our tests are + * measured against. + *

    + * + *

    The result of the new architecture is a solution that ends up being very + * simple for this class. + *

    + * + * @author RoyalBlueRanger 2020-01-28 + * @return + */ +public class BluesSpigetSemVerComparator + extends VersionComparator { + + @Override + public boolean isNewer(String currentVersion, String checkVersion) { + return performComparisons( currentVersion, checkVersion); + } + + /** + *

    This function will take two String values and convert both to a + * SemanticVersioningData objects, that in turn, will parse the string + * and encapsulate the full representation as an object. Since this object + * implements comparable, its then as simple as comparing the new semVer to the + * current semVer to find out if it is actually newer. + *

    + * + *

    The semantic versions must be valid. At a minimum they have to + * have a format such as 1.0.0. If one is invalid, then compareTo will + * favor the valid semVer. If both are invalid then compareTo will return + * a negative -1000, which will equate to false result. + *

    + * + * @param currentVersion A String value representing the current semVer + * @param checkVersion A String value representing the checked semVer + * @return True if the checkVersion is a higher semVer than the current version + */ + public boolean performComparisons( String currentVersion, String checkVersion ) { + + BluesSemanticVersionData currentSemVer = new BluesSemanticVersionData(currentVersion); + BluesSemanticVersionData checkSemVer = new BluesSemanticVersionData(checkVersion); + + return (checkSemVer.compareTo( currentSemVer ) > 0); + } + + /** + *

    Example how to use: + *

    + * + *
    +	 * String ver = Bukkit.getVersion().trim();
    +	 * ver = ver.substring( ver.indexOf("(MC: ") + 5, ver.length() -1 );
    +	 * if ( new BluesSpigetSemVerComparator().compareTo(ver, "1.9.0") ) {
    +	 *     // if mc version is less than 1.9.0
    +	 * }
    +	 * 
    + * + * @param currentVersion + * @param checkVersion + * @return + */ + public int compareTo( String currentVersion, String checkVersion ) { + + BluesSemanticVersionData currentSemVer = new BluesSemanticVersionData(currentVersion); + BluesSemanticVersionData checkSemVer = new BluesSemanticVersionData(checkVersion); + + return currentSemVer.compareTo( checkSemVer ); + } + + /** + *

    This uses the minecraft version of the server to compare to the provided version. + *

    + * + *

    Samples of what a bukkit, spigot, and paper version would look like. Notice + * they all have the version at the end between (MC: and ). + *

    + * + *
      + *
    • Spigot 1.8.8: git-Spigot-21fe707-e1ebe52 (MC: 1.8.8)
    • + *
    • Spigot 1.10.2: git-Spigot-de459a2-51263e9 (MC: 1.10.2)
    • + *
    • Spigot 1.12.2: git-Spigot-79a30d7-acbc348 (MC: 1.12.2)
    • + *
    • Spigot 1.15.2: git-Spigot-2040c4c-893ad93 (MC: 1.15.2)
    • + *
    • Paper 1.10.2: git-Paper-916.2 (MC: 1.10.2)
    • + *
    • Paper 1.14.2: git-Paper-234 (MC: 1.14.4)
    • + *
    + * + * *

    Example how to use: + *

    + * + *
    +	 * if ( new BluesSpigetSemVerComparator().compareMCVersionTo("1.9.0") < 0 ) {
    +	 *     // if mc version is less than 1.9.0
    +	 * }
    +	 * 
    + * + * @param checkVersion + * @return + */ + public int compareMCVersionTo( String checkVersion ) { + int results = -1; + String currentVersion = Bukkit.getVersion().trim().toLowerCase(); + int i = currentVersion.indexOf("(mc:"); + int len = currentVersion.length(); + if ( i >= 0 && (i+4 < len)) { + currentVersion = currentVersion.substring( i + 4, len -1 ).trim(); + + results = compareTo( currentVersion, checkVersion ); + } + return results; + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/util/SpigotYamlFileIO.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/util/SpigotYamlFileIO.java new file mode 100644 index 000000000..3e2a0a034 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/util/SpigotYamlFileIO.java @@ -0,0 +1,151 @@ +package tech.mcprison.prison.spigot.util; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import tech.mcprison.prison.file.YamlFileIO; +import tech.mcprison.prison.output.Output; + +public class SpigotYamlFileIO + extends YamlFileIO { + + private YamlConfiguration yamlConfig; + + public SpigotYamlFileIO( File yamlFile ) { + super( yamlFile ); + + this.yamlConfig = new YamlConfiguration(); + + loadYaml(); + } + + @Override + protected boolean loadYaml() { + boolean results = false; + if (!getYamlFile().exists()) { + getYamlFile().getParentFile().mkdirs(); + + try { + getYamlFile().createNewFile(); + } + catch ( IOException e ) { + Output.get().logWarn( + String.format( "Notice: Failed to create a config file. " + + "&cFile: &7%s &cError: &7%s", + getYamlFile().getName(), e.getMessage()) ); + } + } + + try { + yamlConfig.load(getYamlFile()); + results = true; + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + } + return results; + } + + protected boolean saveYaml() { + boolean results = false; + + try { + yamlConfig.save(getYamlFile()); + results = true; + } catch (IOException e) { + e.printStackTrace(); + } + return results; + } + + @Override + public Set getKeys() { + return yamlConfig.getKeys( true ); + } + + @Override + public Map getValues() { + return yamlConfig.getValues( true ); + + } + + @Override + protected void clear() { + Set keys = getKeys(); + for ( String key : keys ) { + yamlConfig.set( key, null ); + } + } + + @Override + protected void set( String key, Object value ) { + yamlConfig.set( key, value ); + } + + @Override + protected void createSection( String key ) { + yamlConfig.createSection( key ) ; + } + + + @Override + protected boolean getBoolean( String key ) { + return yamlConfig.getBoolean( key ) ; + } + @Override + protected boolean getBoolean( String key, boolean defaultValue ) { + return yamlConfig.getBoolean( key, defaultValue ) ; + } + + @Override + protected String getString( String key ) { + return yamlConfig.getString( key ) ; + } + @Override + protected String getString( String key, String defaultValue ) { + return yamlConfig.getString( key, defaultValue ) ; + } + + @Override + protected double getDouble( String key ) { + return yamlConfig.getDouble( key ) ; + } + @Override + protected double getDouble( String key, double defaultValue ) { + return yamlConfig.getDouble( key, defaultValue ) ; + } + + @Override + protected int getInteger( String key ) { + return yamlConfig.getInt( key ) ; + } + @Override + protected int getInteger( String key, int defaultValue ) { + return yamlConfig.getInt( key, defaultValue ) ; + } + + @Override + protected long getLong( String key ) { + return yamlConfig.getLong( key ) ; + } + @Override + protected long getLong( String key, long defaultValue ) { + return yamlConfig.getLong( key, defaultValue ) ; + } + + + public void test( ) { + +// yamlConfig. ; + + + } + + + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6Integration.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6Integration.java new file mode 100644 index 000000000..387c3d226 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6Integration.java @@ -0,0 +1,69 @@ +package tech.mcprison.prison.spigot.worldguard; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import tech.mcprison.prison.integration.WorldGuardIntegration; +import tech.mcprison.prison.output.Output; + +/** + *

    WorldGuard 6 uses com.sk89q.worldguard.bukkit.WorldGuardPlugin. + *

    + * + *
      + *
    • WorldGuard v6.1.0 - mc 1.7 to 1.8
    • + *
    • WorldGuard v6.1.2 - mc 1.9 to 1.10
    • + *
    • WorldGuard v6.2.0 - mc 1.9 to 1.11
    • + *
    • WorldGuard v6.2.2 - mc 1.12
    • + *
    • WorldGuard v7.0.0 - mc 1.13 to 1.14
    • + *
    • WorldGuard v7.0.1 - mc 1.14
    • + *
    • WorldGuard v7.0.2 - mc 1.15
    • + *
    + * + */ +public class WorldGuard6Integration + extends WorldGuardIntegration { + + @SuppressWarnings( "unused" ) + private WorldGuard6IntegrationWrapper wrapper = null; + + public WorldGuard6Integration() { + super( "WorldGuard6", "WorldGuard" ); + + } + + @Override + public void integrate() { + if ( isRegistered()) { + try { + Plugin wgPlugin = Bukkit.getPluginManager().getPlugin(getProviderName()); + if ( wgPlugin != null ) { + String version = wgPlugin.getDescription().getVersion(); + + // If the version starts with 6.x.y then this is the correct + // version of WorldGuard to use with the integration: + setIntegrated( version.startsWith( "6." ) ); + + String message = String.format( "WorldGuardIntegration: keyName=%s version=%s", + getKeyName(), version); + + Output.get().logInfo( message ); + + if ( hasIntegrated() ) { + this.wrapper = new WorldGuard6IntegrationWrapper( wgPlugin ); + } + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } + + + + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6IntegrationWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6IntegrationWrapper.java new file mode 100644 index 000000000..4f873e32c --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard6IntegrationWrapper.java @@ -0,0 +1,10 @@ +package tech.mcprison.prison.spigot.worldguard; + +import org.bukkit.plugin.Plugin; + +public class WorldGuard6IntegrationWrapper { + + public WorldGuard6IntegrationWrapper( Plugin plugin ) { + + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7Integration.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7Integration.java new file mode 100644 index 000000000..72f3aa992 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7Integration.java @@ -0,0 +1,49 @@ +package tech.mcprison.prison.spigot.worldguard; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import tech.mcprison.prison.integration.WorldGuardIntegration; +import tech.mcprison.prison.output.Output; + +/** + *

    WorldGuard 7 uses com.sk89q.worldguard.bukkit.WorldGuardPlugin. + *

    + * + */ +public class WorldGuard7Integration + extends WorldGuardIntegration { + + public WorldGuard7Integration() { + super( "WorldGuard7", "WorldGuard" ); + + } + + @Override + public void integrate() { + if ( isRegistered()) { + try { + Plugin wgPlugin = Bukkit.getPluginManager().getPlugin(getProviderName()); + if ( wgPlugin != null ) { + String version = wgPlugin.getDescription().getVersion(); + + // If the version starts with 7.x.y then this is the correct + // version of WorldGuard to use with the integration: + setIntegrated( version.startsWith( "7." ) ); + + String message = String.format( "WorldGuardIntegration: keyName=%s version=%s", + getKeyName(), version); + + Output.get().logInfo( message ); + } + } + catch ( NoClassDefFoundError | IllegalStateException e ) { + // ignore this exception since it means the plugin was not loaded + } + catch ( Exception e ) { + e.printStackTrace(); + } + } + } + +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7IntegrationWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7IntegrationWrapper.java new file mode 100644 index 000000000..53ea7a1fd --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/worldguard/WorldGuard7IntegrationWrapper.java @@ -0,0 +1,10 @@ +package tech.mcprison.prison.spigot.worldguard; + +import org.bukkit.plugin.Plugin; + +public class WorldGuard7IntegrationWrapper { + + public WorldGuard7IntegrationWrapper( Plugin plugin ) { + + } +} diff --git a/prison-spigot/src/main/resources/config.yml b/prison-spigot/src/main/resources/config.yml index 63aec8faa..d4218e193 100644 --- a/prison-spigot/src/main/resources/config.yml +++ b/prison-spigot/src/main/resources/config.yml @@ -1,15 +1,134 @@ # == == == == == === == == == == == # # Prison 3 Configuration # # Be sure to check your formatting! # +# This must be valid yml. # # == == == == == === == == == == == # +# +# New features have been recently added to prison. These new features +# can add a lot of value to your server, but they may also be in the +# process of growing and evolving. If you experiance any issues, have +# questions, or you have ideas on how to improve upon these items, +# then please visit our discord server. These new features are +# marked with the "NEW" tag below. +# +# -debug: false +# Note: This actually doesn't do anything. +# May have done something with v3.1.1 or earlier. +#debug: false +# Upon server startup prison will check to see if a newer version has been released. check-updates: true + +# Provides unidentifiable metrics so we can better understand what versions +# of minecraft, spigot, server OS, and the Prison plugin version that is +# being used. This is used to understand what versions of spigot should be +# supported, and which versions of prison is actually being used and can +# greatly impact future development efforts. Enabling the +# send-metrics would be greatly appreciatd since it will help us greatly. +# You can view the stats here: https://bstats.org/plugin/bukkit/Prison send-metrics: true + + +# This will show prison based alerts, such as new versions of the plugin are available. +# If an alert is being shown, it can be disabled online by acknowledging it. show-alerts: true + +# NEW: Prison now has prestiges! +# https://github.com/PrisonTeam/Prison/blob/bleeding/docs/prison_docs_107_setting_up_pestiges.md +prestiges: true + +# NEW: This enables new physics to be applied when jumpping on slime blocks. +# When holding different items, like some block types or picks, the player can +# jump even higher to heights of 40 to 60+ blocks. +slime-fun: false + +# NEW: Prison now has GUIs for most features and actions. For more options +# please check the GuiConfig.yml. +# Access with /gui +prison-gui-enabled: true + +# NEW: Enable or disable /mines to open the GUI to players (This won't works +# with OPs or admins who have the permission mines.admin or prison.admin) +# This command's only a shortcut of /prisonmanager mines +mines-gui-enabled: true + +# NEW: Enable or disable /ranks to open the GUI to players (This won't +# works with OPs or admins who have the permission ranks.admin or prison.admin) +# This command's only a shortcut of /prisonmanager ranks +ranks-gui-enabled: true + +# NEW: /ranks prestiges's a shortcut of /prisonmanager prestiges and the same +# as the /prestiges shortcut +ranks-gui-prestiges-enabled: true + +# NEW: /prestiges command which opens the Prestiges GUI, this's basically a +# shortcut of the /prisonmanager prestiges +prestiges-gui-enabled: true + +# NEW: /prestige will open a confirmation GUI if this's on true, if on false +# will use a chat confirmation instead. +prestige-confirm-gui: true + +# There're more options for the GUIs, please edit the GuiConfig.yml + + +# NEW: Enable or disable the SellAll command and sub-commands +# New feature so setting to false since it will override other shop plugins. +sellall: false + + + +# NEW: Broadcast rankups and demotions to the whole server, in all worlds. +# If this value does not exist, or has a true value, it will be broadcasted. +# Any other value besides true, or non-existant, will be treated as false. +broadcast-rankups: true + + + +# NEW: Startup testing of available blocks within prison. Tests three kinds of +# sources: Prison's BlockType, XMaterial's blocks (should mirror what's avaiable +# within spigot/bukkit, and the raw spigot/bukkit source. +# This should only be used to see what's available for the current version of +# spigot that is running on the server. This will not provide any +# extra blocks for any prison environment. +prison-block-compatibility-report: false + + + +# NEW: Placeholder progress bar. +# Can control how many segments are generated. The more segments that +# are displayed the better reactive it will appear to the players. +# Also what is used for the postive and negative parts of the status bar, +# including color code and the text character to use. The color code will +# only apply once, not for each segment. +# You must quote the colors and segments or the yaml file can be fail to load. +placeholder: + bar-segments: 20 + bar-positive-color: "&2" + bar-positive-segment: "#" + bar-negative-color: "&4" + bar-negative-segment: "=" + + + + +# NEW: Non-Functional at this time. Coming soon! +# This is strictly a work in progress right now, and does not work yet. +# This uses a new prison block model for providing the internal block lists +# and for generating the blocks. This allows usage from mc 1.8.x through +# v1.16.x and enables all new blocks for the version the server is running. +# This new model currently does not support custom blocks. +#use-new-prison-block-model: false + + + +# Default language that is used as the primary language. +# See the lang directories under /plugins/Prison/module_conf/ for the +# language files that can be edited. default-language: en_US + # The storage engine that Prison should use to store data. # Ensure that only one is left uncommented out. Also, ensure # that you specify your MongoDB or SQL login credentials if necessary. @@ -17,12 +136,19 @@ storageType: "json" #storageType: "mongo" #storageType: "sql" +# NOTE: The following is not used since SQL and MongoDB are not valid options. # Login credentials for either SQL or MongoDB -database: - enabled: false - type: sql # sql or mongo - host: localhost - database: prison - username: root - password: root - port: 3306 \ No newline at end of file +#database: +# enabled: false +# type: sql # sql or mongo +# host: localhost +# database: prison +# username: root +# password: root +# port: 3306 + + + +# NEW: Non-functional. Coming soon! +#prison-mines-reset-gap: 5000 + diff --git a/prison-spigot/src/main/resources/plugin.yml b/prison-spigot/src/main/resources/plugin.yml index ad7c3ed4a..99196e81f 100644 --- a/prison-spigot/src/main/resources/plugin.yml +++ b/prison-spigot/src/main/resources/plugin.yml @@ -3,7 +3,22 @@ main: tech.mcprison.prison.spigot.SpigotPrison version: "${version}" description: Prison is an all-in-one plugin for the Minecraft prison game mode. website: https://mc-prison.tech -softdepend: [Essentials, Vault, LuckPerms, Multiverse-Core, Multiworld, MVdWPlaceholderAPI] +softdepend: [Essentials, Vault, LuckPerms, Multiverse-Core, Multiworld, MVdWPlaceholderAPI, PlaceholderAPI, GemsEconomy] + +# Older versions than 1.13 will ignore this, but this will allow 1.13 and up to use newer block types? +api-version: 1.13 + + # ====================================================== + # New commands compatible only with spigot + # ====================================================== +commands: + prisonmanager: + description: Call a fancy GUI where manage the ranks and mines + usage: / gui + sellall: + description: SellAll Command and SubCommands + usage: / + permissions: @@ -25,6 +40,15 @@ permissions: prison.alerts.clear.all: description: Clear the alerts for the entire server. + + prison.gui: + description: Opens the Prison's GUI menu. + + prison.prisonmanagergui: + description: Opens the Prison's GUI menu. + + prison.placeholder: + description: Tools to list and test placeholders. # Permission Kits prison.admin: @@ -36,6 +60,9 @@ permissions: prison.alerts: true prison.alerts.clear: true prison.alerts.clear.all: true + prison.gui: true + prison.prisonmanagergui: true + prison.placeholder: true # ================== # Mines permissions @@ -56,31 +83,59 @@ permissions: mines.info: description: Access to the /mines info command. + mines.reset: + description: Access to the /mines reset command. + mines.wand: description: Access to the /mines wand command. mines.list: description: Access to the /mines list command. - mines.reset: - description: Access to the /mines reset command. + mines.skipreset: + description: Access to the /mines set skipReset command. + + mines.zeroblockresetdelay: + description: Access to the /mines set zeroBlockResetDelay command. + mines.stats: + description: Access to the /mines stats command. + + mines.whereami: + description: Access to the /mines whereami command. + + mines.resettime: + description: Access to the /mines set resetTime command. + + mines.notification: + description: Access to the /mines set notification command. + mines.tp: description: Access to the /mines tp command. + mines.command: + description: Access to the /mines command command. + mines.admin: description: Contains all the commands for managing mines. default: op children: - mines.create: true - mines.set: true mines.block: true + mines.create: true mines.delete: true + mines.set: true mines.info: true mines.wand: true mines.list: true mines.reset: true - nubes.tp: true + mines.skipreset: true + mines.zeroblockresetdelay: true + mines.stats: true + mines.whereami: true + mines.resettime: true + mines.notification: true + mines.tp: true + mines.command: true # ================== # Ranks permissions @@ -108,6 +163,16 @@ permissions: ranks.delete: description: Access to the /ranks delete command. + ranks.promote: + description: Access to the /ranks promote command. + + ranks.demote: + description: Access to the /ranks demote command. + + ranks.setrank: + description: Access to the /ranks set rank command. + + ranks.admin: description: Contains all the commands reserved for admins. default: op @@ -119,3 +184,6 @@ permissions: ranks.list: true ranks.create: true ranks.delete: true + ranks.promote: true + ranks.demote: true + ranks.setrank: true diff --git a/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionDataTest.java b/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionDataTest.java new file mode 100644 index 000000000..880d02631 --- /dev/null +++ b/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSemanticVersionDataTest.java @@ -0,0 +1,172 @@ +package tech.mcprison.prison.spigot.spiget; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class BluesSemanticVersionDataTest +{ + + @Test + public void test() + { + + // It should be noted that a valid semantic version has at least + // three segments: + + // Test the creation of a SemanticVersioningData object: + BluesSemanticVersionData svData = new BluesSemanticVersionData("0.1.0"); + assertEquals("0.1.0 (valid) [0.1.0]", svData.toString()); + assertTrue(svData.isValid()); + assertNotNull(svData.getMajor()); + assertNotNull(svData.getMinor()); + assertNotNull(svData.getPatch()); + assertEquals( 0, (long) svData.getMajor() ); + assertEquals( 1, (long) svData.getMinor() ); + assertEquals( 0, (long) svData.getPatch() ); + assertNull( svData.getPrerelease() ); + assertNull( svData.getBuildmetadata() ); + + svData = new BluesSemanticVersionData("0.1.1"); + assertEquals("0.1.1 (valid) [0.1.1]", svData.toString()); + assertTrue(svData.isValid()); + assertEquals( 0, (long) svData.getMajor() ); + assertEquals( 1, (long) svData.getMinor() ); + assertEquals( 1, (long) svData.getPatch() ); + assertNull( svData.getPrerelease() ); + assertNull( svData.getBuildmetadata() ); + + svData = new BluesSemanticVersionData("0.1.2"); + assertEquals("0.1.2 (valid) [0.1.2]", svData.toString()); + assertTrue(svData.isValid()); + assertEquals( 0, (long) svData.getMajor() ); + assertEquals( 1, (long) svData.getMinor() ); + assertEquals( 2, (long) svData.getPatch() ); + assertNull( svData.getPrerelease() ); + assertNull( svData.getBuildmetadata() ); + + svData = new BluesSemanticVersionData("0.1.3-alpha.1"); + assertEquals("0.1.3-alpha.1 (valid) [0.1.3-alpha.1]", svData.toString()); + assertTrue(svData.isValid()); + assertEquals( 0, (long) svData.getMajor() ); + assertEquals( 1, (long) svData.getMinor() ); + assertEquals( 3, (long) svData.getPatch() ); + assertEquals( "alpha.1", svData.getPrerelease() ); + assertNull( svData.getBuildmetadata() ); + + svData = new BluesSemanticVersionData("3.5.3-alpha.3+build0038273"); + assertEquals("3.5.3-alpha.3+build0038273 (valid) [3.5.3-alpha.3+build0038273]", svData.toString()); + assertTrue(svData.isValid()); + assertEquals( 3, (long) svData.getMajor() ); + assertEquals( 5, (long) svData.getMinor() ); + assertEquals( 3, (long) svData.getPatch() ); + assertEquals( "alpha.3", svData.getPrerelease() ); + assertEquals( "build0038273", svData.getBuildmetadata() ); + + + + // Test for a few invalid semVer types: + svData = new BluesSemanticVersionData("3"); + assertEquals(".. (invalid) [3]", svData.toString()); + assertFalse(svData.isValid()); + + svData = new BluesSemanticVersionData("3.2"); + assertEquals(".. (invalid) [3.2]", svData.toString()); + assertFalse(svData.isValid()); + + svData = new BluesSemanticVersionData("3.1-alpha"); + assertEquals(".. (invalid) [3.1-alpha]", svData.toString()); + assertFalse(svData.isValid()); + + svData = new BluesSemanticVersionData("v3.2.0"); + assertEquals(".. (invalid) [v3.2.0]", svData.toString()); + assertFalse(svData.isValid()); + + svData = new BluesSemanticVersionData("03.2.0"); + assertFalse(svData.isValid()); + svData = new BluesSemanticVersionData("v3.2.0.1"); + assertFalse(svData.isValid()); + svData = new BluesSemanticVersionData("3A.2.0"); + assertFalse(svData.isValid()); + svData = new BluesSemanticVersionData("3.A.0"); + assertFalse(svData.isValid()); + svData = new BluesSemanticVersionData("3.2.a"); + assertFalse(svData.isValid()); + + + // Check the compareTo functionality: + svData = new BluesSemanticVersionData("3.37.5"); + assertTrue(svData.isValid()); + + BluesSemanticVersionData svDataCheck = new BluesSemanticVersionData("3.37.5"); + assertTrue(svDataCheck.isValid()); + + // Should be equal: + assertTrue(svDataCheck.compareTo( svData ) == 0 ); + + + svDataCheck = new BluesSemanticVersionData("3.37.6"); + assertTrue(svDataCheck.isValid()); + + // svDataCheck should be greater than svData: + assertTrue(svDataCheck.compareTo( svData ) > 0 ); + + // Check that its transitive, in that svData is less than svDataCheck: + assertTrue(svData.compareTo( svDataCheck ) < 0 ); + + + svDataCheck = new BluesSemanticVersionData("3.37.6-beta.1"); + assertTrue(svDataCheck.isValid()); + + // svDataCheck should be greater than svData, and transitive too: + assertTrue(svDataCheck.compareTo( svData ) > 0 ); + assertTrue(svData.compareTo( svDataCheck ) < 0 ); + + + + svData = new BluesSemanticVersionData("3.37.6-beta.1"); + assertTrue(svData.isValid()); + svDataCheck = new BluesSemanticVersionData("3.37.6-beta.2"); + assertTrue(svDataCheck.isValid()); + + // the beta.2 should be greater than beta.1, they should also be transitive too: + assertTrue(svDataCheck.compareTo( svData ) > 0 ); + assertTrue(svData.compareTo( svDataCheck ) < 0 ); + + + svData = new BluesSemanticVersionData("3.37.6-beta.2"); + assertTrue(svData.isValid()); + svDataCheck = new BluesSemanticVersionData("3.37.6"); + assertTrue(svDataCheck.isValid()); + + + // the version with no prerelease should be greater than beta.2: + assertTrue(svDataCheck.compareTo( svData ) > 0 ); + assertTrue(svData.compareTo( svDataCheck ) < 0 ); + + + svData = new BluesSemanticVersionData("3.37.6"); + assertTrue(svData.isValid()); + svDataCheck = new BluesSemanticVersionData("3.37.6+build20382"); + assertTrue(svDataCheck.isValid()); + + // These should be identical since buildmeta should always be ignored: + assertTrue(svDataCheck.compareTo( svData ) == 0 ); + assertTrue(svData.compareTo( svDataCheck ) == 0 ); + + + // Test the compareTo function: + assertTrue( new BluesSpigetSemVerComparator().compareTo( "1.8.0", "1.9.0" ) < 0 ); + assertTrue( new BluesSpigetSemVerComparator().compareTo( "1.8.8", "1.9.0" ) < 0 ); + assertFalse( new BluesSpigetSemVerComparator().compareTo( "1.9.0", "1.9.0" ) < 0 ); + assertFalse( new BluesSpigetSemVerComparator().compareTo( "1.15.2", "1.9.0" ) < 0 ); + assertTrue( new BluesSpigetSemVerComparator().compareTo( "1.9.0", "1.9.0" ) == 0 ); + assertTrue( new BluesSpigetSemVerComparator().compareTo( "1.15.2", "1.9.0" ) > 0 ); + + } + +} diff --git a/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparatorTest.java b/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparatorTest.java new file mode 100644 index 000000000..76a312a73 --- /dev/null +++ b/prison-spigot/src/test/java/tech/mcprison/prison/spigot/spiget/BluesSpigetSemVerComparatorTest.java @@ -0,0 +1,111 @@ +package tech.mcprison.prison.spigot.spiget; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import tech.mcprison.prison.spigot.spiget.BluesSpigetSemVerComparator; + +public class BluesSpigetSemVerComparatorTest + extends BluesSpigetSemVerComparator +{ + + @Test + public void test() + { + + // Test a few comparisons that should work easily: + + // Two valid semVers, but at same version should return a false: + assertFalse( performComparisons( "1.0.1", "1.0.1" ) ); + + assertFalse( performComparisons( "1.0.2", "1.0.1" ) ); + + // Newer version available, should return true: + assertTrue( performComparisons( "1.0.1", "1.0.2" ) ); + + // Newer version available, should return true: + assertTrue( performComparisons( "1.4.1", "1.5.0" ) ); + + // Newer version available, should return true: + assertTrue( performComparisons( "1.4.61", "1.5.0" ) ); + + + + + + // First test a SINGLE digit number: no decimals: + + // Neither are valid semVersion so should return false: + assertFalse( performComparisons( "1", "1" ) ); + + // Neither are valid semVersion so should return false: + assertFalse( performComparisons( "0", "1" ) ); + + // Test for a larger gap: + assertFalse( performComparisons( "1", "7" ) ); + + // Test for one of them being a double digit: + assertFalse( performComparisons( "1", "17" ) ); + + // Test for one of them being a double digit, but older version's digit a non-one: + assertFalse( performComparisons( "8", "17" ) ); + + // These should all be false since they are all equal: + assertFalse( performComparisons( "1.0", "1.0" ) ); + assertFalse( performComparisons( "1.0.1", "1.0.1" ) ); + + // The following is an invalid semVer so it should return a false: + assertFalse( performComparisons( "2.37.902.181.0.2.4", "2.37.902.181.0.2.4" ) ); + assertFalse( performComparisons( "2.37.902.181.0.2.4", "2.37.902.181.0.2.5" ) ); + + + // This should be false, where current is newer than "new". + // This situation would happen if the developer is running a newer version than + // what has been published: + + // equals... so should be false: + assertFalse( performComparisons( "2.37.902-alpha.181-beta.0+build2.5", + "2.37.902-alpha.181-beta.0+build2.5" ) ); + + // buildmeta should be ignored so does not matter. Should be false: + assertFalse( performComparisons( "2.37.902-alpha.181-beta.0+build2.5", + "2.37.902-alpha.181-beta.0+build2.7.34" ) ); + + // should be true: + assertTrue( performComparisons( "2.37.902-alpha.181-beta.0+build2.5", + "2.37.902-alpha.181-beta.1+build2.7.34" ) ); + + // Newer version has invalid semVer: always return false: + assertFalse( performComparisons( "1.2.3", "1.3" ) ); + + + // Current version has invalid semVer, but newer has valid. Always true: + assertTrue( performComparisons( "1.2", "1.2.1" ) ); + assertTrue( performComparisons( "1.501", "1.2.1" ) ); + assertTrue( performComparisons( "9999901", "1.2.1" ) ); + + + // Test situations with prerelease tagging: + + // First test that these are equal and returns a value of false: + assertFalse( performComparisons( "1.2.3-alpha.1", + "1.2.3-alpha.1" ) ); + + // Newer has alpha.2 and should return true: + assertTrue( performComparisons( "1.2.3-alpha.1", + "1.2.3-alpha.2" ) ); + // current has alpha.2 and should return false: + assertFalse( performComparisons( "1.2.3-alpha.2", + "1.2.3-alpha.1" ) ); + + // Current has alpha.1 and newer has full release, should return true: + assertTrue( performComparisons( "1.2.3-alpha.1", + "1.2.3" ) ); + // Newer has alpha.1 and should return false since full release is ranked + // higher than prerelease: + assertFalse( performComparisons( "1.2.3", + "1.2.3-alpha.1" ) ); + + } +} diff --git a/prison-sponge/build.gradle b/prison-sponge/build.gradle index 29df52707..d85a24f80 100644 --- a/prison-sponge/build.gradle +++ b/prison-sponge/build.gradle @@ -16,6 +16,11 @@ * along with this program. If not, see . */ +/* + * NOTE: Sponge is disabled since it is missing many core functions that will not + * work with current builds. + * + plugins { id 'org.spongepowered.plugin' version '0.8.1' } @@ -33,7 +38,7 @@ repositories { dependencies { annotationProcessor 'org.spongepowered:spongeapi:6.0.0' - compile project(':prison-core') + implementation project(':prison-core') compileOnly 'org.spongepowered:spongeapi:6.0.0' } @@ -54,3 +59,5 @@ shadowJar { } build.dependsOn(shadowJar) + +*/ \ No newline at end of file diff --git a/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePlatform.java b/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePlatform.java index 3d5e3e7af..e75b52a3a 100644 --- a/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePlatform.java +++ b/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePlatform.java @@ -72,7 +72,25 @@ public SpongePlatform(SpongePrison plugin) { .collect(Collectors.toList()); } - @Override public String getPluginVersion() { +// /** +// * Not yet implemented... +// */ +// @Override +// public Optional getOfflinePlayer( String name ) +// { +// return null; +// } +// +// /** +// * Not yet implemented... +// */ +// @Override +// public Optional getOfflinePlayer( UUID uuid ) +// { +// return null; +// } + + @Override public String getPluginVersion() { return Sponge.getPluginManager().getPlugin("prison-sponge") .orElseThrow(IllegalStateException::new).getVersion().orElse("null"); } diff --git a/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePrison.java b/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePrison.java index 6cda3d91c..6af447d60 100644 --- a/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePrison.java +++ b/prison-sponge/src/main/java/tech/mcprison/prison/sponge/SpongePrison.java @@ -20,6 +20,7 @@ import com.google.inject.Inject; import org.slf4j.Logger; +import org.spongepowered.api.Sponge; import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.game.state.GameStartedServerEvent; @@ -45,7 +46,7 @@ public class SpongePrison { if (!configDir.toFile().exists()) { configDir.toFile().mkdirs(); } - Prison.get().init(new SpongePlatform(this)); + Prison.get().init(new SpongePlatform(this), Sponge.getPlatform().getMinecraftVersion().getName()); } public Logger getLogger() { diff --git a/settings.gradle b/settings.gradle index 8304f5dfc..fdd891d2b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,10 +16,11 @@ * along with this program. If not, see . */ + rootProject.name = 'prison' include 'prison-core' include 'prison-spigot' -include 'prison-sponge' +/* include 'prison-sponge' */ include 'prison-mines' include 'prison-ranks'