diff --git a/docs/changelog_v3.3.x.md b/docs/changelog_v3.3.x.md index 3702a8db8..abeda4468 100644 --- a/docs/changelog_v3.3.x.md +++ b/docs/changelog_v3.3.x.md @@ -11,7 +11,327 @@ These build logs represent the work that has been going on within prison. -# 3.3.0-alpha.12 2022-06-25 + +# 3.3.0-alpha.13 2022-08-25 + + +**3.3.0-alpha.13 2022-08-25** + +Highlights of some of the changes included in this alpha.13 release. Please see the change logs for all details. + + +* Added a new tool: `mines tp list` which will show a player all of the mines they have access to. They can also click on a listed mine to generate the TP command. This command can also be ran from the console to inspect what players have access to. +* Fixed a recently introduced bug where if the server starts up, but someone has no ranks, it was not able to properly assign them their first default rank. It was leading to circular references. +* Fixed an issue with color codes not being translated correctly with placeholderAPI. +* Prison has a rank cost multiplier where ranks on different ladders can increase, or decrease, the cost of all ranks the player buys. So when they prestige, it makes ranks A-Z cost more each time. What's new is that now you can control which ladders these rank cost multipliers are applied to, such as not on prestiges, but only on default. +* Fixed calculations of the placeholder `prison_rank__player_cost_rankname`. It was not fully working with every possible rank on every possible ladder. Now it works correctly if trying to get the player's cost for even many prestige ranks out (it includes cals for all A-Z mines at multiple passes). +* Mine bombs: Changed to only allow mine bombs to be setoff withn mines the player has access to. Fixed an issue with color codes within the mine bomb's tags. +* Fixes issues with NBT, color codes with prison broadcast commands. +* Rewrote topN for better performance: `/topn`. Older players are archived within topN and can be queried: `/topn archive`. +* Update ladder details on a few commands. +* Update XSeries from v8.8.0 to v9.0.0 so prison now supports 1.19.x blocks. +* Bug fixes with first join events. Bug fix with a few guis. +* CMI update: If CMI is detected at startup, and delayed startup is not enabled, prison will go in a simple delayed startup mode to allow CMI a chance to enable it's economy through vault. This reduces the learning curve with CMI users. +* New feature: Prison will now make an auto backup of all files in it's directory when it detects a change in version. Can manually backup too. The backup stores temp files then removes them from the server, this helps keep the server clean. +* Update bstats: Gained control of the account and started to add useful custom reports to help zero in on what we need to help support. +* More work on block converts. Will be added in the next alpha releases. +* Bug fixes: mines gui fixes for virtual mines. Sellall bug fixes. Placeholders fixes. + + + + +* **Minor addition to bstats.** + + +* **Player Mine GUI had the wrong calculation for volume which also threw off blocks remaining and percent remaining.** +The calculation for volume was using the surface area and not the total number of blocks. + + +**v3.3.0-alpha.12L 2022-08-25** + + +* **Updates to the bstats....** + + +* **New placeholders: `prison_rank__linked_mine_tag_rankname` and alias `prison_r_lmt_rankname`.** +Similar to `prison_rank__linked_mine_rankname` but uses the mine's tag instead of the mine's name. + + +* **Mine TP list: use mine tags and clickable mines to teleport to them.** + + +* **Mines TP list. Added a new options to mines tp command to list all mines that the player actually has access to.** +Not finished with it... will add clickable links to them when in game. + + +* **There was an unused updated tool in prison. It's against my policy to auto update this plugin, which would need to be consented to anyway, but I feel that admins need to be in full control of updates and know what is included in the updates. There was identified a potential exploit called zip-slip-vulnerability that could hijack a server if malicious zip is extracted. Prison never used this tool, so it's been fully disabled with no intention of reenabling. It may be deleted in the near future.** + + +* **TopN bug fix: If a player was in an archived state, they were not being moved to active when they would login.** + + +* **If the player is holding the mine bomb in their off hand, then remove the inventory from their off hand.** + + +* **v3.3.0-alpha.12k** + + +* **Fixed an issue when starting the server an no ranks exist. Also fixes an issue when starting the server an a player has no rank.** +Was using a mix of really old code, and the latest code, which caused a conflict since neither was doing what it was really supposed to. + + +* **Added the custom bstats report for Prison Vault Plugins.** +This reports all plugins that have been integrated through Vault. This report does not impact any other plugins report. This is segmented by integration type. + + +* **Fixed bug when server starts up when no player ranks exist.** +It will now bypass the player validation until ranks have been configured. + + +* **v3.3.0-alpha.12j 2022-08-21** + + +* **Update bstats to remove old custom reports that are not wanted/needed anymore.** +Added 6 new placeholder reports that classifies placeholders ini various categories related to how they are used within prison. Any placeholder that appears in these lists, will not be included in the generic 4-category placeholder lists. +Added a few more simple pie charts to cover a lot of the details on ranks, ladders, and players. Simple is better so you can just glance at all of them, without having to drill down on each one. + + +* **v3.3.0-alpha.12i 2022-09-19** + + +* **TopN players - fixed an issue where topN was being processed before the offline players were validated and fixed.** +There was an issue with processing an invalid player that did not have a default rank. + + +* **v3.3.0-alpha.12h 2022-08-19** + + +* **Rankup costs: Minor clean up of existing code. Using the calculateTargetPlayerRank function within the RankPlayer object.** + + +* **PAPI Placeholders: Force color code translations on all resulting placeholders.** +There were a few issues where placeholder color codes were not being properly translated. This was not consistent with everyone. Not sure why it was working for most. +These changes are more in line with how chat handlers and MVdW placeholders works. + + +* **Ladder: apply rank cost multiplier to a ladder or not.** +This new feature enables you to disable all rank cost multipliers for a specific ladder. Normally that rank cost multiplier applies to all ladders, but now you can suppress it. It's for the whole ladder, and not on a per rank basis. + + +* **Fixed an issue with calculating the player's rank cost when they already on the presetiges ladder and calculating the higher prestige ranks.** +Appears as if this becomes an issue when at the last rank on the default ladder. + + +* **v3.3.0-alpha12g 2022-08-14** + + +* **Fxing of the calculations of the placeholder prison_rank__player_cost_rankname and related placeholders.** +The original implementation did not take in to consideration the prestige ranks in relation to the default rank. +The improvements in this calculation now generates a list of all ranks between the current rank and the target rank. So if a few prestige ranks out from the player's current prestige rank will result in calculating every rank in between including multiple passes through all default ranks. So if there are 26 default ranks, and the player is at rank A with no prestiges, then to calculate rank P4 would include the following ranks: +b --> z + p1 + a --> z + p2 + a --> z + p3 + a --> z + p4. +This results in a total of 107 ranks that must be collected, then the player's cost for each rank will have to be calculated. Then all of these must be added together to get the player's cost on rank P4. +This calculation has to be performed for each rank in it's entirety +Warning: this calculation on high prestige ranks will be a performance issue. If this becomes a problem on any particular server, then the only recommendation that can be provided is not to use any of the prison_rank__player_cost placeholders. + + +* **TopN : a few more adjustments to fix a few issues with duplicates and also with using values from within the topN to include in the report to help minimize the need to recalculate everything especially with archived entries.** + + +* **Mine bombs: Fixed an issue with the mine bomb names not always working with color codes.** +Honestly the wrong function was being used so how it even worked I don't know. lol + + +* **New topN functionality: far better performance, with regular updates.** +TopN now is a singleton and is self contained. When the singleton is instantiated, it then loads and setup the prisonTopN.json file on the first run. 30 seconds after the initial load, it then hits all players to load their balances in an async thread. +The command /ranks topn, or just /topn has new parameter: "archived". Any player who has not been online for more than 90 days will be marked as archived. The archived option will show just the archived players. +Setup new parameters within config.yml to control the topn behavior with the async task. + + +* **v3.3.0-alpha.12f 2022-08-08 ** (forgot to commit when made this version) + + +* **Mine Bombs: Only allow bombs to be placed when within a mine that the player has access to.** +This will help prevent wasted bombs. + + +* **Fixed an issue with nbt items not having a value for toString().** + + +* **Encode color codes for the prison utils broadcast command.** + + +* **Added an "invalid player name" message to the rankup commands.** +Also added missing messages to the zh_TW.properties file. + + +* **BlockEvents were changed to auto display the existing rows so it's easier for the end user to know which row to select.** +All they need to do is to enter the mine's name, then press enter to submit the command, and then the existing rows details will be shown. Then the user can select the row and complete the command. +Updated docs on block events. + + +* **BlockEvents were changed to auto display the existing rows so it's easier for the end user to know which row to select.** +All they need to do is to enter the mine's name, then press enter to submit the command, and then the existing rows details will be shown. Then the user can select the row and complete the command. + + +* **minor updates for disabled mine reset times. No functional changes were made.** + + +* **Fixed a potential NPE with giving the players overflow blocks, but not sure what the exact cause was, but looked like there was an issue with mapping to a spigot item stack.** + + + **CMI delayed startup: Added new feature to try to auto enable Prison's delayed startup if CMI is detected as an active plugin, and if the delayed startup is disabled within the config.yml.** +This is to help get more CMI users up and running without more effort, but yet still provide the ability to customize how it is triggered. +If CMI is active, there is NO WAY to disable a delayed startup check.* + +* **Added the the option for playerName to the `/rankup` command so the command can be scripted and ran from the console.** + + +* **There was another issue with using `/gui` related to no ladders being loaded.** +This fixes that problem, and it appears like the issue was caused by plugman messing things up. This does not "solve" the problem with ladders not being loaded, but prevents the NPE from happening. + + +* **There was an issue with `/prison reload gui` causing a NPE.** + + +* **Fixed the `/ranks topn` command (`/topn`) to sort the list of players before printing the list.** +The list was being set a server startup time, and if someone would rankup or prestige, it was not reflecting their new position. The list is also now sorted after each rankup. Sorting should be a low cost operation since the list used never is regenerated so the changes made during sorting is minimal at best. + + +* **Added the ability to control the prefix spaces on the unit names.** +NOTE: may need to enable the use of the `core_text__time_units_short` since the long units are not being used. May need to create another placeholder for short/long. It used to be short, so may need to use long with the new placeholder and convert the calcs to the short as the default. +This was requested by PassBL. + + +* **v3.3.0-alpha.12e** + + +* **Fixed issue rank null issues when showing ladder details.** + + +* **Prison backups: Fixed an issue with folders not existing when running the backups the first time.** + + +* **v3.3.0-alpha.12d 2022-07-25** + + +* **Added more information on ladder listing to show name, number of ranks, and rank cost multiplier.** + + +* **bStats update: Added a new bstats custom chart for auto features.** + + +* **Update some docs. Added docs for Prison Backups** +[Prison Backup Document](prison_docs_050_Prison_backups.md) + + +* **Upgrade XSeries from v8.8.0 to v9.0.0** + + +* **Fixed issue with prison version check triggering a backup upon startup.** +It was always bypassing the previous version check, so it was always creating another backup. + + +* **Update bstats by moving to its own class in its own package.** +Added 4 new custom charts to split the plugins in to 4 parts. + + +* **Fixed a few issues with the ranks gui where they were using the wrong message key (placeholder).** + + +* **Prison v3.3.0-alpha.12c** + + + +* **Prison bstats: setup 4 new bstats charts for prison. May change a few charts or add new ones in the near future.** +Got control over the prison bstats so can now add custom stats. + + +* **Prison backups: Created a Prison/backups/versions.log file which gets logs when a new prison version is detected on startup, which also performs a backup.** +All backups are also logged in the versions.log file too. + + +* **v3.3.0-alpha.12b** +- Added the fix for the placeholders. See next note. + + +* **Fixed an issue with placeholders not be properly evaluated; there were 3 sections and they were combined in to one so it would not bypass any.** + + +* **Possible bug fix with first join: it appears like it was inconsistant with running the rank commands. Fixed by rewriting how the first join event is handled.** + + + +* **Prison backups: Added new features where it is generating a stats file in the root of the zip file which contains all of the "prison support submit" items.** +This is just about ready, but lacking support for auto backups when prison versions change, or job submission to run auto backups at regular intervals. + + +* **Setup a prison backup command that will backup all files within the prison plugin folder.** +When finished, it will delete all temp files since they have been included in the backup. +The new command is `/prison support backup help`. + + +* **v3.3.0-alpha.12a** + + +* **Added a new set of intelligent placeholders: these show the tags for the default ladder and prestige ladder, for the "next" rank but are linked together.** +They only apply to the default ladder and the prestige ladders. The tags are only shown if the player has that rank, or if that will become their next rank. +These ONLY show the tags that will be appropriate when the next rank up. So if the can still rankup on the default ladder, then only the default rank shows the next ranks tag. If they are at the end of the default rank, then it will show the next rank on the prestiges ladder; if they do not have a rank there currently, then it will show the next prestige rank with the default rank showing the first rank on that ladder. + + +* **When the command handler starts up, it now logs the pluigin's root command and the command prefix which is used if there are duplicate commands found during bukkit command registration.** + + +* **Bug fix: Placeholders search was missing the assignment of the placeholderKey, which is what would like the search results on the raw placeholders, with the actual data that is tied back to the player.** +In otherwords, without the PlaceholderKey it was not possible to extract the player's data to be displayed within the command: /prison placeholders search. + + +* **Added constants for the default and prestiges ladder name so it does not have to be duplicated all over the place, which can lead to bugs with typos.** + + +* **Sellall bug fix: There wasn't a common point of refernce to check if sellall is enabled. Many locations were directly checking config.yml, but the new setting has been moved to the modules.yml file. ** +If config.yml has sellall enabled in there, it will be used as a secondary setting if the sellall setting in modules.yml is not defined or set to false. Eventually the config.yml setting will be removed. + + +* **Found that bStats was erroring out with the servers hitting the rate limit so this makes a few adjustments to try to get prison to work with bstats.** +Basically plugins that load last will be unable to report their stats since every single plugin that is using bstats submits on it's own, and therefore it quickly reaches the limits. + + +* **BlockConverters: More changes to block converters.** +Added defaults for auto blocking, and for auto features with support for *all* blocks. + + +* **Bug fix: mines gui was not able to handle virtual mines with the internal placeholders. +This bug fix was included with the deployment of alpha.12 to spigotmc.org. + + + +* **Pull Request from release.branch.v3.3.0-alpha.12 to Master - 2022-06-25** + + +This represents about six months of a lot of work with many bug fixes, performance improvements, and new features that have been introduced. The last two alphas were not pulled back to main, but they were released, This PR will preserve the released alpha as it has been published. + +Also, this helps to ensure that this work will not be lost in the event the bleeding branch is lost/removed. Hopefully it won't be, but a lot of work has gone in to it and it will be impossible to recreate the current state of the alpha release. + +This version, v3.3.0-alpha.12, has 300 commits and 323 changed files. The list of actual changes since v3.2.11 is substantial and the change log should be referenced. + +Highlights of some of the changes include (a sparse list): + +* new block model - full support for CustomItems custom blocks - updated XSeries which mean prison supports Spigot 1.19. +* major improvements to auto features - streamlined and new code - higher performance - many bugs eliminated - now supports drop canceling to make prison more compatible with other plugins +* better multi-language support - supports UTF-8 +* Improved rankup - rankup commands are now ran in batch and will not lag the server if players spam it +* rewrite of the async mine resets - next to impossible for mine resets to cause lag - Uses a new intelligence design that will throttle placing blocks as the server load increases, which makes it next to impossible for it to cause lag. +* Enhanced debugging tools - if a server owner is having issues, prison has more useful tools and logging to better identify where the issues are - new areas are not able to log details when in debug mode - debug mode now has a "count down timer" where if debug mode is 8enabled like /prison debug 10 then it will only allow 10 debug messages to print, then it will turn off debug mode automatically. This is very useful on very heavy servers when a lot of players are active... it prevents massive flooding of the console. +* Major rewrite of the placeholder code that identifies which placeholder raw text is tied to, so it can then retrieve and process the data. - Pre-cache that provides mapping to raw text, so once it is mapped, it can prevent the expensive costs of finding the correct placeholder - Added the beginning of tracking stats (through the pre-cache0 and will be adding an actual placeholder cache in the near future. +* Mine Bombs - fixes and enhancements +* Starting to create a new sellall module that will support multiple shops and custom blocks (not just XMaterial names) +* Block Converters - Will allow full customization on all block specific things within auto features - will eliminate all hard coded block +* Started to add NBT support. - Used in mine bombs - Starting to use in GUI's to simplify the complexity of hooking actions up with the menu items. +* Added rank scores and top-n players - Rank score is a fair way to score players within a rank. It's currently the percentage of money they have to rank up (0 to 100 percent), but once they cross the 100% threshold, then 10% of the excess subtracts from their rank score. This prevents camping at levels. +* There is more stuff, some major, a bunch of minor, and many bug fixes along the way. + + + * **v3.3.0-alpha.12 2022-06-25** diff --git a/docs/knownissues_v3.3.x.md b/docs/knownissues_v3.3.x.md index 13385ef25..b736e0fe2 100644 --- a/docs/knownissues_v3.3.x.md +++ b/docs/knownissues_v3.3.x.md @@ -5,9 +5,57 @@ -# TODO Items for v3.3.0-alpha.11 +# TODO Items for v3.3.0-alpha.13 +- In the GUI menu, the config setting: Enchantment_effect_current_rank is off by 1. If D rank it shows E being unlocked. - CITYJD + + +- Add `*all*` to `/mines set notification` - CITYJD + + +- Auto-Forced-Rankups and prestiges when the player has enough money. Kikiisyourfriend + - a new autoFeatures entry + +- Enable Access to prior mines - kikiisyourfriend + - disable to prevent going back to prior rank mines. + + +- DONE: prison_rank__linked_mines_rankname prison_r_lm_rankname provide a placeholder for the mine's tags. B0mer + + +- DONE: Mines tp: option to list all available mines per rank? Thaysa + + +- bStats: + - DONE: Remove Plugins & Prison Ladders + - DONE: Add: modules, Economy (integrations), Perms (integrations), Placeholder (intg) + - DONE: Add: Language + - + + +- `/ranks player` is not including all of the info for rank multipliers like it should have. + - add them + - List total rank multiplier. Show the multiplier per ladder. + + + +- Custom mine shapes - Chain / darragh + + + +- admin - prevent you breaking mine liners - me... on other people's servers. lol + + +- It looks like the following text field is no longer being used, but it should with the placeholders `prison_mines_timeleft_minename` ... looks like it's using the long version. May want to provide a short and long format? Long (the current default) may be way too long... + `core_text__time_units_short=y,m,w,d,h,m,s` + +- There is a potential error with the class tech.mcprison.prison.util.TextMessage in that all of the messages are using `.withReplacements( "%s" )` but yet none of the text components are using parameters. So if a parameter is added to any of those, the `%s` could potentially cause errors!? Need to test and if errors, then remove all of the `.withReplacements()` in that class. + + + + +- DONE: Option to skip applying the rank cost multiplier to a specific ladder. - For v3.3.0 release: @@ -18,8 +66,8 @@ - ranks and ladders - auto prestiges (unlimited) based upon formulas - new file formats - json ORM? - - archiving old players - - more work on top-n rankings + - DONE usind topn: archiving old players + - DONE: more work on top-n rankings - /mines wguard - worldguard hints for mines (generate scripts to run?) - Use patterns that can be edited @@ -53,7 +101,9 @@ - autofeatures BLOCKEVENTS priority - include backpacks on auto sell - Ryankayz -- Top block and top tokens - Phoung Nguyen + +- TopN for tokens - Phoung Nguyen + - (done ??) auto features autosell - tie to sellall's player toggle - Ryankayz @@ -64,8 +114,10 @@ - Mine Resets - Glass block not being removed - harold -- Archive old players - budderman18 +- DONE?: Archive old players - budderman18 - archive all player files to a zip. Remove the originals. If player logs back on, then restore the archives. Prevent startup from adding these players back. + - Archiving players in topN - this may address the archive needs. Part of the issue was related to performance calculations upon startup, which the new topn does address. + - Placeholders - dynamic content - - custom placeholders based upon other primary placeholders? @@ -196,6 +248,47 @@ https://github.com/Auxilor/EcoEnchants/blob/master/eco-core/core-plugin/src/main # Completed tasks +. DONE: Use of placeholders in gui mines is not working correctly - PassBl + - Trying to use: - '&7Test Cost P1: &3%prison_rank__player_cost_formatted_p1%' + - Using deluxe menus - may not be sending player info with placeholders? + - Fixed: complex new calculations for `prison_rank__player_cost` calculations. + + +- DONE: Mine Bomb Name and tags not showing color - Thaysa + - Works with 1.8.8 + - Fails with 1.18.2 - colors are fine in GUIs and commands + + +- DONE: Modified the `/mine blockEvent` commands to auto show the existing rows if only the mine name is entered for the command. It will show the list of rows, then the user can select it, then continue entering the rest of the command. There has been a lot of confusion on how to work with the blockEvent rows since it was not obvious they had to first "list" the blockEvents for that mine. + + +- DONE: If prison cannot hook in to the economy, prison should see if CMI is a plugin on the server, then it should try to enable the delayed startup for CMI. Kind of a forced-auto-enable if CMI is detected. + + +- DONE: Mine bombs - If at rank d and in mine w and I set off a mine bomb, it will not break any blocks, but it will lock them and prevent others at that rank from breaking them. - redonthehead + - player should not be able to "lock" blocks + - DONE: Player should not be able to drop a mine bomb in a mine they don't have access to + + +- DONE: command `/ranks topn` - does not appear to be sorting by prestiges first - redonthehead + - Fixed? Force sort when using the command each time. + - When player's rankup, should probably force a sort. + - The sorting should be somewhat low cost, since if players are in the same position, it short-circuits the sorting so there is less processing. Ie.. if nothing is sorted, it's only one quick pass. If only one player changes, then it's only moving that player. + + - DONE: Issue with topn Rank-Score. When player is at top of default ladder and has not prestiged yet. + - Rank-score looks like it's just their money balance? + + +- DONE: Sellall enablement - needs to be in module.yml, but must enable it in config.yml? + - bug in alpha.12? + + +- DONE: A placeholder in PLAYER that will show the next tag for both default and prestige. - surawesome + - but only show next prestige if on last rank in default. Default will show first rank. + - {prison_rankup_linked_rank_tag} <- [p]\\[d] + - {prison_rankup_linked_rank_tag_prestiges} {prison_rankup_linked_rank_tag_default} + +- DONE: Top block and top tokens - Phoung Nguyen - DONE: When placing a mine, it spams resets while it's setting up the liner and the primary mine. May be an issue with zero block resets and having zero block counts remaining. May want to initially set the block counts to something like 1, then after the mine is done being laid out, then reset it. When it's in "tracer" mode, no blocks exist that can be broken, so a value of 1 cannot be decreased.i diff --git a/docs/prison_docs_000_toc.md b/docs/prison_docs_000_toc.md index 05649f0f3..b28d516d3 100644 --- a/docs/prison_docs_000_toc.md +++ b/docs/prison_docs_000_toc.md @@ -26,20 +26,19 @@
-# Prison Supports Spigot 1.8 through Spigot 1.18.x -# Prison Supports Java 1.8 though Java 17 +# Prison Supports Spigot 1.8 through Spigot 1.19.x +# Prison Supports Java 1.8 though Java 19 # Prison is created for the Spigot Platform, and works on other platforms based upon Spigot -With the release of Spigot 1.17.x, there were a few minor changes that were needed to be made to prison to support Java 16. These were mostly related to a couple of NMS routines that were trying to figure out the player's default language they have selected. Due to new restrictions moving forward with Spigot, the ability to correctly identify the player's default language may not be possible, but prison will still use the selected language setting in the config files. - - -Prison supports both Spigot 1.17.1 and Spigot 1.18.x, along with Java 17. At this time there hasn't been any reports of incompatibilities. Since prison is using a library to support the correct blocks for the version of the server that you are running, we are limited to when updates are released for that library. Luckily they have had a couple of releases and we have applied them to the latest alpha releases. So if you are wanting to maximize the new Spigot 1.18.x experience, please upgrade to the latest alpha release as found on our discord server in the #alpha channel. +Prison supports Spigot 1.19.x, along with Java 17 and 18. At this time there hasn't been any reports of incompatibilities. Since prison is using a library to support the correct blocks for the version of the server that you are running, we are limited to when updates are released for that library. Luckily they have had a couple of releases and we have applied them to the latest alpha releases. So if you are wanting to maximize the new Spigot 1.19 experience, please upgrade to the latest alpha release as found on our discord server in the #alpha channel. ### Newer features and updates in Prison: +* Prison Backups: Prison now has a new feature that will backup (zip) all of the files within it's plugin directory. Prison now will perform an automated backup when it detects a change in Prison's version to help preserve settings from prior versions. See the new [Prison Backup Document](prison_docs_050_Prison_backups.md). + * Auto Configure: Even if you really don't want to use auto configure when setting up your server, it may be worth trying it out just to see what it does. If you're not happy with it, then deleting the `plugins/Prison/` directory will remove "everything" and on the next restart of your server, prison will load for the first time. So if you are just getting started with prison, it's worth a try. @@ -58,6 +57,8 @@ Prison supports both Spigot 1.17.1 and Spigot 1.18.x, along with Java 17. At th These new features are in the planning stages... +* Block Converters: Auto features can be controlled by enabling different blocks to perform different functions, if so desired. Currently the list of blocks is very limited and hard coded in both the configuration files, and also in the source code. But Block Converters will remove all of that, and place the configs in a new json file. The new format will provide a very powerful way to control all block conversions, such as smelting, blocking, and even drops. It will support permissions on all blocks and items, so you could setup a smelting to provide multiple block types, or even provide different block and item types based upon perms. Block Converters will take prison to the next level on mining customizations, but yet simplify auto features in both the auto features configs and code. + * New backpacks: A rewrite of the backpacks that will give a little more flexibility. You will be ble to use them as backpacks, or as vaults. Could even sell, or trade backpacks/vaults with their contents. ETA is unknown since a new storage management system needs to be created. * Mine Effects: The ability to set mine effects for a given mine, or to allow players to buy effects using their earned tokens. Effects could be simple potion effects (haste, night vision), or even effects such as no fall damage, and even flight. Other options could be no-pvp, enable pvp, no block break, no fire, etc... The options are numerous, but will be added a little at a time, and upon request. @@ -224,6 +225,9 @@ Auto configure can get you up and running with as little as two commands. The f # Guides: Setting Up the Server Basics +* [Prison Backup Document](prison_docs_050_Prison_backups.md) Automatic and manual backups of `plugins/Prison/` directory. + + * [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. @@ -324,6 +328,11 @@ Get your prison setup quickly by running the command `/ranks autoCommand` which +* [Setting up Mine Bombs](prison_docs_130_Prison_Mine_Bombs.md) + A work in progress: How to use and setup Prison Mine Bombs. + + + * [Setting up Backpacks](prison_docs_117_setting_up_backpacks.md) How to enable backpacks. diff --git a/docs/prison_docs_050_Prison_backups.md b/docs/prison_docs_050_Prison_backups.md new file mode 100644 index 000000000..b251d3d44 --- /dev/null +++ b/docs/prison_docs_050_Prison_backups.md @@ -0,0 +1,139 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +## Prison - Prison Backups + +Prison has the new capability with v3.3.0-beta.12d to make backups of all of its files. Prison will automatically make a backup when it detects that the server is starting up with a new version of prison. Manual backups are also available. + +All backup files are zip files and can be opened, unzipped, or inflated by almost any zip/compression tool. + +Prison will generate temporary files through different conditions, such as if it detects a possible problem when saving data. During a backup, prison will identify all of these temporary files, and will purge them once they are added to a backup, thus cleaning up the file storage that prison uses. + +If anything needs to be recovered from a backup, then it must be done manually. + + +
+ + +# The Prison Backup Command + +The Prison back command is: + +`/prison support backup ` + +This command generates a manual backup. The notes are used to append to the file name so as to help identify the purpose of the backup. + + +
+ + +# Backup Locations + +The backups are stored in the directory: + +`plugins/Prison/backups/` + +None of the backups are ever removed automatically. If they are to be removed, they must manually be removed. + +
+ + + +# The versions.log file + +All backups are logged in the file: +`plugins/Prison/backups/versions.log` + +Also all detections of version changes for Prison are logged in this file, which is how prison detects when a new version of prison is being used. + +It should be noted that only the last entry in the versions.log file is used to compare to the current version, and if the versions do not match, then it forces a version change backup. The important point here is that "any" change will be detected and trigger a backup, which will work if downgraded or if it skips many versions. + +
+ + + +# The "Backup Stats" Report + +Within each backup, there will be generated one Backup Stats report that will be placed at the root directory of the backup. It is a plain text file. + +This report contains detailed information on what temporary files were backed up and then deleted. + +The rest of this report is a dump of all of the `/prison suppor submit` documents, all combined together in to one document. + +
+ + + +# The Auto Backup on Version Change Detection + + +The problem with backing up the files within Prison upon startup, is that the Backup Stats file cannot be generated because all of the sub-systems that build that report, have not yet been loaded. But there is a risk that on a version update, the original configuration files may be automatically backed up. + +To deal with this issue of starting up and potential changes to the config files, prison, upon startup, will make a backup of all config files. That way, if any are updated upon startup, their prior state and format will have been preserved as a backup file. + +The list of configuration files that are copied as backup files: + + - config.yml + - autoFeaturesConfig.yml + - blockConvertersConfig.json + - modules.yml + - module_conf/mines/mineBombsConfig.json + - SellAllConfig.yml + - GuiConfig.yml + - backpacks/backpacksconfig.yml + +All of these files are copied with a new name that follows this format: +`.newPrisonVersion_.bu` + +The format of the `` is the original file name, as listed above, with the file suffix. + +The format of the `` is: +`yyyy-MM-dd_HH-mm-ss` + + +The location of these backup files will be located in the same directory as the original files. An example of what the `plugins/Prison/` folder enteries would look like is as follows: + + +``` +autoFeaturesConfig.yml +autoFeaturesConfig.yml.newPrisonVersion_2022-07-06_22-18-23.bu +blockConvertersConfig.json +blockConvertersConfig.json.newPrisonVersion_2022-07-06_22-18-23.bu +config.yml +config.yml.newPrisonVersion_2022-07-06_22-18-23.bu +GuiConfig.yml +GuiConfig.yml.newPrisonVersion_2022-07-06_22-18-23.bu +modules.yml +modules.yml.newPrisonVersion_2022-07-06_22-18-23.bu +SellAllConfig.yml +SellAllConfig.yml.newPrisonVersion_2022-07-06_22-18-23.bu +``` + +**Please note:** All of these backup files will be removed once the backup is finished. + +
+ + + +# Temporary Files Will Be Purged after being Backed Up + + +Prison Backups will identify various temporary files. These files will be added to the current backup, and then they will be removed from the server's file system. + +This is a list of identifiers that are used to detect temporary files, where `*` is a wild-card character. + + - `*.bu` + - `*.temp` + - `*.del` + - `_archived_*` + - `*.json.ver_*.txt` + +**Warning:** When temporary files are backed up, they are removed from the server's file system. Therefore the **only** copy of those files are within that specific backup file. Therefore, it is very important that you do not delete these backup files without careful consideration. Deleting them will mean the permanent loss of those files. + + + + + +
+ diff --git a/docs/prison_docs_115_using_BlockEvents.md b/docs/prison_docs_115_using_BlockEvents.md index 0b239dc31..610bf9364 100644 --- a/docs/prison_docs_115_using_BlockEvents.md +++ b/docs/prison_docs_115_using_BlockEvents.md @@ -4,16 +4,16 @@ # BlockEvents -Prison's BlockEvents provides a way to fire commands when a block is broken by a player. Every BlockEvent must have a chance which can range from 0.0000 percent to 100.0 percent. Optionally, you can tie the block event to a permission, to an event type, and for Token Enchant explosions you can filter on the enchantment type that triggered that explosion. You can also filter on a Crazy Enchant blast too. Soon, BlockEvents will be able to filter on block types too. +Prison's BlockEvents provides a way to fire commands when a block is broken by a player. Every BlockEvent must have a chance which can range from 0.0000 percent to 100.0 percent. Optionally, you can tie the block event to a permission, to an event type, and for Token Enchant explosions you can filter on the enchantment type that triggered that explosion. You can also filter on a Crazy Enchant blast too. BlockEvents can even filter on block types so you can only trigger an even on a specific block type. -A single BlockEvent can also have more than one command that is ran together with each command separated by a semi-colon. ";" +A single BlockEvent can consist of more than one command that is ran together with each command separated by a semi-colon. ";" BlockEvents have a taskMode that defines how the specified commands are to be ran. -*Documented updated: 2021-12-03* +*Documented updated: 2022-08-06*
@@ -22,8 +22,9 @@ BlockEvents have a taskMode that defines how the specified commands are to be ra BlockEvents can be access through the command prefixes of: `/mines blockEvent` +`/mines blockEvent block` -There are 9 sub-commands, which includes add and list, which are probably the most important of the commands. +There are 10 sub-commands (two under blocks), which includes add and list, which are probably the most important of the commands. BlockEvent Sub-Commands @@ -38,14 +39,30 @@ The BlockEvent listings can also be included in the following command by includi ## Use of Row Numbers In The BlockEvent Commands: - Due to the complexity of the blockEvent commands being deeply nested, and there could be so many of them, many of the blockEvent commands uses a **Row** identifier. This row identifier is based upon the order of the commands as listed under the `/mines blockEvent list` command. + Due to the complexity of the blockEvent commands being deeply nested, and there could be so many of them, many of the blockEvent commands uses a **Row** identifier to select the blockEvent of interest. This row identifier is based upon the order of the commands as listed under the `/mines blockEvent list` command. + + +All of the blockEvent commands now supports displaying the current list of rows by completing the blockEvent command up to the required mine name. When pressing enter to submit the incomplete command, it will then show the list of rows. + A number of examples of a few different blockEvets is as follows. Emphasis is on the row numbers. + BlockEvent Sub-Commands +# Starting with BlockEvents - Add a new BlockEvent + +It is strongly suggested that you first review the help on the following command: + +`/mines blockEvent add help` + + +That will show the current information on blockEvents. It's also important to understand that the list of **placeholders** is also critical since it provides a great deal of power and flexibility in what commands can be crafted. See the next section on **BlockEvents Placeholders**. + + +The first step of creating blockEvents, is to add them. There are a lot of options that can be used for customizing them and they can become rather complicated, so the add function only requires a percentage value since all other options are optional, and some may not be used. # BlockEvents Placeholders @@ -55,12 +72,33 @@ Within the BlockEvents commands you can use placeholders and Prison will substit For a current list of all blockEvent commands use **placeholders** keyword with the **add** command: +`/mines blockEvent add help` + `/mines blockEvent add placeholders` `{player} {player_uid} {msg} {broadcast} {title} {actionBar} {inline} {inlinePlayer} {sync} {syncPlayer} {blockName} {mineName} {locationWorld} {locationX} {locationY} {locationZ} {coordinates} {worldCoordinates} {blockCoordinates} {blockChance} {blockIsAir} {blocksPlaced} {blockRemaining} {blocksMinedTotal} {mineBlocksRemaining} {mineBlocksRemainingPercent} {mineBlocksTotalMined} {mineBlocksSize} {blockMinedName} {blockMinedNameFormal} {blockMinedBlockType} {eventType} {eventTriggered} {utilsDecay}` + +Some examples showing how to use placeholders in the blockEvents. + + +``` +/mines blockEvent add a 100 {utilsDecay} rainbow {blockCoordinates} AIR 40 {mineName} + +/mines blockevent add A 0.075 prison utils repairAll {player};{actionBar}Your items have been repaired! + +/mines blockevent add A 1 prison utils smelt {player} + +/mines blockEvent add A 5.0 prison utils potionEffect speed 90 2;prison utils potionEffect night_vision 300 1 + +``` + + +

Placeholders Explained

+ + - **{player}** Provides the player's name. @@ -98,27 +136,11 @@ There are four **taskModes** on how BlockEvent commands run. The taskModes can b - **{syncPlayer}** This placeholder is similar to the **{sync}** placeholder, with the exception the commands are ran as the player, so the player must have access to those commands. -NOTE: It is impossible to run a command asynchronously, since the execution of a command must always be in a synchronous thread. Hence you can submit an async task, but then when you run the command, it does so in a sync thread. +NOTE: It is impossible to run a command asynchronously, since the execution of a command must always be in a synchronous thread. Hence you can submit an async task, but then when you run the command, it does so in a sync thread. This explanation may not make a lot of sense, but it creates an additional task, but fails to run in asynchronous mode since it's impossible so it's not even worth using it. -`/mines blockEvent add help` -`/mines blockEvent add placeholders` - - -Some examples showing how to use placeholders in the blockEvents. -``` - -/mines blockEvent add a 100 {utilsDecay} rainbow {blockCoordinates} AIR 40 {mineName} - -/mines blockevent add A 0.075 prison utils repairAll {player};{actionBar}Your items have been repaired! - -/mines blockevent add A 1 prison utils smelt {player} - -/mines blockEvent add A 5.0 prison utils potionEffect speed 90 2;prison utils potionEffect night_vision 300 1 - -``` - **{blockName}** The name of the block that was placed in the mine during the last mine reset. The same name as used and provided within the command `/mines block search`. @@ -206,7 +228,7 @@ The BlockEvent command that is used must be be runnable from the console. It is The structure of the command should be the same as within the console. Do not include a a leading /. Do not quote the command. Use the placeholders mentioned above for referring to the players. -Move than one command can be chained together by using a semi-colon between the commands. The commands do not need to end with a semi-colon. No space needs to follow a semi-colon before the next command. There is no limit imposed upon the length of a command, or commands, but the max length may be controlled by outside factors such as ingame text message constraints. Hence why it may be advisable to enter long commands through the console. +Move than one command can be chained together by using a semi-colon between the commands. The commands do not need to end with a semi-colon. No space needs to follow a semi-colon before the next command. There is no limit imposed upon the length of a command, or commands, but the max length may be controlled by outside factors such as in-game text message constraints. Hence why it may be advisable to enter long commands through the console. See the above examples on how to enter the commands when setting up a BlockEvent. @@ -216,7 +238,7 @@ See the above examples on how to enter the commands when setting up a BlockEvent -# BlockEvents Chance +# BlockEvents Percent Chance The chance that is tied to a block event can range from 100 percent to as low as 0.0001 percent. @@ -324,19 +346,97 @@ You must enter the correct value for this triggered command as used by Token Enc # BlockEvents Blocks -Coming soon. Not yet available. +You can filter blockEvents by block types. You have to already have setup a block command to add a block filter. + + +For example if you only want a specific blockEvent to be fired when redstone is broken. + + +The following shows the available commands: + +`/mines blockEvent block` + + +You can only add and remove blocks. The commands will help step you through selecting the correct values. + + +First, if using the **block add** command, only enter the mine name and press enter. Let's assume we are working with mine A: + + +`/mines blockEvent block add a` + + +This incomplete **block add** command will show you all of the existing block events for mine A. From the list, it will show the row number for each blockEvent. + +``` +>mines blockEvent block add a + --- < Add blocks to a BlockEvent for [A] > ------ (3.3.0-alpha.12e) + Hover over values for more information and clickable actions. + * Row: 1 75.00000% [] all (sync) 'say How's it going?' + * Blocks: [cobblestone iron_ore] + * Row: 2 5.00000% [] all (inline) 'prison utils potionEffect {player} night_vision 600 2' + * Row: 3 25.00000% [] all (inline) '{msg} night vision!;prison utils potionEffect {player} night_vision 600 2' + * Row: 4 0.01000% [] all (inline) '{msg}Hey this is a test Message!' + * Row: 5 100.00000% [] all (inline) '{utilsDecay} obby {blockCoordinates} AIR 40 {mineName}' + * Blocks: [cobblestone] + Select a BlockEvent by row number to add a block + /mines blockEvent block add A [rowBlockEvent] [rowBlockName] + +``` + +Press the **** key on your keyboard and it will show last entered command. Then just hit **** then enter the row number and press ****. For our example, let's pretend we want to add a block to the third row: + + +`/mines blockEvent block add a 3` + + +This incomplete **block add** command will then show the list of blocks in the mine. You select them by choosing the row number for the desired block. So in this example, let's choose Andesite which is row 1. + + +``` +>mines blockEvent block add a 3 + --- < Add blocks to a BlockEvent for [A] > ------ (3.3.0-alpha.12e) + Select a block from this mine by using the block's row number: + /mines blockEvent block add A 3 [rowBlockName] + Row: 1 andesite 5.00000 + Row: 2 cobblestone 95.00000 +``` + +`/mines blockEvent block add a 3 1` +`[22:02:32] [Server thread/INFO]: Block has been added to BlockEvent` + + +So if we list the block events it will now include the newly added block: -Will be able to filter block events based upon block types. Will be able to have more than one block defined for each BlockEvent. + +``` +/mines blockEvent list a + + --- < Add blocks to a BlockEvent for [A] > ------ (3.3.0-alpha.12e) + Hover over values for more information and clickable actions. + * Row: 1 75.00000% [] all (sync) 'say How's it going?' + * Blocks: [cobblestone iron_ore] + * Row: 2 5.00000% [] all (inline) 'prison utils potionEffect {player} night_vision 600 2' + * Row: 3 25.00000% [] all (inline) '{msg} night vision!;prison utils potionEffect {player} night_vision 600 2' + * Blocks: [andesite] + * Row: 4 0.01000% [] all (inline) '{msg}Hey this is a test Message!' + * Row: 5 100.00000% [] all (inline) '{utilsDecay} obby {blockCoordinates} AIR 40 {mineName}' + * Blocks: [cobblestone] +``` +You can add more than one block to each block event. Please note that in row 1 that blockEvent has two blocks tied to that filter. Please ignore the fact that iron_ore is not in this mine (oops).
-# BlockEvents +# BlockEvents ect... + + +More to be added
diff --git a/docs/prison_docs_120_setting_up_BlockConverters.md b/docs/prison_docs_120_setting_up_BlockConverters.md new file mode 100644 index 000000000..8984cc1c6 --- /dev/null +++ b/docs/prison_docs_120_setting_up_BlockConverters.md @@ -0,0 +1,111 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +# BlockConverters + + +Block Converters are a powerful new feature that is used within the Auto Features. +Block Converters provides for a very dynamic way to customize the processing of all block +behaviors within the Auto Features, such as selecting which blocks are to be processed +by the auto features (auto pickup, auto smelt, and auto blocking). + + +Block Converters really shine by being using a given block type to figure out what to +**convert** it to. These conversions are used by smelting and blocking, where is there one +block type as an input, but the output can be none, one, or more other blocks or items. + + +These conversions are powerful in their own right, but what makes them even more powerful, +is that both the input and output blocks can be selectively controlled by permissions. +This type of selectivity can provide a very dynamic result that can vary for each player. +This "filtering" can be used to give different results (blocks) to different players. +For example, the in terms of smelting or blocking, different players, because of their perms, +could get different results and quantities, including multiple items. + + + +*Documented updated: 2022-06-255* + +
+ + +# Editing Block Converters + +There are no in game commands to configure Block Converters. You must manually edit the +configuration file to set up the Block Converters. There is a possibility that a misconfiguration will +result in unexpected behavior or complete failure. Therefore, follow this documentation carefully. + + +If you have questions, please contact our discord server for help. + + +The file to edit: + +`plugins/Prison/BlockConvertersConfig.json` + + +This configuration file is in the JSON format, and therefore, if the json format is made invalid, +then none of the conifgs will be loaded. You can use an online web site to validate and fix invalid +json structures. + +
+ + + +# Block Names + + +The names used to identify the blocks, and or items, must be something that prison understands, and +what can be converted to valid bukkit item stacks so they can be given to the players. + + + +Block names are mostly tied to the names as found within the XSeries' XMaterial object. This helps +to provide a unified name, of which the bukkit names for many items and blocks have changed a number +of times between Spigot 1.8 through Spigot 1.19. Some of the bukkit names actually conflicts with +prior version's names. So this is how prison can provide consistency. If there is a custom +block named, then it will need to include the namespace, a colon, followed by the name. +Example of a custom block name: **CustomItem:compressedCobblestone**. + + + +The best way to ensure you are using the correct names, use the block search to get the names that +prison will understand. There are two different types of block search that you can use, the primary +search excludes non-blocks. + + +``` +/mines block search help +/mines block search stone 3 +``` + + +The following search includes items. Do not use items for a **keyBlockName** or the key for a +BlockConverter since it will never be used. + + +``` +/mines block searchAll help +/mines block searchAll chest +/mines block searchAll gold 2 +``` + + + + +
+ + + +# Block Converters - Enabling Auto Features and Normal Drops + + +Within the `blockConvertersConfig.json` file, are major groups of Block Converters. The group +name **autoPickupFeatures** identifies which blocks can be processed. If a block is not included +in this list, then it will be ignored. NOTE: ignored/excluded blocks may, or may not, be breakable. + + +
+ + diff --git a/docs/prison_docs_130_Prison_Mine_Bombs.md b/docs/prison_docs_130_Prison_Mine_Bombs.md new file mode 100644 index 000000000..53d4d2476 --- /dev/null +++ b/docs/prison_docs_130_Prison_Mine_Bombs.md @@ -0,0 +1,46 @@ + +### Prison Documentation +[Prison Documents - Table of Contents](prison_docs_000_toc.md) + +# Prison Mine Bombs + + +Prison mine bombs... + +*Note: this document is a work in progress.* + + +*Documented updated: 2022-08-06* + +
+ + +Commands: + +``` +/prison utils bomb list help +/prison utils bomb reload +/prison utils bomb give help + +/prison utils bombs help +``` + + + +
+ + +# Prison Mine Bombs Config File + +The Prison Mine Bombs use a configuration file to customize the mine bombs. There are no in-game commands to edit the settings. + +The location of the configuration file is: + +`plugins/Prison/module_conf/mines/mineBombsConfig.json` + + +If this file does not exist, you can have prison auto generate it by using any of the above mine bomb commands. The best may be + +
+
+ diff --git a/docs/prison_docs_310_guide_placeholders.md b/docs/prison_docs_310_guide_placeholders.md index d77a90ddb..1d9b8707e 100644 --- a/docs/prison_docs_310_guide_placeholders.md +++ b/docs/prison_docs_310_guide_placeholders.md @@ -7,7 +7,7 @@ This document covers different aspects of placeholders within Prison. It explains how they work, how to use them, and different ways to use them. -*Documented updated: 2021-12-03* +*Documented updated: 2022-08-14*
@@ -17,7 +17,7 @@ This document covers different aspects of placeholders within Prison. It explai 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 complicated things going on. +On the surface they appear to be simple, but there are a lot of moving parts below the surface, and with Prison, there are even more complicated 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. @@ -39,13 +39,36 @@ Sub-command listing of all placeholders commands: * **/prison placeholders** +> Shows a list of all of the placeholder related commands. + * **/prison placeholders list** + +> Lists all placeholders that are available, including their aliases. Placeholders are grouped by their type, of which there are 10 different kinds. Some are player specific, rank or ladder specific, mine specific, or even hybrid combinations such as player-mine were the mine stats are based upon which mine the player is in. + + * **/prison placeholders search** + +> Provides a simple search for placeholders, optionally using a player's name, and it shows the results with actual live placeholder values. Search values can be one or more word fragments. + + * **/prison placeholders test** +> Somewhat like search, but you have to provide the whole placeholder surrounded by escape characcters. Can provide more than one placeholder too. This is more like testing what you would place within a chat prefix or holographic sign. + + +* **/prison placeholders stats** + +> A new command that currently only shows what placeholders have been hitting prison and their total hit count and the total average duration to process. This tool provides a simple pre-cache to more quickly identify which is the correct placeholder to use with the raw placeholder text. This even include bad placeholder hits so the pre-cache can bypass processing of the junk, which improves server performance. Eventually this will also include a placeholder value cache. + + + * **/prison placeholders reload** +> Reloads and rebuilds all placeholders and then reregisters them. This is performed when adding or removing a new mine or rank. + + + NOTE: the `/prison placeholders reload` command only reloads and registers the placeholders with the placeholder integrations. This would be required to enable placeholders when adding a new mine, a new rank, or a new ladder. If you reload another plugin, such as papi, you may need to reload the placeholders which will re-register them. Prison has been setup to survive a papi restart, but there could still be issues. @@ -59,61 +82,114 @@ NOTE: Information on these command are provided in detail below. Prison has numerous placeholders, with number of different types. Each of these types have different internal requirements and conditions of usage. This is a high overview of the various types and their counts. -* **Player Related:** 92 including aliases + +* **Total Available Placeholders:** 294 including aliases + + +* **Player Related:** 110 including aliases * **Rank Related:** 8 including aliases * **Rankup Related:** 20 including aliases * **Player Balance Related:** 8 including aliases + * **Player Token Balance Related:** 12 including aliases + * **Player Blocks Totals Related: 4 including aliases * **Player Tool Related:** 34 including aliases * **Player Health Related:** 14 including aliases * **Player XP and Levels Related:** 6 including aliases * **Player Walk Speed:** 2 including aliases -* **Ladders Related:** 30 including aliases **times** each ladder - A LADDERS placeholder must include the ladder's name in the placeholder and therefore is static. - +* **Ladders Related:** 32 including aliases **times** each ladder + +> A LADDERS placeholder must include the ladder's name in the placeholder and therefore is static. + +> The ladder based placeholders end with `_laddername`, but they include the following possible prefixes, which may not be exclusive to ladder based placeholders. + +> Ladder placeholder prefixes: + * `prison_rank_` + * `prison_rankup_` + * `prison_player_` + + * **Ranks Related:** 22 including aliases **times** each rank - A RANKS placeholder needs to specify the Rank name as part of the placeholder. Each placeholder must specify a Rank and is static. +> A RANKS placeholder needs to specify the Rank name as part of the placeholder. Each placeholder must specify a Rank and is static. + +> The ranks based placeholders end with `_rankname` but do not require a player to work. They also have a prefix of `prison_rank__` but these prefixes and suffixes are also shared with **RankPlayers** placeholders. See them for specifics. + +> Ranks placeholder prefixes: + * `prison_rank__` * **RankPlayers Related:** 12 including aliases **times** each rank - A RANKPLAYERS placeholder is one that will change based upon the player's own Rank. Each player could have a different rank. Because of this relationship, these can only be used with instances of players such as scoreboards and chat prefixes; they will not work in holographic displays or with signs. +> A RANKPLAYERS placeholder is one that will change based upon the player's own Rank. Each player could have a different rank. Because of this relationship, these can only be used with instances of players such as scoreboards and chat prefixes; they will not work in holographic displays or with signs. + +> RankPlayers placeholders are very similar to the Rank placeholders in respect to the prefixes and suffixes, but these placeholders MUST include a player or they cannot work. Without a player, they cannot return a valid value, and therefore will return either a blank or zero. +> What makes these placeholders stand out from rank placeholders is the prefix includes `player_`. For example: -* **Mines Related:** 18 including aliases **times** each ladder +> RankPlayers placeholder prefixes: + * `prison_rank__player_` + + +> **Performance Warning:** There is a high risk of performance issues when calculating a full GUI page of items that includes the use of placeholders that are based upon the placeholders: `prison_rank__player_cost_rankname`. The reason for this is related to a progressively great number of calculations required to calculate high ranked prestige ranks. The performance impact can include processing and memory usages. + +> For example, if there are 26 default ranks, and the player is at rank A with no prestiges, then to calculate rank P4 would include the following ranks: +b --> z + p1 + a --> z + p2 + a --> z + p3 + a --> z + p4. +This results in a total of 107 ranks that must be collected, then the player's cost for each rank will have to be calculated. Then all of these must be added together to get the player's cost on rank P4. + +> So if there are thousands of prestige ranks, then the calculations will be numerous for each placeholder. * **Player Ladder Balance Related:** 32 including aliases **times** each Mine - A MINES placeholder must specify the Mine mine as part of the placeholder. Each placeholder must specify a mine and is static. +* **Mines Related:** 32 including aliases **times** each ladder + +> A MINES placeholder must specify the Mine mine as part of the placeholder. + +> The Mines related placeholders have the following prefixes, but they are also shared with the MinePlayers placeholders. +* `prison_mines_` +* `prison_top_mine_` + + + +* **MinePlayers Related:** 32 including aliases + +> A MINEPLAYERS placholder is similar to a RankPlayers placeholder in that it is dynamic and you cannot specify a mine's name with it. How it works, is that it provides the related stats for the mine in which the player is standing in. When the player leaves the mine, then these placeholders returns an empty string value (a blank). + +> The MinePLayers placeholders includ the following prefixes, but they are also similar to the Mines related placeholder too: + * `prison_player_` + * `prison_mines_` + -* **MinePlayers Related:** 34 including aliases +* **PLAYERBLOCKS:** 4 including aliases - A MINEPLAYERS placholder is similar to a RankPlayers placeholder in that it is dynamic and you cannot specify a mine's name with it. How it works, is that it provides the related stats for the mine in which the player is standing in. When the player leaves the mine, then these placeholders returns an empty string value (a blank). +> Every block type that a player breaks within a mine is tracked. These provide the stats on those blocks. -* *STATSMINES Related:** 14 including aliases +* **STATSMINES Related:** 14 including aliases - These are a work in progress, but will provide access to some mine stats such as total blocks mined. +> These list the blocks within a mine. They are referred to with a `_nnn_` notation where "nnn" is the line number -* *STATSRANKS Related:** 6 including aliases +* **STATSRANKS Related:** 6 including aliases - These are a work in progress, but will provide access to some rank stats such as top ranked player in that rank. +> Top-n stats for each rank. The rank name must be specified on the placeholder. +* **STATSPLAYERS:** 30 including aliases + +> Various placeholders for the top-n ranked players. These are over all ranks. There are a couple of shortcut placehodlders where one placeholders contain multiple fileds for easier usage. + -**Total base Placeholders:** 240 including aliases -
+
# Requirements @@ -122,11 +198,17 @@ There is always more than one way to do things, and the same goes for having mor **PlaceholderAPI** - [Setting up PlaceholderAPI](prison_docs_0xx_setting_up_PlaceholderAPI.md) - Strongly Suggested if using placeholders. + +**Holographs** - There are actual a couple of holograph display plugins. Most of the references in this document refer to Holographic Displays, but some of the newer plugins can provide the same functionality, plus many new features. +
# Placeholder Theory for Prison +*(needs updating: Prison now has 10 types of placeholders including top-n variations)* + + There are few major types of placeholders in prison: Player, Ranks, Ladders, Mines, and Stats based. With the most recent releases of prison, there have been hybrids added that are a combination of the player placeholders and with mines and ranks. For example there are now ladder based placeholder that allow targeting ranks on a specific ladder, instead of all ladders. Also there are player mine placeholders that report on the mine stats for the mine in which the player is physically located in. The player mine placeholders have no value when the player is not in a mine, and they will change as the player goes from mine to mine. There are also different types of data that are returned in placeholders. Text, numbers, formatted numbers, and bar graphs. @@ -152,7 +234,7 @@ Also, internally, prison only responds to the placeholder name without the escap
-# Placeholder Customization with Placeholder Attributes +# Placeholder Attributes - Customize almost any Prison Placeholder *Since Prison v3.2.4-alpha.2* @@ -160,10 +242,14 @@ Also, internally, prison only responds to the placeholder name without the escap Placeholders within prison can now be dynamically customized without having to make any changes to any configurations. The same placeholder can be used in multiple place, each with different configurations. This is now possible through the use of placeholder attributes and offers a large degree of customization. -The placeholder attributes is additional text that is added at the end of a placeholder but within the escape characters. The placeholder attribute always begins with a double colon `::` and each parameter is separated with a single colon `:`. Some placeholders cannot use the attributes, and not all attributes can be used on a placeholder that will work with an attribute. See additonal information below pertain to each attribute. +The **Placeholder Attributes** is additional text that is added at the end of a placeholder but within the escape characters. The placeholder attribute always begins with a double colon `::` and each parameter is separated with a single colon `:`. Some placeholders cannot use the attributes, and not all attributes can be used on a placeholder. See additional information below that pertains to each attribute. + + +As of v3.3.0-alpha.11h one placeholder can have more than one Placeholder Attribute, but they have to be of a different type. At this time, this really is not beneficial since the Placeholder Attributes are specific to a certain type of data. But this opens the door to future possibilities that are not possible currently, such as hybrid between a bar graph with a value super-imposed on top of it. -Here are a couple of basic placeholders, as they are listed, and as they would be used for a mine named **temp5**. + +The following shows how a few placeholder need to have the "minename" replaced with the actual mine's name. For these examples, we will use a mine named **temp5**. * **prison_mines_size_minename** - `{prison_mines_size_temp5}` * **prison_mines_remaining_minename** - `{prison_mines_remaining_temp5}` @@ -171,7 +257,8 @@ Here are a couple of basic placeholders, as they are listed, and as they would b * **prison_mines_timeleft_bar_minename** - `{prison_mines_timeleft_bar_temp5}` -And these are some examples of using the attributes: + +A few Examples using Placeholder Attributes: * `{prison_mines_size_temp5::nFormat:#,##0}` - **654,321** * `{prison_mines_size_temp5::nFormat:#,##0:0:none}` - **654,321** @@ -211,7 +298,7 @@ Using the command `/prison placeholders test` should be used to test and perfect -## Placeholder Attribute - Numeric Formats +## Placeholder Attribute: nFormat - Numeric Formats The Numeric Format attribute will only work on placeholders that return plain numbers. If there is a "_format" version of the placeholder, then an attribute will override the default formatting. @@ -290,7 +377,7 @@ Although it is not suggested to include color codes in the formatting of numbers -## Placeholder Attribute - Bar Graphs +## Placeholder Attribute: bar - Bar Graphs The bar placeholder attribute only works with placeholders with the word bar in them. @@ -362,7 +449,7 @@ Examples of using hex color codes in a bar graph placeholder. Try it first with -## Placeholder Attribute - Text +## Placeholder Attribute: text - Text This placeholder attribute is for text formatting the placeholder results. The only thing it does is to process the hex, hex2, and debug options. diff --git a/gradle.properties b/gradle.properties index f5658bab9..0297303c1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ ## # 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.3.0-alpha.12 +version=3.3.0-alpha.13 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 fbda21369..7c36a7f80 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/Prison.java +++ b/prison-core/src/main/java/tech/mcprison/prison/Prison.java @@ -44,6 +44,7 @@ import tech.mcprison.prison.store.Database; import tech.mcprison.prison.troubleshoot.TroubleshootManager; import tech.mcprison.prison.util.EventExceptionHandler; +import tech.mcprison.prison.util.PrisonStatsUtil; import tech.mcprison.prison.util.PrisonTPS; /** @@ -102,7 +103,7 @@ public class Prison private Database metaDatabase; - + private PrisonStatsUtil statsUtil; private PrisonTPS prisonTPS; @@ -174,13 +175,15 @@ public void setupJUnitInstance( Platform platform ) { *

* Note that modules should not call this method. This is solely for the implementations. */ - public boolean init(Platform platform, String minecraftVersion) { + public boolean init(Platform platform, String minecraftVersion, File dataFolder ) { long startTime = System.currentTimeMillis(); this.platform = platform; this.minecraftVersion = minecraftVersion; + this.dataFolder = dataFolder; + if (!initDataFolder()) { Output.get().logInfo("&cFailure: &eInitializing the Prison Data Folders!" ); Output.get().logInfo("&e&k!=&d Prison Plugin Terminated! &e&k=!&7" ); @@ -188,6 +191,9 @@ public boolean init(Platform platform, String minecraftVersion) { } + this.statsUtil = new PrisonStatsUtil(); + + this.prisonTPS = new PrisonTPS(); this.prisonTPS.submitAsyncTPSTask(); @@ -362,7 +368,13 @@ public void displaySystemSettings( LinkedHashMap fields ) { } - public void displaySystemTPS( ChatDisplay display ) { + + + public PrisonStatsUtil getPrisonStatsUtil() { + return statsUtil; + } + + public void displaySystemTPS( ChatDisplay display ) { DecimalFormat iFmt = new DecimalFormat("#,##0"); PrisonTPS prisonTPS = Prison.get().getPrisonTPS(); @@ -530,7 +542,8 @@ public void setStorageSize( long storageSize ) { private boolean initDataFolder() { // Creates the /Prison directory, for core configuration. - this.dataFolder = getPlatform().getPluginDirectory(); + +// this.dataFolder = getPlatform().getPluginDirectory(); return this.dataFolder.exists() || this.dataFolder.mkdirs(); } 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 bae6c2bd7..6e9504e81 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java +++ b/prison-core/src/main/java/tech/mcprison/prison/PrisonCommand.java @@ -23,10 +23,7 @@ import java.io.IOException; import java.nio.file.Files; import java.text.DecimalFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Optional; import java.util.Set; @@ -35,6 +32,8 @@ import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; import tech.mcprison.prison.autofeatures.AutoFeaturesWrapper; +import tech.mcprison.prison.backups.PrisonBackups; +import tech.mcprison.prison.backups.PrisonBackups.BackupTypes; import tech.mcprison.prison.cache.PlayerCachePlayerData; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; @@ -53,7 +52,6 @@ import tech.mcprison.prison.placeholders.PlaceholdersStats; import tech.mcprison.prison.troubleshoot.TroubleshootResult; import tech.mcprison.prison.troubleshoot.Troubleshooter; -import tech.mcprison.prison.util.JumboTextFont; import tech.mcprison.prison.util.PrisonJarReporter; /** @@ -99,7 +97,7 @@ else if ( options == null ) { options = "basic"; } - ChatDisplay display = displayVersion(options); + ChatDisplay display = Prison.get().getPrisonStatsUtil().displayVersion(options); display.send(sender); @@ -251,195 +249,195 @@ public void setAliases( List aliases ) { } - - public ChatDisplay displayVersion(String options) { - - boolean isBasic = options == null || "basic".equalsIgnoreCase( options ); - - ChatDisplay display = new ChatDisplay("/prison version"); - display.addText("&7Prison Version: %s", Prison.get().getPlatform().getPluginVersion()); - - display.addText("&7Running on Platform: %s", Prison.get().getPlatform().getClass().getName()); - display.addText("&7Minecraft Version: %s", Prison.get().getMinecraftVersion()); - - - // System stats: - display.addText(""); - - Prison.get().displaySystemSettings( display ); - - Prison.get().displaySystemTPS( display ); - - - display.addText(""); - - - // This generates the module listing, the autoFeatures overview, - // the integrations listings, and the plugins listings. - boolean showLaddersAndRanks = true; - Prison.get().getPlatform().prisonVersionFeatures( display, isBasic, showLaddersAndRanks ); - - - -// List features = Prison.get().getPlatform().getActiveFeatures(); -// if ( features.size() > 0 ) { -// -// display.addText(""); -// for ( String feature : features ) { -// -// if ( !feature.startsWith( "+" ) ) { -// -// display.addText( feature ); -// } -// else if ( !isBasic ) { -// -// display.addText( feature.substring( 1 ) ); -// } -// } -// } -// +// +// public ChatDisplay displayVersion(String options) { +// +// boolean isBasic = options == null || "basic".equalsIgnoreCase( options ); +// +// ChatDisplay display = new ChatDisplay("/prison version"); +// display.addText("&7Prison Version: %s", Prison.get().getPlatform().getPluginVersion()); +// +// display.addText("&7Running on Platform: %s", Prison.get().getPlatform().getClass().getName()); +// display.addText("&7Minecraft Version: %s", Prison.get().getMinecraftVersion()); +// // +// // System stats: // display.addText(""); // -// // Active Modules: -// display.addText("&7Prison's root Command: &3/prison"); -// -// for ( Module module : Prison.get().getModuleManager().getModules() ) { -// -// display.addText( "&7Module: %s : %s %s", module.getName(), -// module.getStatus().getStatusText(), -// (module.getStatus().getStatus() == ModuleStatus.Status.FAILED ? -// "[" + module.getStatus().getMessage() + "]" : "") -// ); -// // display.addText( ". &7Base Commands: %s", module.getBaseCommands() ); -// } +// Prison.get().displaySystemSettings( display ); // -// List disabledModules = Prison.get().getModuleManager().getDisabledModules(); -// if ( disabledModules.size() > 0 ) { -// display.addText( "&7Disabled Module%s:", (disabledModules.size() > 1 ? "s" : "")); -// for ( String disabledModule : Prison.get().getModuleManager().getDisabledModules() ) { -// display.addText( ". &cDisabled Module:&7 %s. Related commands and placeholders are non-functional. ", -// disabledModule ); -// } -// } +// Prison.get().displaySystemTPS( display ); +// // // display.addText(""); -// display.addText("&7Integrations:"); -// -// IntegrationManager im = Prison.get().getIntegrationManager(); -// String permissions = -// (im.hasForType(IntegrationType.PERMISSION) ? -// " " + im.getForType(IntegrationType.PERMISSION).get().getDisplayName() : -// "None"); // -// display.addText(". . &7Permissions: " + permissions); // -// String economy = -// (im.hasForType(IntegrationType.ECONOMY) ? -// " " + im.getForType(IntegrationType.ECONOMY).get().getDisplayName() : -// "None"); +// // This generates the module listing, the autoFeatures overview, +// // the integrations listings, and the plugins listings. +// boolean showLaddersAndRanks = true; +// Prison.get().getPlatform().prisonVersionFeatures( display, isBasic, showLaddersAndRanks ); // -// display.addText(". . &7Economy: " + economy); -// -// -// List integrationRows = im.getIntegrationComponents( isBasic ); -// for ( DisplayComponent component : integrationRows ) -// { -// display.addComponent( component ); -// } -// -// -// display.addText(""); -// display.addText("&7Locale Settings:"); -// -// for ( String localeInfo : Prison.get().getLocaleLoadInfo() ) { -// display.addText( ". . " + localeInfo ); -// } - // -// 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.addText(""); -// display.addText( "&7Registered Plugins: " ); -// -// List plugins = getRegisteredPlugins(); -// Collections.sort( plugins ); -// List plugins2Cols = Text.formatColumnsFromList( plugins, 2 ); -// -// for ( String rp : plugins2Cols ) { -// -// display.addText( rp ); -// } -// -//// StringBuilder sb = new StringBuilder(); -//// for ( String plugin : getRegisteredPlugins() ) { -//// if ( sb.length() == 0) { -//// sb.append( ". " ); -//// sb.append( plugin ); -//// } else { -//// sb.append( ", " ); -//// sb.append( plugin ); -//// display.addText( sb.toString() ); -//// sb.setLength( 0 ); -//// } -//// } -//// if ( sb.length() > 0 ) { -//// display.addText( 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(); +//// List features = Prison.get().getPlatform().getActiveFeatures(); +//// if ( features.size() > 0 ) { //// -//// for ( String key : keys ) { -//// RegisteredPluginsData plugin = getRegisteredPluginData().get(key); +//// display.addText(""); +//// for ( String feature : features ) { //// -//// 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 ( !feature.startsWith( "+" ) ) { +//// +//// display.addText( feature ); +//// } +//// else if ( !isBasic ) { +//// +//// display.addText( feature.substring( 1 ) ); //// } //// } -//// if ( sb.length() > 0 ) { -//// display.text( sb.toString()); +//// } +//// +//// +//// display.addText(""); +//// +//// // Active Modules: +//// display.addText("&7Prison's root Command: &3/prison"); +//// +//// for ( Module module : Prison.get().getModuleManager().getModules() ) { +//// +//// display.addText( "&7Module: %s : %s %s", module.getName(), +//// module.getStatus().getStatusText(), +//// (module.getStatus().getStatus() == ModuleStatus.Status.FAILED ? +//// "[" + module.getStatus().getMessage() + "]" : "") +//// ); +//// // display.addText( ". &7Base Commands: %s", module.getBaseCommands() ); +//// } +//// +//// List disabledModules = Prison.get().getModuleManager().getDisabledModules(); +//// if ( disabledModules.size() > 0 ) { +//// display.addText( "&7Disabled Module%s:", (disabledModules.size() > 1 ? "s" : "")); +//// for ( String disabledModule : Prison.get().getModuleManager().getDisabledModules() ) { +//// display.addText( ". &cDisabled Module:&7 %s. Related commands and placeholders are non-functional. ", +//// disabledModule ); //// } //// } -// -// -//// RegisteredPluginsData plugin = getRegisteredPluginData().get( "Prison" ); -//// String pluginDetails = plugin.getdetails(); //// -//// display.text( pluginDetails ); +//// display.addText(""); +//// display.addText("&7Integrations:"); +//// +//// IntegrationManager im = Prison.get().getIntegrationManager(); +//// String permissions = +//// (im.hasForType(IntegrationType.PERMISSION) ? +//// " " + im.getForType(IntegrationType.PERMISSION).get().getDisplayName() : +//// "None"); +//// +//// display.addText(". . &7Permissions: " + permissions); +//// +//// String economy = +//// (im.hasForType(IntegrationType.ECONOMY) ? +//// " " + im.getForType(IntegrationType.ECONOMY).get().getDisplayName() : +//// "None"); +//// +//// display.addText(". . &7Economy: " + economy); +//// +//// +//// List integrationRows = im.getIntegrationComponents( isBasic ); +//// for ( DisplayComponent component : integrationRows ) +//// { +//// display.addComponent( component ); +//// } +//// +//// +//// display.addText(""); +//// display.addText("&7Locale Settings:"); +//// +//// for ( String localeInfo : Prison.get().getLocaleLoadInfo() ) { +//// display.addText( ". . " + localeInfo ); +//// } // -// -//// if ( !isBasic ) { -//// Prison.get().getPlatform().dumpEventListenersBlockBreakEvents(); +//// +//// 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.addText(""); +//// display.addText( "&7Registered Plugins: " ); +//// +//// List plugins = getRegisteredPlugins(); +//// Collections.sort( plugins ); +//// List plugins2Cols = Text.formatColumnsFromList( plugins, 2 ); +//// +//// for ( String rp : plugins2Cols ) { +//// +//// display.addText( rp ); +//// } +//// +////// StringBuilder sb = new StringBuilder(); +////// for ( String plugin : getRegisteredPlugins() ) { +////// if ( sb.length() == 0) { +////// sb.append( ". " ); +////// sb.append( plugin ); +////// } else { +////// sb.append( ", " ); +////// sb.append( plugin ); +////// display.addText( sb.toString() ); +////// sb.setLength( 0 ); +////// } +////// } +////// if ( sb.length() > 0 ) { +////// display.addText( 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 ); +//// +//// +////// if ( !isBasic ) { +////// Prison.get().getPlatform().dumpEventListenersBlockBreakEvents(); +////// } +//// +//// +//// Prison.get().getPlatform().getWorldLoadErrors( display ); +//// +//// if ( !isBasic && getPrisonStartupDetails().size() > 0 ) { +//// display.addText(""); +//// +//// for ( String msg : getPrisonStartupDetails() ) { +//// display.addText( msg ); +//// } //// } // -// -// Prison.get().getPlatform().getWorldLoadErrors( display ); -// -// if ( !isBasic && getPrisonStartupDetails().size() > 0 ) { -// display.addText(""); -// -// for ( String msg : getPrisonStartupDetails() ) { -// display.addText( msg ); -// } -// } - - return display; - } +// return display; +// } /** @@ -1234,8 +1232,7 @@ public void supportSubmitVersion(CommandSender sender } - ChatDisplay display = displayVersion("ALL"); - StringBuilder text = display.toStringBuilder(); + StringBuilder text = Prison.get().getPrisonStatsUtil().getSupportSubmitVersionData(); PrisonPasteChat pasteChat = new PrisonPasteChat( getSupportName(), getSupportURLs() ); @@ -1255,6 +1252,8 @@ public void supportSubmitVersion(CommandSender sender } + + @Command(identifier = "prison support submit configs", description = "For Prison support: This will copy the contents of Prison's config " + @@ -1274,23 +1273,7 @@ public void supportSubmitConfigs(CommandSender sender return; } - Prison.get().getPlatform().saveResource( "plugin.yml", true ); - - String fileNames = "config.yml plugin.yml autoFeaturesConfig.yml modules.yml module_conf/mines/config.json " + - "SellAllConfig.yml GuiConfig.yml backpacks/backpacksconfig.yml"; - List files = convertNamesToFiles( fileNames ); - - - StringBuilder text = new StringBuilder(); - - for ( File file : files ) { - - addFileToText( file, text ); - - if ( file.getName().equalsIgnoreCase( "plugin.yml" ) ) { - file.delete(); - } - } + StringBuilder text = Prison.get().getPrisonStatsUtil().getSupportSubmitConfigsData(); PrisonPasteChat pasteChat = new PrisonPasteChat( getSupportName(), getSupportURLs() ); @@ -1311,7 +1294,8 @@ public void supportSubmitConfigs(CommandSender sender } - + + @Command(identifier = "prison support submit ranks", description = "For Prison support: This will copy the contents of Prison's " + "ladders and ranks configs to paste.helpch.at so it can be " + @@ -1329,22 +1313,7 @@ public void supportSubmitRanks(CommandSender sender } - List files = listFiles( "data_storage/ranksDb/ladders/", ".json" ); - files.addAll( listFiles( "data_storage/ranksDb/ranks/", ".json" ) ); - - - StringBuilder text = new StringBuilder(); - - - text.append( Prison.get().getPlatform().getRanksListString() ); - printFooter( text ); - - - for ( File file : files ) { - - addFileToText( file, text ); - - } + StringBuilder text = Prison.get().getPrisonStatsUtil().getSupportSubmitRanksData(); PrisonPasteChat pasteChat = new PrisonPasteChat( getSupportName(), getSupportURLs() ); @@ -1365,6 +1334,7 @@ public void supportSubmitRanks(CommandSender sender } + @Command(identifier = "prison support submit mines", @@ -1383,30 +1353,8 @@ public void supportSubmitMines(CommandSender sender return; } - - List files = listFiles( "data_storage/mines/mines/", ".json" ); - Collections.sort( files ); - - StringBuilder text = new StringBuilder(); - - text.append( "\n" ); - text.append( "Table of contents:\n" ); - text.append( " 1. Mine list - All mines including virtual mines: /mines list all\n" ); - text.append( " 2. Mine info - All mines: /mines info all\n" ); - text.append( " 3. Mine files - Raw JSON dump of all mine configuration files.\n" ); - text.append( "\n" ); - - // Display a list of all mines, then display the /mines info all for each: - text.append( Prison.get().getPlatform().getMinesListString() ); - printFooter( text ); - - - - for ( File file : files ) { - - addFileToText( file, text ); - - } + + StringBuilder text = Prison.get().getPrisonStatsUtil().getSupportSubmitMinesData(); PrisonPasteChat pasteChat = new PrisonPasteChat( getSupportName(), getSupportURLs() ); @@ -1427,83 +1375,162 @@ public void supportSubmitMines(CommandSender sender } + +// +// private StringBuilder getSupportSubmitVersionData() { +// ChatDisplay display = displayVersion("ALL"); +// StringBuilder text = display.toStringBuilder(); +// return text; +// } +// +// private StringBuilder getSupportSubmitConfigsData() { +// Prison.get().getPlatform().saveResource( "plugin.yml", true ); +// +// String fileNames = "config.yml plugin.yml backups/versions.txt " + +// "autoFeaturesConfig.yml blockConvertersConfig.json " + +// "modules.yml module_conf/mines/config.json module_conf/mines/mineBombsConfig.json " + +// "SellAllConfig.yml GuiConfig.yml backpacks/backpacksconfig.yml"; +// List files = convertNamesToFiles( fileNames ); +// +// +// StringBuilder text = new StringBuilder(); +// +// for ( File file : files ) { +// +// addFileToText( file, text ); +// +// if ( file.getName().equalsIgnoreCase( "plugin.yml" ) ) { +// file.delete(); +// } +// } +// return text; +// } +// +// +// private StringBuilder getSupportSubmitRanksData() { +// List files = listFiles( "data_storage/ranksDb/ladders/", ".json" ); +// files.addAll( listFiles( "data_storage/ranksDb/ranks/", ".json" ) ); +// +// +// StringBuilder text = new StringBuilder(); +// +// +// text.append( Prison.get().getPlatform().getRanksListString() ); +// printFooter( text ); +// +// +// for ( File file : files ) { +// +// addFileToText( file, text ); +// +// } +// return text; +// } +// +// +// private StringBuilder getSupportSubmitMinesData() { +// List files = listFiles( "data_storage/mines/mines/", ".json" ); +// Collections.sort( files ); +// +// StringBuilder text = new StringBuilder(); +// +// text.append( "\n" ); +// text.append( "Table of contents:\n" ); +// text.append( " 1. Mine list - All mines including virtual mines: /mines list all\n" ); +// text.append( " 2. Mine info - All mines: /mines info all\n" ); +// text.append( " 3. Mine files - Raw JSON dump of all mine configuration files.\n" ); +// text.append( "\n" ); +// +// // Display a list of all mines, then display the /mines info all for each: +// text.append( Prison.get().getPlatform().getMinesListString() ); +// printFooter( text ); +// +// +// +// for ( File file : files ) { +// +// addFileToText( file, text ); +// +// } +// return text; +// } - private List listFiles( String path, String fileSuffix ) { - List files = new ArrayList<>(); - - File dataFolder = Prison.get().getDataFolder(); - File filePaths = new File( dataFolder, path ); - - for ( File file : filePaths.listFiles() ) { - if ( file.getName().toLowerCase().endsWith( fileSuffix.toLowerCase() )) { - files.add( file ); - } - } - - return files; - } - - private void addFileToText( File file, StringBuilder sb ) - { - DecimalFormat dFmt = new DecimalFormat("#,##0"); - SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); - - sb.append( "\n" ); - - JumboTextFont.makeJumboFontText( file.getName(), sb ); - - sb.append( "\n" ); - - sb.append( "File Name: " ).append( file.getName() ).append( "\n" ); - sb.append( "File Path: " ).append( file.getAbsolutePath() ).append( "\n" ); - sb.append( "File Size: " ).append( dFmt.format( file.length() ) ).append( " bytes\n" ); - sb.append( "File Date: " ).append( sdFmt.format( new Date(file.lastModified()) ) ).append( " bytes\n" ); - sb.append( "File Stats: " ) - .append( file.exists() ? "EXISTS " : "" ) - .append( file.canRead() ? "READABLE " : "" ) - .append( file.canWrite() ? "WRITEABLE " : "" ) - .append( "\n" ); - - sb.append( "\n" ); - sb.append( "=== --- --- --- --- --- --- --- --- --- ===\n" ); - sb.append( "\n" ); - - - if ( file.exists() && file.canRead() ) { - readFileToStringBulider( file, sb ); - } - else { - sb.append( "Warning: The file is not readable so it cannot be included.\n" ); - } - - - printFooter( sb ); - } +// private List listFiles( String path, String fileSuffix ) { +// List files = new ArrayList<>(); +// +// File dataFolder = Prison.get().getDataFolder(); +// File filePaths = new File( dataFolder, path ); +// +// for ( File file : filePaths.listFiles() ) { +// if ( file.getName().toLowerCase().endsWith( fileSuffix.toLowerCase() )) { +// files.add( file ); +// } +// } +// +// return files; +// } +// +// private void addFileToText( File file, StringBuilder sb ) +// { +// DecimalFormat dFmt = new DecimalFormat("#,##0"); +// SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ); +// +// sb.append( "\n" ); +// +// JumboTextFont.makeJumboFontText( file.getName(), sb ); +// +// sb.append( "\n" ); +// +// sb.append( "File Name: " ).append( file.getName() ).append( "\n" ); +// sb.append( "File Path: " ).append( file.getAbsolutePath() ).append( "\n" ); +// sb.append( "File Size: " ).append( dFmt.format( file.length() ) ).append( " bytes\n" ); +// sb.append( "File Date: " ).append( sdFmt.format( new Date(file.lastModified()) ) ).append( " bytes\n" ); +// sb.append( "File Stats: " ) +// .append( file.exists() ? "EXISTS " : "" ) +// .append( file.canRead() ? "READABLE " : "" ) +// .append( file.canWrite() ? "WRITEABLE " : "" ) +// .append( "\n" ); +// +// sb.append( "\n" ); +// sb.append( "=== --- --- --- --- --- --- --- --- --- ===\n" ); +// sb.append( "\n" ); +// +// +// if ( file.exists() && file.canRead() ) { +// readFileToStringBulider( file, sb ); +// } +// else { +// sb.append( "Warning: The file is not readable so it cannot be included.\n" ); +// } +// +// +// printFooter( sb ); +// } - public static void printFooter( StringBuilder sb ) { - - sb.append( "\n\n\n" ); - sb.append( "=== --- === --- === --- === --- === --- ===\n" ); - sb.append( "=== # # ### # # # ### # # # ### # # # ### # # # ### # # ===\n" ); - sb.append( "=== --- === --- === --- === --- === --- ===\n" ); - sb.append( "\n\n" ); - - } - - private List convertNamesToFiles( String fileNames ) - { - List files = new ArrayList<>(); - - File dataFolder = Prison.get().getDataFolder(); - - for ( String fileName : fileNames.split( " " )) { - File file = new File( dataFolder, fileName ); - files.add( file ); - } - - return files; - } +// public static void printFooter( StringBuilder sb ) { +// +// sb.append( "\n\n\n" ); +// sb.append( "=== --- === --- === --- === --- === --- ===\n" ); +// sb.append( "=== # # ### # # # ### # # # ### # # # ### # # # ### # # ===\n" ); +// sb.append( "=== --- === --- === --- === --- === --- ===\n" ); +// sb.append( "\n\n" ); +// +// } +// +// private List convertNamesToFiles( String fileNames ) +// { +// List files = new ArrayList<>(); +// +// File dataFolder = Prison.get().getDataFolder(); +// +// for ( String fileName : fileNames.split( " " )) { +// File file = new File( dataFolder, fileName ); +// files.add( file ); +// } +// +// return files; +// } @Command(identifier = "prison support submit latestLog", description = "For Prison support: This will copy the contents of `logs/latest.log` " + @@ -1589,6 +1616,47 @@ private void readFileToStringBulider( File textFile, StringBuilder text ) } } + + @Command(identifier = "prison support submit listeners", + description = "For Prison support: This will copy the server's active listeners " + + "for blockBreak, chat, and playerInteracts to paste.helpch.at so it can be " + + "easily shared with Prison's support staff.", + onlyPlayers = false, permissions = "prison.debug" ) + public void supportSubmitListeners(CommandSender sender + ) { + + + if ( getSupportName() == null || getSupportName().trim().isEmpty() ) { + sender.sendMessage( "The support name needs to be set prior to using this command." ); + sender.sendMessage( "Use &7/prison support setSupportName help" ); + + return; + } + + + StringBuilder text = Prison.get().getPrisonStatsUtil().getSupportSubmitListenersData( "all" ); + + PrisonPasteChat pasteChat = new PrisonPasteChat( getSupportName(), getSupportURLs() ); + + String helpURL = pasteChat.post( text.toString() ); + + getSupportURLs().put( "Submit Listeners:", helpURL ); + + if ( helpURL != null ) { + + sender.sendMessage( "Prison's support information has been pasted. Copy and " + + "paste this URL in to Prison's Discord server." ); + sender.sendMessage( String.format( "Paste this URL: %s", helpURL )); + } + else { + sender.sendMessage( "There was an error trying to generate the paste.helpch.at URL." ); + } + + + } + + + @Command(identifier = "prison support listeners", description = "For Prison support: Provide a 'dump' of all event listeners.", @@ -1598,7 +1666,7 @@ public void supportListenersDump(CommandSender sender, description = "Provides a detailed list of all registered event listeners for" + "the various event types. BlockBreak listeners will include all " + "listeners that are being monitored within auto features. " + - "[blockBreak, traceBlockBreak, chat, playerInteract]" + "[all, blockBreak, chat, playerInteract]" ) String listener ) { @@ -1608,29 +1676,31 @@ public void supportListenersDump(CommandSender sender, return; } - String results = null; + String results = Prison.get().getPrisonStatsUtil().getSupportSubmitListenersData( listener ).toString(); - if ( "blockBreak".equalsIgnoreCase( listener ) ) { - - results = Prison.get().getPlatform().dumpEventListenersBlockBreakEvents(); - } - - if ( "chat".equalsIgnoreCase( listener ) ) { - - results = Prison.get().getPlatform().dumpEventListenersPlayerChatEvents(); - } - - if ( "traceBlockBreak".equalsIgnoreCase( listener ) ) { - - Prison.get().getPlatform().traceEventListenersBlockBreakEvents( sender ); - - return; - } - - if ( "playerInteract".equalsIgnoreCase( listener ) ) { - - results = Prison.get().getPlatform().dumpEventListenersPlayerInteractEvents(); - } +// String results = null; +// +// if ( "blockBreak".equalsIgnoreCase( listener ) ) { +// +// results = Prison.get().getPlatform().dumpEventListenersBlockBreakEvents(); +// } +// +// if ( "chat".equalsIgnoreCase( listener ) ) { +// +// results = Prison.get().getPlatform().dumpEventListenersPlayerChatEvents(); +// } +// +// if ( "traceBlockBreak".equalsIgnoreCase( listener ) ) { +// +// Prison.get().getPlatform().traceEventListenersBlockBreakEvents( sender ); +// +// return; +// } +// +// if ( "playerInteract".equalsIgnoreCase( listener ) ) { +// +// results = Prison.get().getPlatform().dumpEventListenersPlayerInteractEvents(); +// } if ( results != null ) { @@ -1643,6 +1713,28 @@ public void supportListenersDump(CommandSender sender, } + @Command(identifier = "prison support backup", + description = "This will make a backup of all Prison settings by creating a new " + + "zip file which will be stored in the directory plugins/Prison/backups. " + + "After creating the backup, this will delete all temp files, backup files, etc.. " + + "since they will be included in the backup.", + onlyPlayers = false, permissions = "prison.debug" ) + public void supportBackupPrison( CommandSender sender, + + @Wildcard(join=true) + @Arg(name = "notes", description = "Optional short note to append to the file name. Only the " + + "first 20 characters will be used.", + def = "") String notes ) { + + PrisonBackups prisonBackup = new PrisonBackups(); + + String message = prisonBackup.startBackup( BackupTypes.manual, notes ); + + sender.sendMessage( message ); + sender.sendMessage( "Backup finished." ); + + } + @Command(identifier = "prison tokens balance", description = "Prison tokens: a player's current balance.", // aliases = "tokens bal", 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 index 881295c55..fecbffd13 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/AutoFeaturesFileConfig.java +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/AutoFeaturesFileConfig.java @@ -6,6 +6,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import tech.mcprison.prison.Prison; import tech.mcprison.prison.autofeatures.ValueNode.NodeType; @@ -981,4 +982,80 @@ public void setConfig( Map config ) { this.config = config; } + + + public TreeMap getBstatsDetails() { + TreeMap tm = new TreeMap<>(); + + bStatsDetailBoolean( AutoFeatures.isAutoFeaturesEnabled, tm ); + + if ( isFeatureBoolean( AutoFeatures.isAutoFeaturesEnabled )) { + + bStatsDetailBoolean( AutoFeatures.cancelAllBlockBreakEvents, tm ); + + bStatsDetailBoolean( AutoFeatures.cancelAllBlockEventBlockDrops, tm ); + bStatsDetailBoolean( AutoFeatures.applyBlockBreaksThroughSyncTask, tm ); + + + bStatsDetailPriority( AutoFeatures.blockBreakEventPriority, tm ); + bStatsDetailPriority( AutoFeatures.ProcessPrisons_ExplosiveBlockBreakEventsPriority, tm ); + bStatsDetailPriority( AutoFeatures.TokenEnchantBlockExplodeEventPriority, tm ); + bStatsDetailPriority( AutoFeatures.CrazyEnchantsBlastUseEventPriority, tm ); + bStatsDetailPriority( AutoFeatures.ZenchantmentsBlockShredEventPriority, tm ); + bStatsDetailPriority( AutoFeatures.PrisonEnchantsExplosiveEventPriority, tm ); + + + bStatsDetailBoolean( AutoFeatures.isCalculateFoodExhustion, tm ); + bStatsDetailBoolean( AutoFeatures.isCalculateXPEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.givePlayerXPAsOrbDrops, tm ); + + bStatsDetailBoolean( AutoFeatures.isAutoSellPerBlockBreakEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.isAutoSellIfInventoryIsFull, tm ); + bStatsDetailBoolean( AutoFeatures.dropItemsIfInventoryIsFull, tm ); + + + bStatsDetailBoolean( AutoFeatures.isAutoFeaturesEnabled, tm ); + if ( isFeatureBoolean( AutoFeatures.isAutoFeaturesEnabled ) ) { + + bStatsDetailBoolean( AutoFeatures.autoPickupEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.autoSmeltEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.autoBlockEnabled, tm ); + } + + bStatsDetailBoolean( AutoFeatures.handleNormalDropsEvents, tm ); + if ( isFeatureBoolean( AutoFeatures.handleNormalDropsEvents ) ) { + + bStatsDetailBoolean( AutoFeatures.normalDropSmelt, tm ); + bStatsDetailBoolean( AutoFeatures.normalDropBlock, tm ); + } + + + + bStatsDetailBoolean( AutoFeatures.tokensEnabled, tm ); + + bStatsDetailBoolean( AutoFeatures.isCalculateDurabilityEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.isPreventToolBreakage, tm ); + bStatsDetailBoolean( AutoFeatures.isCalculateFortuneEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.isExtendBukkitFortuneCalculationsEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.isCalculateAltFortuneEnabled, tm ); + bStatsDetailBoolean( AutoFeatures.isCalculateAltFortuneOnAllBlocksEnabled, tm ); + + } + + return tm; + } + + private void bStatsDetailBoolean( AutoFeatures af, TreeMap tm ) { + tm.put( af.name(), Boolean.toString( isFeatureBoolean( af )) ); + } + + private void bStatsDetailPriority( AutoFeatures af, TreeMap tm ) { + + String priority = getFeatureMessage( af ); + if ( !"DISABLED".equalsIgnoreCase( priority ) ) { + + tm.put( af.name(), priority ); + } + } + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConverterResults.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConverterResults.java index ada1a6ecc..7794f16d6 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConverterResults.java +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConverterResults.java @@ -10,6 +10,8 @@ public class BlockConverterResults { private String sourceBlockName; private int sourceBlockQuantity; + private BlockConverter blockConverter; + // Results: private boolean resultsSuccess; @@ -22,6 +24,8 @@ public BlockConverterResults( String blockName, int blockQuanty ) { this.sourceBlockName = blockName; this.sourceBlockQuantity = blockQuanty; + this.blockConverter = null; + this.resultsSuccess = false; this.resultsItemStack = new ArrayList<>(); this.resultsQuantityConsumed = -1; @@ -41,6 +45,13 @@ public void setSourceBlockQuantity(int sourceBlockQuantity) { this.sourceBlockQuantity = sourceBlockQuantity; } + public BlockConverter getBlockConverter() { + return blockConverter; + } + public void setBlockConverter(BlockConverter blockConverter) { + this.blockConverter = blockConverter; + } + public boolean isResultsSuccess() { return resultsSuccess; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersFileConfig.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersFileConfig.java index 472c4a990..5bd4210a0 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersFileConfig.java +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersFileConfig.java @@ -20,6 +20,9 @@ public class BlockConvertersFileConfig private transient File configFile; private TreeMap> blockConverters; + + private TreeMap processAutoFeaturesAllBlocks = null; + public enum BlockConverterTypes { aSample01, @@ -34,6 +37,18 @@ public BlockConvertersFileConfig() { this.blockConverters = new TreeMap<>(); + this.processAutoFeaturesAllBlocks = new TreeMap<>(); + + } + + public boolean processAutoFeaturesForBlock( RankPlayer player, String blockName ) { + boolean results = isProcessAutoFeaturesAllBlocks( player ); + + if ( !results ) { + + } + + return results; } public BlockConverterResults getBlockConverterItemStacks( RankPlayer player, @@ -48,30 +63,37 @@ public BlockConverterResults getBlockConverterItemStacks( RankPlayer player, if ( bc != null && blockQuantity >= bc.getKeyQuantity() ) { - // getBlockConverterOutputs applies chance, perms, etc... - List outputs = getBlockConverterOutputs( player, bc ); - int multiplier = blockQuantity / bc.getKeyQuantity(); - results.setResultsQuantityConsumed( bc.getKeyQuantity() * multiplier ); - - for (BlockConverterOutput output : outputs) { + // Make sure there is enough of a quantity match, which must be 1 or more. + if ( multiplier >= 1 ) { + + results.setBlockConverter( bc ); + + // getBlockConverterOutputs applies chance, perms, etc... + List outputs = getBlockConverterOutputs( player, bc ); - String blkName = output.getBlockName(); - int blkQuanity = output.getQuantity(); + results.setResultsQuantityConsumed( bc.getKeyQuantity() * multiplier ); - PrisonBlock pBlock = Prison.get().getPlatform().getPrisonBlock( blkName ); - if ( pBlock != null ) { + for (BlockConverterOutput output : outputs) { - ItemStack pItemStack = pBlock.getItemStack( blkQuanity ); + String blkName = output.getBlockName(); + int blkQuanity = output.getQuantity(); - if ( pItemStack != null ) { - results.getResultsItemStack().add( pItemStack ); + PrisonBlock pBlock = Prison.get().getPlatform().getPrisonBlock( blkName ); + if ( pBlock != null ) { + + ItemStack pItemStack = pBlock.getItemStack( blkQuanity ); + + if ( pItemStack != null ) { + results.getResultsItemStack().add( pItemStack ); + } } } + + results.setResultsSuccess( true ); } - results.setResultsSuccess( true ); } } @@ -248,4 +270,34 @@ public void setBlockConverters(TreeMapThis function will take a player, and check if that player should have the ability to process all + * blocks under auto features. This is the "global" setting that bypasses checking individual block types. + *

+ * + *

If the auto features settings `options.autoFeatures.isAutoFeaturesEnabled: false` is + * disabled (set to false), then this will apply to the normal drops if + * `options.normalDrop.handleNormalDropsEvents: true' is enabled (set to true). + * If both of those are set to disabled, then no blocks will be processed. + *

+ * + * @param player + * @return + */ + public Boolean isProcessAutoFeaturesAllBlocks( RankPlayer player ) { + + if ( !processAutoFeaturesAllBlocks.containsKey( player.getName() ) ) { + BlockConverterResults allBlocksBCR = getBlockConverterItemStacks( player, "*all*", 1, BlockConverterTypes.autoPickupFeatures ); + + BlockConverter allBlocks = allBlocksBCR.getBlockConverter(); + + boolean playerAllBlocks = ( allBlocks != null && allBlocks.isEnabled() ); + + processAutoFeaturesAllBlocks.put( player.getName(), playerAllBlocks ); + } + + return processAutoFeaturesAllBlocks.get( player.getName() ); + } + + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersInitializer.java b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersInitializer.java index 3a9502cbb..148aed76d 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersInitializer.java +++ b/prison-core/src/main/java/tech/mcprison/prison/autofeatures/BlockConvertersInitializer.java @@ -53,6 +53,7 @@ private void loadDefaultBlockConverters( BlockConverterTypes bcType, TreeMap blockConverters) { BlockConverter bc1 = new BlockConverter( "coal_ore", 9 ); @@ -149,32 +151,126 @@ public void loadDefaultBlockConverterSmelters( addSmeltBlockConverterDisabled( "potato", "baked_potato", blockConverters ); - addSmeltBlockConverterDisabled( "kep", "dried_kelp", "1.13", blockConverters ); + addSmeltBlockConverterDisabled( "kelp", "dried_kelp", "1.13", blockConverters ); } + private void loadDefaultBlockConverterAutoBlocking(TreeMap blockConverters) { + + addBlockingConverter( "gold_ingot", 9, "gold_ore", blockConverters ); + + addBlockingConverter( "iron_ingot", 9, "iron_block", blockConverters ); + + addSmeltBlockConverter( "iron_ore", "iron_ingot", blockConverters ); + addSmeltBlockConverter( "deepslate_iron_ore", "iron_ingot", "1.17", blockConverters ); + addSmeltBlockConverter( "raw_iron", "iron_ingot", "1.17", blockConverters ); + + addSmeltBlockConverter( "coal_ore", "coal", blockConverters ); + addSmeltBlockConverter( "deepslate_coal_ore", "coal", "1.17", blockConverters ); + + addSmeltBlockConverter( "diamond_ore", "diamond", blockConverters ); + addSmeltBlockConverter( "deepslate_diamond_ore", "diamond", "1.17", blockConverters ); + + addSmeltBlockConverter( "emerald_ore", "emerald", blockConverters ); + addSmeltBlockConverter( "deepslate_emerald_ore", "emerald", "1.17", blockConverters ); + + addSmeltBlockConverter( "lapis_ore", "lapis_lazuli", blockConverters ); + addSmeltBlockConverter( "deepslate_lapis_ore", "lapis_lazuli", "1.17", blockConverters ); + + // NOTE: redstone dust is called redstone within XMaterials: + addSmeltBlockConverter( "redstone_ore", "redstone", blockConverters ); + addSmeltBlockConverter( "deepslate_redstone_ore", "redstone", "1.17", blockConverters ); + + addSmeltBlockConverter( "nether_quartz_ore", "quartz", blockConverters ); + + addSmeltBlockConverterDisabled( "ancient_debris", "netherite_scrap", "1.16", blockConverters ); + + addSmeltBlockConverter( "copper_ore", "copper_ingot", "1.17", blockConverters ); + addSmeltBlockConverter( "deepslate_copper_ore", "copper_ingot", "1.17", blockConverters ); + addSmeltBlockConverter( "raw_copper", "copper_ingot", "1.17", blockConverters ); + + + + } + + + private void addBlockingConverter(String keyBlockName, int keyBlockQuantity, String outputName, + TreeMap blockConverters) { + + addBlockingConverter( keyBlockName, keyBlockQuantity, outputName, null, true, blockConverters ); + } + + private static void addBlockingConverter(String keyBlockName, int keyBlockQuantity, String outputName, + String semanticVersion, + boolean enabled, + TreeMap blockConverters) { + + BlockConverter bc = new BlockConverter(keyBlockName, keyBlockQuantity); + bc.getOutputs().add( new BlockConverterOutput( outputName, 1 ) ); + + // All converters are auto-enabled, so only need to disable: + if (!enabled) { + bc.setEnabled(enabled); + } + + if (semanticVersion != null && semanticVersion.trim().length() > 0) { + bc.setMininumSpigotSemanticVersion(semanticVersion); + } + + // add output name: + + blockConverters.put(bc.getKeyBlockName(), bc); + } + public void loadDefaultBlockConverterAutoPickupBlocks(TreeMap blockConverters) { + + + addSAutoPickupBlockConverter("*all_blocks*", blockConverters); + + addSAutoPickupBlockConverter("*example_of_disabled_block_valid_for_1_13_and_higher*", "1.13", false, blockConverters); + addSAutoPickupBlockConverter("cobblestone", blockConverters); addSAutoPickupBlockConverter("stone", blockConverters); addSAutoPickupBlockConverter("gold_ore", blockConverters); + addSAutoPickupBlockConverter("nether_gold_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_gold_ore", blockConverters); + addSAutoPickupBlockConverter("raw_gold", blockConverters); + addSAutoPickupBlockConverter("iron_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_iron_ore", blockConverters); + addSAutoPickupBlockConverter("raw_iron", blockConverters); + addSAutoPickupBlockConverter("coal_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_coal_ore", blockConverters); + addSAutoPickupBlockConverter("diamond_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_diamond_ore", blockConverters); addSAutoPickupBlockConverter("redstone_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_redstone_ore", blockConverters); + addSAutoPickupBlockConverter("emerald_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_emerald_ore", blockConverters); + addSAutoPickupBlockConverter("quartz_ore", blockConverters); + addSAutoPickupBlockConverter("lapis_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_lapis_ore", blockConverters); addSAutoPickupBlockConverter("snowball", blockConverters); + addSAutoPickupBlockConverter("glowstone_dust", blockConverters); + addSAutoPickupBlockConverter("copper_ore", blockConverters); + addSAutoPickupBlockConverter("deepslate_copper_ore", blockConverters); + addSAutoPickupBlockConverter("raw_copper", blockConverters); + } diff --git a/prison-core/src/main/java/tech/mcprison/prison/backups/PrisonBackups.java b/prison-core/src/main/java/tech/mcprison/prison/backups/PrisonBackups.java new file mode 100644 index 000000000..e789a2885 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/backups/PrisonBackups.java @@ -0,0 +1,548 @@ +package tech.mcprison.prison.backups; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.cache.PlayerCacheFiles; +import tech.mcprison.prison.file.ZipFileIO; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.util.PrisonStatsUtil; + +public class PrisonBackups { + + public static final String FILE_BACKUP_DIRECTORY_PATH = "backups"; + public static final String FILE_BACKUP_VERSIONS_FILE = FILE_BACKUP_DIRECTORY_PATH + "/versions.log"; + + public static final String VERSIONS_FILE_VERSION_PREFIX = "New_Prison_Version:"; + public static final String VERSIONS_FILE_BACKUP_MADE_PREFIX = "Backup:"; + + + private File backupDirectory = null; + private Date backupStartDate; + private long startTimeNanos = 0L; + + + private String zipFilePrefix; + private Path sourceDirectoryPath; + private File zipFile; + + private ArrayList filesBackups; + private ArrayList filesToBackup; + private ArrayList filesToDelete; + private ArrayList filesWithErrors; + + private DecimalFormat dFmt = new DecimalFormat("#,##0.000"); + private SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd_kk-mm" ); + private SimpleDateFormat sdsFmt = new SimpleDateFormat( "yyyy-MM-dd kk:mm:ss.SSS" ); + + + public enum BackupTypes { + upgrade, + auto, + manual + } + + public PrisonBackups() { + super(); + + this.filesBackups = new ArrayList<>(); + this.filesToBackup = new ArrayList<>(); + this.filesToDelete = new ArrayList<>(); + this.filesWithErrors = new ArrayList<>(); + + } + + + public void initialStartupVersionCheck() { + + String prisonVersion = Prison.get().getPlatform().getPluginVersion(); + String lastWrittenVersion = getLastWrittenVersion(); + + + // Run a backup if lastWrittenVersion is null or not equal to prisonVersion: + if ( lastWrittenVersion == null || !prisonVersion.equalsIgnoreCase(lastWrittenVersion) ) { + + // Make backups of the following files: + PrisonStatsUtil psUtils = new PrisonStatsUtil(); + psUtils.copyConfigsFiles(); + } + } + + private String getLastWrittenVersion() { + String lastWrittenVersion = null; + + File versionsFile = new File( Prison.get().getDataFolder(), FILE_BACKUP_VERSIONS_FILE ); + + try { + if ( versionsFile.exists() ) { + List lines = Files.readAllLines( versionsFile.toPath() ); + + for (String line : lines) { + if ( line != null && line.length() > 0 && line.startsWith( VERSIONS_FILE_VERSION_PREFIX ) ) { + String vers[] = line.split( " " ); + if ( vers.length >= 2 ) { + + lastWrittenVersion = vers[1]; + } + } + } + } + else { + + versionsFile.getParentFile().mkdirs(); + + Files.createFile( versionsFile.toPath() ); + } + + } + catch (IOException e) { + e.printStackTrace(); + } + + return lastWrittenVersion; + } + + public void serverStartupVersionCheck() { + + String prisonVersion = Prison.get().getPlatform().getPluginVersion(); + String lastWrittenVersion = getLastWrittenVersion(); + + File versionsFile = new File( Prison.get().getDataFolder(), FILE_BACKUP_VERSIONS_FILE ); + + // Run a backup if lastWrittenVersion is null or not equal to prisonVersion: + if ( lastWrittenVersion == null || !prisonVersion.equalsIgnoreCase(lastWrittenVersion) ) { + + String message = String.format( + "Prison detected a version change. Forcing a backup of all settings. " + + "Current version: %s Previous version: %s", + prisonVersion, + ( lastWrittenVersion == null ? "(not detected)" : lastWrittenVersion ) + ); + + Output.get().logInfo( message ); + + // First write the current version to the file: + writeCurrentVersionToVersionsFile( versionsFile, prisonVersion ); + + // Run the backup: + startBackup( BackupTypes.upgrade, null ); + } + + } + + private void writeCurrentVersionToVersionsFile(File versionsFile, String prisonVersion) { + + String line = String.format( + "%s %s %s :: New prison version detected. Forcing a backup.\n", + VERSIONS_FILE_VERSION_PREFIX, + prisonVersion, + sdsFmt.format( new Date() ) + ); + try { + Files.write( versionsFile.toPath(), line.getBytes(), StandardOpenOption.APPEND ); + } + catch (IOException e) { + e.printStackTrace(); + } + + } + + private void writeCurrentBackupInfoToVersionsFile( String message ) { + File versionsFile = new File( Prison.get().getDataFolder(), FILE_BACKUP_VERSIONS_FILE ); + + String line = String.format( + "%s :: %s \n", + VERSIONS_FILE_BACKUP_MADE_PREFIX, + message + ); + try { + Files.write( versionsFile.toPath(), line.getBytes(), StandardOpenOption.APPEND ); + } + catch (IOException e) { + e.printStackTrace(); + } + + } + + + public String startBackup( BackupTypes backupType, String notes ) { + + this.backupStartDate = new Date(); + this.startTimeNanos = System.nanoTime(); + + + // Reset collections: + this.filesBackups.clear(); + this.filesToBackup.clear(); + this.filesToDelete.clear(); + this.filesWithErrors.clear(); + + this.zipFile = getNewBackupFile( backupType, notes ); + + // Gather all files: + gatherFiles( Prison.get().getDataFolder() ); + + + // The files in the zip file needs to be placed in a directory: +// SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd_kk-mm" ); + String zipFilePrefix = "backup_" + sdFmt.format( backupStartDate ); + this.zipFilePrefix = zipFilePrefix; + + // Save to zip file: + ZipFileIO zipIo = new ZipFileIO(); + + zipIo.writeToZipFileBackups( + zipFile, + this ); + + + // Print out the list of errors from generating the zip file: + for ( File file : filesWithErrors ) { + + Path targetFile = Prison.get().getDataFolder().toPath().relativize( file.toPath() ); + + String message = String.format( + "PrisonBackups: Error trying to add file Prison backup file: %s %s", + zipFile.getAbsolutePath(), + targetFile.toString() + ); + Output.get().logError( message ); + + } + + // Delete the files that should be deleted: + for ( File file : filesToDelete ) { + + try { + Files.delete( file.toPath() ); + } + catch (IOException e) { + + String message = String.format( + "PrisonBackups: Error trying to delete a temp file in prison which was " + + "backed in: %s " + + "Temp file: %s [%s]", + zipFile.getAbsoluteFile(), + file.getAbsolutePath(), + e.getMessage() + ); + Output.get().logError( message ); + + } + } + + long size = zipFile.length(); + double sizeKb = size / 1024.0; + + String message = String.format( + "Backup status: %s %s KB files: %d temp files purged: %d errors: %d", + zipFile.getAbsolutePath(), + dFmt.format( sizeKb ), + filesToBackup.size(), + filesToDelete.size(), + filesWithErrors.size() + + ); + + writeCurrentBackupInfoToVersionsFile( message ); + + return message; + } + + + public String backupReport01() { + +// DecimalFormat dFmt = new DecimalFormat("#,##0.000"); + long stop = System.nanoTime(); + double runTimeMs = ( stop - getStartTimeNanos() ) / 1000000.0d; + +// long size = zipFile.length(); +// double sizeKb = size / 1024.0; + + + + String msg1 = String.format( + "Prison backup:\n" + + " Started: %s \n" + + " Compleated: %s %s ms \n" + + " %s \n" + + " files backed up: %d \n" + + " temp files: %d (deleted) \n" + + " Errors: %d \n\n", + sdsFmt.format( getBackupStartDate() ), + sdsFmt.format( new Date() ), + dFmt.format( runTimeMs ), + zipFile.getAbsolutePath(), +// dFmt.format( sizeKb ), + filesBackups.size(), + filesToDelete.size(), + filesWithErrors.size() + ); + + return msg1; + } + + public StringBuilder backupReportVersionData() { + return Prison.get().getPrisonStatsUtil().getSupportSubmitVersionData(); + } + + public StringBuilder backupReportConfigsData() { + return Prison.get().getPrisonStatsUtil().getSupportSubmitConfigsData(); + } + + public StringBuilder backupReportRanksData() { + return Prison.get().getPrisonStatsUtil().getSupportSubmitRanksData(); + } + + public StringBuilder backupReportMinesData() { + return Prison.get().getPrisonStatsUtil().getSupportSubmitMinesData(); + } + + public StringBuilder backupReportListenersData() { + return Prison.get().getPrisonStatsUtil().getSupportSubmitListenersData( "all" ); + } + + /** + * This generates a report of everything that was backed up. + * + * @return + */ + public String backupReportListTemporalFiles() { + StringBuilder sb = new StringBuilder(); + +// sb.append( "\n\n" ); +// +// sb.append( "Files Backed Up: \n- - - - - - - - - -\n" ); +// for ( File file : filesToBackup ) { +// sb.append( printFileDetails(file, true) ); +// } + + sb.append( "\n\n" ); + + sb.append( "Files Deleted: \n- - - - - - - - - -\n Warning: They are only contained in this " + + "zip file now. delete this zip file with caution.\n" ); + for ( File file : filesToDelete ) { + sb.append( printFileDetails(file, true) ); + } + + sb.append( "\n\n" ); + + sb.append( "Files with Errors - Unable to backup:\n" + + "- - - - - - - - - - - - - - - - - - -\n" ); + for ( File file : filesWithErrors ) { + sb.append( printFileDetails(file, true) ); + } + + sb.append( "\n\n" ); + + sb.append( "Existing Backup Files (not included in zip):\n" + + "- - - - - - - - - - - - - - - - - - - - - - \n" ); + for ( File file : filesBackups ) { + sb.append( printFileDetails(file, true) ); + } + + return sb.toString(); + } + + private StringBuilder printFileDetails(File file, boolean includeZipPrefix ) { + StringBuilder sb = new StringBuilder(); + + Path targetFile = getSourceDirectoryPath().relativize( file.toPath() ); + + sb.append( " " ); + + if ( includeZipPrefix ) { + sb.append( getZipFilePrefix() ); + sb.append( "/" ); + } + + sb.append( targetFile.toString() ); + + double size = file.length() / 1024.0d; + sb.append( " " ); + sb.append( dFmt.format(size) ); + sb.append( " kb " ); + + sb.append( sdFmt.format( new Date( file.lastModified() ) )); + + sb.append( "\n" ); + + return sb; + } + + + /** + * File names able to be deleted: + * *.bu + * *.tmp + * _archived_* + * + * Backups of minebombs need to be changed... was changed to have a .bu suffix. + * + * @param folder + */ + private void gatherFiles( File folder ) { + + File[] files = folder.listFiles(); + + for ( File file : files ) { + + + if ( file.isDirectory() ) { + + gatherFiles( file ); + } + + else if ( file.isFile() ) { + + String fName = file.getName(); + + boolean isDeleteable = + fName.endsWith( PlayerCacheFiles.FILE_SUFFIX_BACKUP ) || + fName.endsWith( PlayerCacheFiles.FILE_SUFFIX_TEMP ) || + fName.endsWith( ".del" ) || + fName.startsWith( "_archived_" ) || + fName.contains( ".json.ver_" ) && fName.endsWith( ".txt" ) + ; + + if ( folder.equals( getBackupDirectoryFile() ) ) { + this.filesBackups.add( file ); + } + else { + this.filesToBackup.add( file ); + + if ( isDeleteable ) { + this.filesToDelete.add( file ); + } + } + + + } + } + } + + /** + *

This function will take the project's data folder and construct the the path + * to the directory, if it does not exist, to where the backup files are stored. + *

+ * + * @return + */ + public File getBackupDirectoryFile() { + if ( backupDirectory == null ) { + + backupDirectory = new File( Prison.get().getDataFolder(), FILE_BACKUP_DIRECTORY_PATH ); + backupDirectory.mkdirs(); + } + + return backupDirectory; + } + + + public File getNewBackupFile( BackupTypes backupType, String fileNameNotes ) { + String notes = null; + + if ( fileNameNotes != null && fileNameNotes.trim().length() > 0 ) { + notes = "_" + fileNameNotes.replaceAll( "[^a-zA-Z0-9\\.\\-]", "_" ); + + // Notes can only be a max length of 20 characters + if ( notes.length() > 20 ) { + notes = notes.substring( 0, 20 ); + } + } + + if ( backupType != null && backupType == BackupTypes.manual && + notes != null && notes.length() > 0) { + backupType = null; + } + + String prisonVersion = Prison.get().getPlatform().getPluginVersion(); + +// SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd_hh-mm" ); + + String fileName = "prison_" + sdFmt.format( new Date() ) + + "_v" + prisonVersion + + ( backupType == null ? "" : "_" + backupType.name()) + + ( notes == null ? "" : notes ) + + ".zip"; + + File file = new File( getBackupDirectoryFile(), fileName ); + + return file; + } + + + public long getStartTimeNanos() { + return startTimeNanos; + } + public void setStartTimeNanos(long startTimeNanos) { + this.startTimeNanos = startTimeNanos; + } + + public Date getBackupStartDate() { + return backupStartDate; + } + public void setBackupStartDate(Date backupStartDate) { + this.backupStartDate = backupStartDate; + } + + public String getZipFilePrefix() { + return zipFilePrefix; + } + public void setZipFilePrefix(String zipFilePrefix) { + this.zipFilePrefix = zipFilePrefix; + } + + public Path getSourceDirectoryPath() { + if ( sourceDirectoryPath == null ) { + sourceDirectoryPath = Prison.get().getDataFolder().toPath(); + } + return sourceDirectoryPath; + } + + + public File getBackupDirectory() { + return backupDirectory; + } + public void setBackupDirectory(File backupDirectory) { + this.backupDirectory = backupDirectory; + } + + public ArrayList getFilesBackups() { + return filesBackups; + } + public void setFilesBackups(ArrayList filesBackups) { + this.filesBackups = filesBackups; + } + + public ArrayList getFilesToBackup() { + return filesToBackup; + } + public void setFilesToBackup(ArrayList filesToBackup) { + this.filesToBackup = filesToBackup; + } + + public ArrayList getFilesToDelete() { + return filesToDelete; + } + public void setFilesToDelete(ArrayList filesToDelete) { + this.filesToDelete = filesToDelete; + } + + public ArrayList getFilesWithErrors() { + return filesWithErrors; + } + public void setFilesWithErrors(ArrayList filesWithErrors) { + this.filesWithErrors = filesWithErrors; + } + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/bombs/MineBombs.java b/prison-core/src/main/java/tech/mcprison/prison/bombs/MineBombs.java index da62c95ff..fb872a6ff 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/bombs/MineBombs.java +++ b/prison-core/src/main/java/tech/mcprison/prison/bombs/MineBombs.java @@ -167,7 +167,7 @@ public void loadConfigJson() { int oldVersion = configs.getDataFormatVersion(); String backupTag = "ver_" + oldVersion; - File backupFile = fio.getBackupFile( configFile, backupTag, "json" ); + File backupFile = fio.getBackupFile( configFile, backupTag, ".bu" ); boolean renamed = configFile.renameTo( backupFile ); 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 130d1f65c..c7acef60b 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 @@ -51,8 +51,8 @@ public class CommandHandler { - public static final String COMMAND_PRIMARY_ROOT_COMMAND = "prison"; - public static final String COMMAND_FALLBACK_PREFIX = "prison"; +// public static final String COMMAND_PRIMARY_ROOT_COMMAND = "prison"; +// public static final String COMMAND_FALLBACK_PREFIX = "prison"; public static final String COMMAND_HELP_TEXT = "help"; @@ -95,6 +95,10 @@ public CommandHandler() { registerArgumentHandler(Player.class, new PlayerArgumentHandler()); registerArgumentHandler(World.class, new WorldArgumentHandler()); registerArgumentHandler(PrisonBlock.class, new BlockArgumentHandler()); + + + Output.get().logInfo( "&3Root command: &7/%s &3fallback-prefix: &7%s", + DefaultSettings.COMMAND_PRIMARY_ROOT_COMMAND, DefaultSettings.COMMAND_FALLBACK_PREFIX ); } @@ -269,7 +273,7 @@ public ChatDisplay getHelpMessage(RegisteredCommand command) { } } - if ( command.getLabel().equalsIgnoreCase( COMMAND_PRIMARY_ROOT_COMMAND ) && + if ( command.getLabel().equalsIgnoreCase( DefaultSettings.COMMAND_PRIMARY_ROOT_COMMAND ) && rootCommands.size() > 1 ) { ArrayList rootCommandsMessages = buildHelpRootCommands(); diff --git a/prison-core/src/main/java/tech/mcprison/prison/commands/DefaultSettings.java b/prison-core/src/main/java/tech/mcprison/prison/commands/DefaultSettings.java new file mode 100644 index 000000000..ce4e9a3d1 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/commands/DefaultSettings.java @@ -0,0 +1,22 @@ +package tech.mcprison.prison.commands; + +/** + *

These are the primary settings that define what the command's primary + * root will be, and what the command fallback prefix will be. + * The command fallback prefix is used when registering commands, it is + * added to the beginning of a command until the command is unique, which means + * it can be added multiple times. + *

+ * + *

The value for COMMAND_PRIMARY_ROOT_COMMAND should be changed for each plugin. + *

+ * + * @author Blue + * + */ +public class DefaultSettings { + + public static final String COMMAND_PRIMARY_ROOT_COMMAND = "prison"; + public static final String COMMAND_FALLBACK_PREFIX = "prison"; + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/file/JsonFileIO.java b/prison-core/src/main/java/tech/mcprison/prison/file/JsonFileIO.java index ff65e920a..a899101e5 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/file/JsonFileIO.java +++ b/prison-core/src/main/java/tech/mcprison/prison/file/JsonFileIO.java @@ -6,12 +6,19 @@ import com.google.gson.GsonBuilder; import tech.mcprison.prison.error.ErrorManager; +import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.modules.ModuleStatus; import tech.mcprison.prison.output.Output; public class JsonFileIO extends FileIO { + public static final String FILE_SUFFIX_JSON = ".json"; + public static final String FILE_PREFIX_BACKUP = ".backup_"; + public static final String FILE_SUFFIX_BACKUP = ".bu"; + public static final String FILE_SUFFIX_TEMP = ".temp"; + public static final String FILE_TIMESTAMP_FORMAT = "_yyyy-MM-dd_HH-mm-ss"; + private final Gson gson; /** @@ -35,6 +42,42 @@ public Gson getGson() return gson; } + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public static String getPlayerFileName( Player player ) { + + String UUIDString = player.getUUID().toString(); + String uuidFragment = getFileNamePrefix( UUIDString ); + + return uuidFragment + "_" + player.getName() + FILE_SUFFIX_JSON; + } + + /** + *

This function returns the first 13 characters of the supplied + * file name, or UUID String. The hyphen is around the 12 or 13th position, + * so it may or may not include it. + *

+ * + * @param playerFileName + * @return + */ + private static String getFileNamePrefix( String UUIDString ) { + return UUIDString.substring( 0, 14 ); + } + /** * This function will save a file as a JSON format. It will first save it as a * temp file to make sure the data can be written to the file system, then once diff --git a/prison-core/src/main/java/tech/mcprison/prison/file/ZipFileIO.java b/prison-core/src/main/java/tech/mcprison/prison/file/ZipFileIO.java new file mode 100644 index 000000000..5dbff29b5 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/file/ZipFileIO.java @@ -0,0 +1,111 @@ +package tech.mcprison.prison.file; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import tech.mcprison.prison.backups.PrisonBackups; +import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.util.Text; + +public class ZipFileIO { + + + public void writeToZipFileBackups( + File zipFile, + PrisonBackups pBackups ) { + + try ( + final ZipOutputStream out = new ZipOutputStream(new FileOutputStream( zipFile )); + ) { + + for (File file : pBackups.getFilesToBackup() ) { + + try { + Path targetFile = pBackups.getSourceDirectoryPath().relativize( file.toPath() ); + + String zipEntryName = pBackups.getZipFilePrefix() + "/" + targetFile.toString(); + + ZipEntry zEntry = new ZipEntry( zipEntryName ); + out.putNextEntry( zEntry ); + + + byte[] bytes = Files.readAllBytes(file.toPath()); + out.write(bytes, 0, bytes.length); + out.closeEntry(); + } + catch (IOException e) { + + pBackups.getFilesWithErrors().add( file ); + + e.printStackTrace(); + } + + } + +// // Add a stats file at the root with backup stats: + try { + String statsFileName = pBackups.getZipFilePrefix() + "_stats.txt"; + + ZipEntry zEntry = new ZipEntry( statsFileName ); + out.putNextEntry( zEntry ); + + // Basic backup stats: + writeOutput( out, pBackups.backupReport01() ); + + // Prison version: + writeOutput( out, pBackups.backupReportVersionData().toString() ); + + // List of backup files that had errors or are temporary and will be removed: + writeOutput( out, pBackups.backupReportListTemporalFiles() ); + + + writeOutput( out, pBackups.backupReportConfigsData().toString() ); + + writeOutput( out, pBackups.backupReportRanksData().toString() ); + + writeOutput( out, pBackups.backupReportMinesData().toString() ); + + writeOutput( out, pBackups.backupReportListenersData().toString() ); + + + out.closeEntry(); + } + catch (IOException e) { + + e.printStackTrace(); + } + + } + catch ( Exception e ) { + + String message = String.format( + "Error trying to build Prison backup file: %s [%s]", + zipFile.getAbsolutePath(), + e.getMessage() + ); + + Output.get().logWarn( message, e ); + } + + + } + + private void writeOutput(ZipOutputStream out, String message) { + + byte[] bytes = Text.stripColor( message ).getBytes(); + + try { + out.write(bytes, 0, bytes.length); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + +} diff --git a/prison-core/src/main/java/tech/mcprison/prison/gui/PrisonCoreGuiMessages.java b/prison-core/src/main/java/tech/mcprison/prison/gui/PrisonCoreGuiMessages.java index ad76ce684..02cd410d9 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/gui/PrisonCoreGuiMessages.java +++ b/prison-core/src/main/java/tech/mcprison/prison/gui/PrisonCoreGuiMessages.java @@ -157,7 +157,7 @@ protected String guiPriceMsg( Integer price ) { protected String guiPriceMsg( String price ) { return Prison.get().getLocaleManager() - .getLocalizable( "sellall_spigot_utils__money_earned" ) + .getLocalizable( "core_gui__price" ) .withReplacements( price ) .localize(); } @@ -215,7 +215,7 @@ protected String guiValueMsg( String value ) { protected String guiPermissionMsg( String prestigeName ) { return Prison.get().getLocaleManager() - .getLocalizable( "core_gui__puermission" ) + .getLocalizable( "core_gui__permission" ) .withReplacements( prestigeName ) .localize(); } 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 c6d19205c..96d190737 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 @@ -273,7 +273,8 @@ else if ( plugins == null || plugins.size() == 0 ) { activeIntegration = true; String pluginUrl = plugin.getPluginSourceURL(); - String msg = String.format( "&a. . %s <%s> %s", plugin.getDisplayName(), + String msg = String.format( "&a. . %s <%s> %s", + plugin.getDisplayName(), ( plugin.hasIntegrated() ? "Active" : "Inactive"), ( pluginUrl == null ? "" : "&7[URL]")); FancyMessage fancy = new FancyMessage( msg ); @@ -426,4 +427,12 @@ public void removeIntegration( Integration integration ) } + public Map> getIntegrations() { + return integrations; + } + public void setIntegrations(Map> integrations) { + this.integrations = integrations; + } + + } 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 c9029ef8f..80dd797a0 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 @@ -42,12 +42,29 @@ public interface Player /** * Returns the unique identifier for this player. */ - UUID getUUID(); + public UUID getUUID(); /** * Returns the player's display name (nickname), which may include colors. */ - String getDisplayName(); + public String getDisplayName(); + + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public String getPlayerFileName(); public String toString(); @@ -56,7 +73,7 @@ public interface Player * * @param newDisplayName The new display name. May include colors, amp-prefixed. */ - void setDisplayName(String newDisplayName); + public void setDisplayName(String newDisplayName); /** * Adds an {@link ItemStack} to the player's inventory. 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 bbb96b5a1..c82d190d4 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 @@ -40,6 +40,7 @@ import tech.mcprison.prison.modules.ModuleElementType; import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.placeholders.Placeholders; +import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.store.Storage; import tech.mcprison.prison.util.Location; @@ -436,5 +437,11 @@ public void autoCreateMineLinerAssignment( List rankMineNames, public int compareServerVerisonTo( String comparisonVersion ); + + + public void checkPlayerDefaultRank( RankPlayer rPlayer ); + + + public void listAllMines(CommandSender sender, Player player); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/placeholders/PlaceholderManager.java b/prison-core/src/main/java/tech/mcprison/prison/placeholders/PlaceholderManager.java index 5c525d890..a3ee1a437 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/placeholders/PlaceholderManager.java +++ b/prison-core/src/main/java/tech/mcprison/prison/placeholders/PlaceholderManager.java @@ -57,11 +57,14 @@ public enum PlaceholderFlags { SUPRESS, - ALIAS + ALIAS, + ONLY_DEFAULT_OR_PRESTIGES ; private final boolean sequence; - private final String desc; + + @SuppressWarnings("unused") + private final String desc; private PlaceholderFlags() { this.sequence = false; @@ -188,6 +191,8 @@ public enum PrisonPlaceHolders { prison_rr(PlaceholderFlags.PLAYER, PlaceholderFlags.ALIAS), prison_rrt(PlaceholderFlags.PLAYER, PlaceholderFlags.ALIAS), + prison_rlrt(PlaceholderFlags.PLAYER, PlaceholderFlags.ALIAS, + PlaceholderFlags.ONLY_DEFAULT_OR_PRESTIGES), @@ -210,6 +215,9 @@ public enum PrisonPlaceHolders { prison_rankup_rank_tag(prison_rrt, PlaceholderFlags.PLAYER), + prison_rankup_linked_rank_tag(prison_rlrt, PlaceholderFlags.PLAYER, + PlaceholderFlags.ONLY_DEFAULT_OR_PRESTIGES), + // Ladder aliases: prison_r_laddername(PlaceholderFlags.LADDERS, PlaceholderFlags.ALIAS), @@ -229,6 +237,11 @@ public enum PrisonPlaceHolders { prison_rr_laddername(PlaceholderFlags.LADDERS, PlaceholderFlags.ALIAS), prison_rrt_laddername(PlaceholderFlags.LADDERS, PlaceholderFlags.ALIAS), + + + prison_rlrt_laddername(PlaceholderFlags.LADDERS, PlaceholderFlags.ALIAS, + PlaceholderFlags.ONLY_DEFAULT_OR_PRESTIGES ), + prison_rank_laddername(prison_r_laddername, PlaceholderFlags.LADDERS), @@ -250,6 +263,10 @@ public enum PrisonPlaceHolders { prison_rankup_rank_tag_laddername(prison_rrt_laddername, PlaceholderFlags.LADDERS), + prison_rankup_linked_rank_tag_laddername(prison_rlrt_laddername, PlaceholderFlags.LADDERS, + PlaceholderFlags.ONLY_DEFAULT_OR_PRESTIGES), + + // player balances. Both with and without ladders. prison_pb(PlaceholderFlags.PLAYER, PlaceholderFlags.ALIAS), @@ -505,6 +522,7 @@ public enum PrisonPlaceHolders { prison_r_id_rankname(PlaceholderFlags.RANKS, PlaceholderFlags.ALIAS), prison_r_pc_rankname(PlaceholderFlags.RANKS, PlaceholderFlags.ALIAS), prison_r_lm_rankname(PlaceholderFlags.RANKS, PlaceholderFlags.ALIAS), + prison_r_lmt_rankname(PlaceholderFlags.RANKS, PlaceholderFlags.ALIAS), @@ -521,6 +539,7 @@ public enum PrisonPlaceHolders { prison_rank__id_rankname(prison_r_id_rankname, PlaceholderFlags.RANKS), prison_rank__player_count_rankname(prison_r_pc_rankname, PlaceholderFlags.RANKS), prison_rank__linked_mines_rankname(prison_r_lm_rankname, PlaceholderFlags.RANKS), + prison_rank__linked_mines_tag_rankname(prison_r_lmt_rankname, PlaceholderFlags.RANKS), diff --git a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/PlayerRank.java b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/PlayerRank.java index f0bd2819e..96342b3ec 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/PlayerRank.java +++ b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/PlayerRank.java @@ -51,7 +51,15 @@ public void applyMultiplier( double rankMultiplier ) { protected void setRankCost( double rankMultiplier ) { - this.rankCost = rank.getCost() * (1.0 + rankMultiplier); + boolean applyMultiplier = rank.getLadder().isApplyRankCostMultiplierToLadder(); + + if ( applyMultiplier ) { + + this.rankCost = rank.getCost() * (1.0 + rankMultiplier); + } + else { + this.rankCost = rank.getCost(); + } } public double getLadderBasedRankMultiplier() { @@ -108,72 +116,81 @@ public double getLadderBasedRankMultiplier( Rank rank ) { // } - public PlayerRank getTargetPlayerRankForPlayer( RankPlayer player, Rank targetRank ) { - return getTargetPlayerRankForPlayer( this, player, targetRank ); - } - - public PlayerRank getTargetPlayerRankForPlayer( PlayerRank playerRank, RankPlayer player, Rank targetRank ) - { - PlayerRank targetPlayerRank = null; - - if ( targetRank != null ) - { - - double targetRankMultiplier = playerRank.getLadderBasedRankMultiplier( targetRank ); - - - PlayerRank pRankForPLayer = player.getLadderRanks().get( targetRank.getLadder() ); - - // PlayerRank pRankForPLayer = getRank( player, targetRank.getLadder() ); - double existingRankMultiplier = pRankForPLayer == null ? 0 - : playerRank.getLadderBasedRankMultiplier( pRankForPLayer.getRank() ); +// public PlayerRank getTargetPlayerRankForPlayer( RankPlayer player, Rank targetRank ) { +// return getTargetPlayerRankForPlayer( this, player, targetRank ); +// } - // Get the player's total rankMultiplier from the default ladder - // because they will always have a rank there: - RankLadder defaultLadder = getDefaultLadder( player ); - - PlayerRank pRank = player.getLadderRanks().get( defaultLadder ); -// PlayerRank pRank = getRank( player, "default" ); - double playerMultipler = pRank == null ? 0 : pRank.getRankMultiplier(); - - // So the actual rank multiplier that needs to be used, is based upon - // the - // Player's current multiplier PLUS the multiplier for the target rank - // AND MINUS the multiplier for the current rank the player has within - // the - // target rank's ladder. - double rankMultiplier = playerMultipler + targetRankMultiplier - existingRankMultiplier; - - targetPlayerRank = createPlayerRank( targetRank, rankMultiplier ); - } +// public PlayerRank getTargetPlayerRankForPlayer( PlayerRank playerRank, RankPlayer player, Rank targetRank ) +// { +// PlayerRank targetPlayerRank = null; +// +// if ( targetRank != null ) +// { +// +// double targetRankMultiplier = playerRank.getLadderBasedRankMultiplier( targetRank ); +// +// +// PlayerRank pRankForPLayer = player.getLadderRanks().get( targetRank.getLadder() ); +// +// // PlayerRank pRankForPLayer = getRank( player, targetRank.getLadder() ); +// double existingRankMultiplier = pRankForPLayer == null ? 0 +// : playerRank.getLadderBasedRankMultiplier( pRankForPLayer.getRank() ); +// +// // Get the player's total rankMultiplier from the default ladder +// // because they will always have a rank there: +// RankLadder defaultLadder = getDefaultLadder( player ); +// +// PlayerRank pRank = player.getLadderRanks().get( defaultLadder ); +//// PlayerRank pRank = getRank( player, "default" ); +// double playerMultipler = pRank == null ? 0 : pRank.getRankMultiplier(); +// +// // So the actual rank multiplier that needs to be used, is based upon +// // the +// // Player's current multiplier PLUS the multiplier for the target rank +// // AND MINUS the multiplier for the current rank the player has within +// // the +// // target rank's ladder. +// double rankMultiplier = playerMultipler + targetRankMultiplier - existingRankMultiplier; +// +// targetPlayerRank = createPlayerRank( targetRank, rankMultiplier ); +// } +// +// return targetPlayerRank; +// } - return targetPlayerRank; - } - private RankLadder getDefaultLadder( RankPlayer player ) - { - RankLadder defaultLadder = null; - - for ( RankLadder ladder : player.getLadderRanks().keySet() ) - { - if ( ladder.getName().equalsIgnoreCase( "default" ) ) { - defaultLadder = ladder; - } - } - - return defaultLadder; - } +// private RankLadder getDefaultLadder( RankPlayer player ) +// { +// return player.getPlayerRankDefault().getRank().getLadder(); +//// RankLadder defaultLadder = null; +//// +//// for ( RankLadder ladder : player.getLadderRanks().keySet() ) +//// { +//// if ( ladder.getName().equalsIgnoreCase( "default" ) ) { +//// defaultLadder = ladder; +//// } +//// } +//// +//// return defaultLadder; +// } - private PlayerRank createPlayerRank( Rank rank, double rankMultiplier ) { - PlayerRank results = new PlayerRank( rank, rankMultiplier ); - - return results; - } +// private PlayerRank createPlayerRank( Rank rank, double rankMultiplier ) { +// PlayerRank results = new PlayerRank( rank, rankMultiplier ); +// +// return results; +// } public Rank getRank() { return rank; } + public String getCurrency() { + + String currency = getRank() == null ? "" : getRank().getCurrency(); + + return currency; + } + public Double getRankMultiplier() { return rankMultiplier; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java index b9ef21843..7bb160ba1 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java +++ b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankLadder.java @@ -53,7 +53,7 @@ public class RankLadder private double rankCostMultiplierPerRank = 0.0d; - + private boolean applyRankCostMultiplierToLadder = true; private boolean dirty = false; @@ -65,6 +65,7 @@ public RankLadder() { this.ranks = new ArrayList<>(); + this.applyRankCostMultiplierToLadder = true; } public RankLadder( int id, String name ) { @@ -242,6 +243,8 @@ public Document toDocument() { ret.put( "rankCostMultiplierPerRank", getRankCostMultiplierPerRank() ); + ret.put( "applyRankCostMultiplierToLadder", isApplyRankCostMultiplierToLadder() ); + // ret.put("maxPrestige", this.maxPrestige); // ret.put( "permissions", getPermissions() ); @@ -533,6 +536,13 @@ public void setRankCostMultiplierPerRank( double rankCostMultiplierPerRank ) { this.rankCostMultiplierPerRank = rankCostMultiplierPerRank; } + public boolean isApplyRankCostMultiplierToLadder() { + return applyRankCostMultiplierToLadder; + } + public void setApplyRankCostMultiplierToLadder(boolean applyRankCostMultiplierToLadder) { + this.applyRankCostMultiplierToLadder = applyRankCostMultiplierToLadder; + } + public boolean isDirty() { return dirty; } diff --git a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java index b80cf1403..cf02b33f4 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java +++ b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/RankPlayer.java @@ -30,6 +30,7 @@ import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.cache.PlayerCache; import tech.mcprison.prison.cache.PlayerCachePlayerData; +import tech.mcprison.prison.file.JsonFileIO; import tech.mcprison.prison.integration.EconomyCurrencyIntegration; import tech.mcprison.prison.integration.EconomyIntegration; import tech.mcprison.prison.internal.ItemStack; @@ -53,8 +54,9 @@ public class RankPlayer public static final long DELAY_THREE_SECONDS = 20 * 3; // 3 seconds in ticks - public static final long RANK_SCORE_COOLDOWN_MS = 1000 * 30; // 30 seconds - public static final double RANK_SCORE_BALANCE_THRESHOLD_PERCENT = 0.05d; // 5% + // The cooldown time for when the rank score will be recalculated +// public static final long RANK_SCORE_COOLDOWN_MS = 1000 * 60 * 5; // 5 minutes +// public static final double RANK_SCORE_BALANCE_THRESHOLD_PERCENT = 0.05d; // 5% /* @@ -88,7 +90,7 @@ public class RankPlayer private Object unsavedBalanceLock = new Object(); private int ubTaskId = 0; - private HashMap economyCustom = new HashMap<>();; +// private HashMap economyCustom = new HashMap<>();; @@ -99,12 +101,13 @@ public class RankPlayer * be a recalculation of the score. *

*/ - private double rankScoreBalance = 0; - private String rankScoreCurrency = null; - private double rankScoreBalanceThreshold = 0; private double rankScore = 0; private double rankScorePenalty = 0; - private long rankScoreCooldown = 0L; + private double rankScoreBalance = 0; + private String rankScoreCurrency = null; + +// private double rankScoreBalanceThreshold = 0; +// private long rankScoreCooldown = 0L; public RankPlayer() { @@ -131,6 +134,20 @@ public RankPlayer( UUID uid, String playerName ) { checkName( playerName ); } + +// public RankPlayer clone() { +// RankPlayer clone = new RankPlayer( getUUID() ); +// +// clone.setBalance( getBalance() ); +// +// Set keys = getLadderRanks().keySet(); +// for (RankLadder key : keys) { +// +// clone.ladderRanks.put( key, getLadderRanks().get( key ) ); +// } +// +// return clone; +// } // @SuppressWarnings( "unchecked" ) // public RankPlayer(Document document) { @@ -200,6 +217,28 @@ public String toString() { return getName() + " " + getRanks(); } + + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public String getPlayerFileName() { + + return JsonFileIO.getPlayerFileName( this ); + } + + public String getRanks() { StringBuilder sb = new StringBuilder(); @@ -374,7 +413,7 @@ public void addRank( Rank rank) { ranksRefs.put(ladderName, rank.getId()); - PlayerRank pRank = new PlayerRank( rank ); + PlayerRank pRank = createPlayerRank( rank ); ladderRanks.put( rank.getLadder(), pRank ); @@ -385,15 +424,55 @@ public void addRank( Rank rank) { // Calculate and apply the rank multipliers: recalculateRankMultipliers(); } + + /** + *

This does not update the RankPlayer with the passed rank or the generated PlayerRank. + * This only crates a new PlayerRank object without setting the multiplier. + *

+ * + * @param rank + * @return + */ + public PlayerRank createPlayerRank( Rank rank ) { + PlayerRank pRank = new PlayerRank( rank, 1.0 ); + + return pRank; + } + /** + *

This will calculate the total multipliers and set that multiplier + * on all of the player's current ranks. This is for their + * actual ranks and ladders. + *

+ * + */ public void recalculateRankMultipliers() { + + recalculateRankMultipliers( getLadderRanks() ); + + } + + /** + *

This function will use a **targetLadderRanks** TreeMap and will + * calculate it's total multipliers for the ranks and ladders that are + * within this TreeMap. + *

+ * + *

This can be used to calculate the cost of a given target rank, based + * upon just changing that target rank's ladder entry to the target rank. + *

+ * + * @param targetLadderRanks + */ + public void recalculateRankMultipliers( + TreeMap targetLadderRanks ) { double multiplier = 0; // First gather and calculate the multipliers: - Set keys = ladderRanks.keySet(); + Set keys = targetLadderRanks.keySet(); for ( RankLadder rankLadder : keys ) { - PlayerRank pRank = ladderRanks.get( rankLadder ); + PlayerRank pRank = targetLadderRanks.get( rankLadder ); double rankMultiplier = pRank.getLadderBasedRankMultiplier(); multiplier += rankMultiplier; @@ -402,12 +481,64 @@ public void recalculateRankMultipliers() { // We now have the multipliers, so apply them to all ranks: for ( RankLadder rankLadder : keys ) { - PlayerRank pRank = ladderRanks.get( rankLadder ); + PlayerRank pRank = targetLadderRanks.get( rankLadder ); pRank.applyMultiplier( multiplier ); // pRank.setRankCost( pRank.getRank().getCost() * (1.0 + multiplier) ); } + } + + + /** + *

This function will taken any rank, on any ladder, and will + * properly calculate it's multiplier (which is based upon all ladders + * and the ranks within those ladders, and the targetRank's rank cost. + *

+ * + *

This is the ONLY valid way to calculate target rank costs for a player. + *

+ * + * @param targetRank + * @return + */ + public PlayerRank calculateTargetPlayerRank( Rank targetRank ) { + PlayerRank targetPlayerRank = null; + + // Can only process if the target rank is not null and it has a ladder: + if ( targetRank != null && targetRank.getLadder() != null ) { + + // Need to get the targetRank's ladder. Not all ranks have ladders. + RankLadder targetLadder = targetRank.getLadder(); + + // Create a new PlayerRank object for this target rank. + // Ignore rank cost multipliers since that will be applied later. + targetPlayerRank = new PlayerRank( targetRank ); + + // Create a new temp targetLadderRanks TreeMap: + TreeMap targetLadderRanks = new TreeMap<>(); + + // Copy the player's actual ladderRanks to the targetLadderRanks: + Set keys = getLadderRanks().keySet(); + for (RankLadder key : keys) { + PlayerRank pRank = getLadderRanks().get( key ); + + targetLadderRanks.put( key, pRank ); + } + // Now add our targetPlayerRank to the targetLadderRanks: + targetLadderRanks.put( targetLadder, targetPlayerRank ); + + + // Now recalculate all multipliers and the rank costs for the targetPlayerRank: + recalculateRankMultipliers( targetLadderRanks ); + + } + + // The targetPlayerRank now has the correct total multiplier from all + // ladders, and it's Rank Cost is based upon those multipliers and if + // the ladder should apply the multipliers or not: + + return targetPlayerRank; } /** @@ -598,6 +729,44 @@ public PlayerRank getPlayerRankPrestiges() { return getPlayerRank( "prestiges" ); } + /** + *

Always return the default rank's position. + * This has a value of >= 0 and <= the highest rank's position on the + * default ladder. Rank positions are zero based. + *

+ * + *

Since all players must have a default rank, this can never + * return a value of negative one. + *

+ * + * @return + */ + public int getRankPositonDefault() { + + int pos = getPlayerRankDefault().getRank().getPosition(); + return pos; + } + + /** + *

This returns the rank position for the prestiges ladder, with + * a value of negative one if the player does not have a prestige rank. + * This has a value of >= -1 and <= the highest rank position on the + * prestiges ladder. Rank positions are zero based. + *

+ * + * @return + */ + public int getRankPositonPrestiges() { + PlayerRank rankPres = getPlayerRankPrestiges(); + + int pos = rankPres == null ? + -1 : + rankPres.getRank().getPosition(); + + return pos; + } + + public HashMap getRanksRefs(){ return ranksRefs ; } @@ -1245,15 +1414,32 @@ public boolean isSneaking() { return false; } - /** - *

Calculates the rankScore for the player's rank on the default ladder. - * The calculation is based upon how much the next rank costs. - *

- * - */ - private void calculateRankScore() { + public PlayerRank getNextPlayerRank() { PlayerRank rankCurrent = getPlayerRankDefault(); - + + // If player does not have a default rank, then assign them one: + if ( rankCurrent == null ) { + Prison.get().getPlatform().checkPlayerDefaultRank( this ); + + rankCurrent = getPlayerRankDefault(); + } + + if ( rankCurrent == null ) { + Output.get().logError( + String.format( + "ERROR Player has no default ladder rank: %s A player should never be without a " + + "rank on the default ladder. Something corrupted prison. Contact prison support. " + + "Make sure you do not manually modify any of the config files, which can lead to corruption. ", + getName() + )); + Output.get().logError( + String.format( + "NOTE: Restarting the server could allow prison to repair players that are corrupted. " + + "Please try restarting the server to see if that fixes the problem before contacting " + + "prison's support team. Thanks!" + )); + } + Rank nRank = rankCurrent.getRank().getRankNext(); // If player does not have a next rank, then try to use the next prestige rank: @@ -1273,21 +1459,103 @@ private void calculateRankScore() { } - String rankNextCurrency = nRank == null ? "" : nRank.getCurrency(); + PlayerRank pRankNext = calculateTargetPlayerRank( nRank ); +// PlayerRank pRankNext = rankCurrent.getTargetPlayerRankForPlayer( this, nRank ); + + return pRankNext; + } + + /** + *

Calculates the rankScore for the player's rank on the default ladder. + * The calculation is based upon how much the next rank costs. + *

+ * + */ + public void calculateRankScore() { + + PlayerRank pRankNext = getNextPlayerRank(); - PlayerRank pRankNext = rankCurrent.getTargetPlayerRankForPlayer( this, nRank ); + String rankNextCurrency = pRankNext == null ? "" : pRankNext.getCurrency(); double cost = pRankNext == null ? 0d : pRankNext.getRankCost(); + double balance = getBalance( rankNextCurrency ); - double score = balance; +// RankPlayerBalance cachedBalance = getCachedRankPlayerBalance( rankNextCurrency, true ); +// +// double balance = cachedBalance.getBalance(); + + calculateRankScore( rankNextCurrency, cost, balance ); + + +// PlayerRank rankCurrent = getPlayerRankDefault(); + +// Rank nRank = rankCurrent.getRank().getRankNext(); +// +// // If player does not have a next rank, then try to use the next prestige rank: +// if ( nRank == null ) { +// PlayerRank prestigeRankCurrent = getPlayerRankPrestiges(); +// +// // if they don't have a current prestige rank, then use the lowest rank: +// if ( prestigeRankCurrent == null ) { +// RankLadder rLadder = getRankLadder( RankLadder.PRESTIGES ); +// nRank = rLadder == null ? null : rLadder.getLowestRank().orElse(null); +// } +// +// if ( prestigeRankCurrent != null ) { +// nRank = prestigeRankCurrent.getRank() == null ? +// null : prestigeRankCurrent.getRank().getRankNext(); +// } +// +// } +// +// +// PlayerRank pRankNext = rankCurrent.getTargetPlayerRankForPlayer( this, nRank ); + +// String rankNextCurrency = nRank == null ? "" : nRank.getCurrency(); +// double balance = getBalance( rankNextCurrency ); + + +// double balance = getBalance( rankNextCurrency ); +// double score = balance; +// double penalty = 0d; +// +// // Do not apply the penalty if cost is zero: +// if ( cost > 0 && isHesitancyDelayPenaltyEnabled() ) { +// score = balance > cost ? cost : score; +// +// double excess = balance > cost ? balance - cost : 0d; +// penalty = excess * 0.2d; +// } +// +// score = (score - penalty); +// +// if ( cost > 0 ) { +// score /= cost * 100.0d; +// } +// +//// double balanceThreshold = cost * RANK_SCORE_BALANCE_THRESHOLD_PERCENT; +// +//// setRankScoreBalance( balance ); +//// setRankScoreCurrency( rankNextCurrency ); +//// setRankScoreBalanceThreshold( balanceThreshold ); +// setRankScore( score ); +// setRankScorePenalty( penalty ); +// +//// setRankScoreCooldown( System.currentTimeMillis() + RANK_SCORE_COOLDOWN_MS ); + } + + private void calculateRankScore( String currency, double cost, double playerBalance ) { + + double score = playerBalance; + double penalty = 0d; // Do not apply the penalty if cost is zero: if ( cost > 0 && isHesitancyDelayPenaltyEnabled() ) { - score = balance > cost ? cost : score; + score = playerBalance > cost ? cost : playerBalance; - double excess = balance > cost ? balance - cost : 0d; + double excess = playerBalance > cost ? playerBalance - cost : 0d; penalty = excess * 0.2d; } @@ -1297,40 +1565,48 @@ private void calculateRankScore() { score /= cost * 100.0d; } - double balanceThreshold = cost * RANK_SCORE_BALANCE_THRESHOLD_PERCENT; - - setRankScoreBalance( balance ); - setRankScoreCurrency( rankNextCurrency ); - setRankScoreBalanceThreshold( balanceThreshold ); setRankScore( score ); setRankScorePenalty( penalty ); - - setRankScoreCooldown( System.currentTimeMillis() + RANK_SCORE_COOLDOWN_MS ); - } - - private void checkRecalculateRankScore() { - - if ( getRankScoreCooldown() == 0L || - System.currentTimeMillis() > getRankScoreCooldown() - ) { - - double currentBalance = getBalance( getRankScoreCurrency() ); - - if ( getRankScoreBalance() != 0 && ( - currentBalance == getRankScoreBalance() || - currentBalance >= (getRankScoreBalance() - getRankScoreBalanceThreshold()) || - currentBalance <= (getRankScoreBalance() + getRankScoreBalanceThreshold() ) )) { - - // increment the cooldown since the balance is either the same, or still - // within the threshold range: - setRankScoreCooldown( System.currentTimeMillis() + RANK_SCORE_COOLDOWN_MS ); - } - else { - calculateRankScore(); - } - } + setRankScoreBalance( playerBalance ); + setRankScoreCurrency( currency ); } +// private void checkRecalculateRankScore() { +// +// calculateRankScore(); +// +//// if ( getRankScoreCooldown() == 0L || +//// System.currentTimeMillis() > getRankScoreCooldown() +//// ) { +//// +//// double currentBalance = getBalance( getRankScoreCurrency() ); +//// +//// if ( getRankScoreBalance() != 0 && ( +//// currentBalance == getRankScoreBalance() || +//// currentBalance >= (getRankScoreBalance() - getRankScoreBalanceThreshold()) || +//// currentBalance <= (getRankScoreBalance() + getRankScoreBalanceThreshold() ) )) { +//// +//// // increment the cooldown since the balance is either the same, or still +//// // within the threshold range: +//// setRankScoreCooldown( System.currentTimeMillis() + RANK_SCORE_COOLDOWN_MS ); +//// } +//// else { +//// calculateRankScore( currentBalance ); +//// } +//// } +// } + +// /** +// *

By setting rankScoreCooldown to zero, it will force that player to have it's +// * rank score to be recalculated. The most expensive part is getting the player's +// * balance from Vault. +// *

+// * +// */ +// public void forcePlayerToRecalculateRankScore() { +// rankScoreCooldown = 0L; +// } + public static String printRankScoreLine1Header() { String header = String.format( "Rank %-16s %-9s %-6s %-9s %-9s %-9s", @@ -1358,7 +1634,7 @@ public String printRankScoreLine1( int rankPostion ) { String prestRankTagNc = Text.stripColor(prestRankTag); String defRankTagNc = Text.stripColor(defRankTag); - String balanceStr = PlaceholdersUtil.formattedKmbtSISize( getBalance(), dFmt, " " ); + String balanceStr = PlaceholdersUtil.formattedKmbtSISize( getRankScoreBalance(), dFmt, " " ); String sPenaltyStr = PlaceholdersUtil.formattedKmbtSISize( getRankScorePenalty(), dFmt, " " ); String message = String.format( @@ -1404,7 +1680,7 @@ public String printRankScoreLine2( int rankPostion ) { String prestRankTagNc = Text.stripColor(prestRankTag); String defRankTagNc = Text.stripColor(defRankTag); - String balanceStr = PlaceholdersUtil.formattedKmbtSISize( getBalance(), dFmt, " " ); + String balanceStr = PlaceholdersUtil.formattedKmbtSISize( getRankScoreBalance(), dFmt, " " ); // String sPenaltyStr = PlaceholdersUtil.formattedKmbtSISize( getRankScorePenalty(), dFmt, " " ); String ranks = prestRankTagNc + defRankTagNc; @@ -1443,16 +1719,17 @@ public void setRankScoreCurrency( String rankScoreCurrency ) { this.rankScoreCurrency = rankScoreCurrency; } - public double getRankScoreBalanceThreshold() { - return rankScoreBalanceThreshold; - } - public void setRankScoreBalanceThreshold( double rankScoreBalanceThreshold ) { - this.rankScoreBalanceThreshold = rankScoreBalanceThreshold; - } +// public double getRankScoreBalanceThreshold() { +// return rankScoreBalanceThreshold; +// } +// public void setRankScoreBalanceThreshold( double rankScoreBalanceThreshold ) { +// this.rankScoreBalanceThreshold = rankScoreBalanceThreshold; +// } + public double getRankScore() { // check if the rankScore needs to be reset: - checkRecalculateRankScore(); +// checkRecalculateRankScore(); return rankScore; } @@ -1467,10 +1744,10 @@ public void setRankScorePenalty( double rankScorePenalty ) { this.rankScorePenalty = rankScorePenalty; } - public long getRankScoreCooldown() { - return rankScoreCooldown; - } - public void setRankScoreCooldown( long rankScoreCooldown ) { - this.rankScoreCooldown = rankScoreCooldown; - } +// public long getRankScoreCooldown() { +// return rankScoreCooldown; +// } +// public void setRankScoreCooldown( long rankScoreCooldown ) { +// this.rankScoreCooldown = rankScoreCooldown; +// } } diff --git a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/StatsRankPlayerBalanceData.java b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/StatsRankPlayerBalanceData.java index 2267bb596..94cf3440e 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/ranks/data/StatsRankPlayerBalanceData.java +++ b/prison-core/src/main/java/tech/mcprison/prison/ranks/data/StatsRankPlayerBalanceData.java @@ -44,7 +44,8 @@ public void recalc( boolean isPenaltyEnabled ) { if ( rank.getRankNext() != null ) { // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank pRankNext = pRank.getTargetPlayerRankForPlayer( player, rank.getRankNext() ); + PlayerRank pRankNext = player.calculateTargetPlayerRank( rank.getRankNext() ); +// PlayerRank pRankNext = pRank.getTargetPlayerRankForPlayer( player, rank.getRankNext() ); //PlayerRank pRankNext = new PlayerRank( rank.getRankNext(), pRank.getRankMultiplier() ); cost = pRankNext.getRankCost(); diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/PrisonStatsUtil.java b/prison-core/src/main/java/tech/mcprison/prison/util/PrisonStatsUtil.java new file mode 100644 index 000000000..692770c62 --- /dev/null +++ b/prison-core/src/main/java/tech/mcprison/prison/util/PrisonStatsUtil.java @@ -0,0 +1,275 @@ +package tech.mcprison.prison.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.discord.PrisonPasteChat; +import tech.mcprison.prison.file.JsonFileIO; +import tech.mcprison.prison.output.ChatDisplay; +import tech.mcprison.prison.output.Output; + +public class PrisonStatsUtil { + + public ChatDisplay displayVersion(String options) { + + boolean isBasic = options == null || "basic".equalsIgnoreCase(options); + + ChatDisplay display = new ChatDisplay("/prison version"); + display.addText("&7Prison Version: %s", Prison.get().getPlatform().getPluginVersion()); + + display.addText("&7Running on Platform: %s", Prison.get().getPlatform().getClass().getName()); + display.addText("&7Minecraft Version: %s", Prison.get().getMinecraftVersion()); + + // System stats: + display.addText(""); + + Prison.get().displaySystemSettings(display); + + Prison.get().displaySystemTPS(display); + + display.addText(""); + + // This generates the module listing, the autoFeatures overview, + // the integrations listings, and the plugins listings. + boolean showLaddersAndRanks = true; + Prison.get().getPlatform().prisonVersionFeatures(display, isBasic, showLaddersAndRanks); + + return display; + } + + public StringBuilder getSupportSubmitVersionData() { + ChatDisplay display = displayVersion("ALL"); + StringBuilder text = display.toStringBuilder(); + return text; + } + + public StringBuilder getSupportSubmitConfigsData() { + Prison.get().getPlatform().saveResource("plugin.yml", true); + + String fileNames = "config.yml plugin.yml backups/versions.log " + + "autoFeaturesConfig.yml blockConvertersConfig.json " + + "modules.yml module_conf/mines/config.json module_conf/mines/mineBombsConfig.json " + + "SellAllConfig.yml GuiConfig.yml backpacks/backpacksconfig.yml"; + List files = convertNamesToFiles(fileNames); + + StringBuilder text = new StringBuilder(); + + for (File file : files) { + + addFileToText(file, text); + + if (file.getName().equalsIgnoreCase("plugin.yml")) { + file.delete(); + } + } + return text; + } + + + public void copyConfigsFiles() { + + String fileNames = "config.yml " + + "autoFeaturesConfig.yml blockConvertersConfig.json " + + "modules.yml module_conf/mines/mineBombsConfig.json " + + "SellAllConfig.yml GuiConfig.yml backpacks/backpacksconfig.yml"; + List files = convertNamesToFiles(fileNames); + + JsonFileIO fio = new JsonFileIO(); + + for (File file : files) { + + if ( file.exists() ) { + File buFile = fio.getBackupFile( file, "newPrisonVersion", "bu" ); + + try { + Files.copy( file.toPath(), buFile.toPath() ); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + } + } + + + + public StringBuilder getSupportSubmitRanksData() { + List files = listFiles("data_storage/ranksDb/ladders/", ".json"); + files.addAll(listFiles("data_storage/ranksDb/ranks/", ".json")); + + StringBuilder text = new StringBuilder(); + + text.append(Prison.get().getPlatform().getRanksListString()); + printFooter(text); + + for (File file : files) { + + addFileToText(file, text); + + } + return text; + } + + public StringBuilder getSupportSubmitMinesData() { + List files = listFiles("data_storage/mines/mines/", ".json"); + Collections.sort(files); + + StringBuilder text = new StringBuilder(); + + text.append("\n"); + text.append("Table of contents:\n"); + text.append(" 1. Mine list - All mines including virtual mines: /mines list all\n"); + text.append(" 2. Mine info - All mines: /mines info all\n"); + text.append(" 3. Mine files - Raw JSON dump of all mine configuration files.\n"); + text.append("\n"); + + // Display a list of all mines, then display the /mines info all for + // each: + text.append(Prison.get().getPlatform().getMinesListString()); + printFooter(text); + + for (File file : files) { + + addFileToText(file, text); + + } + return text; + } + + public StringBuilder getSupportSubmitListenersData( String listenerType ) { + + StringBuilder sb = new StringBuilder(); + + if ( listenerType == null ) { + listenerType = "all"; + } + + if ( "blockBreak".equalsIgnoreCase( listenerType ) || "all".equalsIgnoreCase( listenerType ) ) { + + sb.append( Prison.get().getPlatform().dumpEventListenersBlockBreakEvents() ); + } + + if ( "chat".equalsIgnoreCase( listenerType ) || "all".equalsIgnoreCase( listenerType ) ) { + + sb.append( Prison.get().getPlatform().dumpEventListenersPlayerChatEvents() ); + } + +// if ( "traceBlockBreak".equalsIgnoreCase( listenerType ) ) { +// +// Prison.get().getPlatform().traceEventListenersBlockBreakEvents( sender ); +// +// } + + if ( "playerInteract".equalsIgnoreCase( listenerType ) || "all".equalsIgnoreCase( listenerType ) ) { + + sb.append( Prison.get().getPlatform().dumpEventListenersPlayerInteractEvents() ); + } + + return sb; + } + + public void readFileToStringBulider(File textFile, StringBuilder text) { + try (BufferedReader br = Files.newBufferedReader(textFile.toPath());) { + String line = br.readLine(); + while (line != null && text.length() < PrisonPasteChat.HASTEBIN_MAX_LENGTH) { + + text.append(line).append("\n"); + + line = br.readLine(); + } + + if (text.length() > PrisonPasteChat.HASTEBIN_MAX_LENGTH) { + + String trimMessage = "\n\n### Log has been trimmed to a max length of " + + PrisonPasteChat.HASTEBIN_MAX_LENGTH + "\n"; + int pos = PrisonPasteChat.HASTEBIN_MAX_LENGTH - trimMessage.length(); + + text.insert(pos, trimMessage); + text.setLength(PrisonPasteChat.HASTEBIN_MAX_LENGTH); + } + + } catch (IOException e) { + Output.get().logInfo("Failed to read log file: %s [%s]", textFile.getAbsolutePath(), e.getMessage()); + return; + } + } + + private List listFiles(String path, String fileSuffix) { + List files = new ArrayList<>(); + + File dataFolder = Prison.get().getDataFolder(); + File filePaths = new File(dataFolder, path); + + for (File file : filePaths.listFiles()) { + if (file.getName().toLowerCase().endsWith(fileSuffix.toLowerCase())) { + files.add(file); + } + } + + return files; + } + + private void addFileToText(File file, StringBuilder sb) { + DecimalFormat dFmt = new DecimalFormat("#,##0"); + SimpleDateFormat sdFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + sb.append("\n"); + + JumboTextFont.makeJumboFontText(file.getName(), sb); + + sb.append("\n"); + + sb.append("File Name: ").append(file.getName()).append("\n"); + sb.append("File Path: ").append(file.getAbsolutePath()).append("\n"); + sb.append("File Size: ").append(dFmt.format(file.length())).append(" bytes\n"); + sb.append("File Date: ").append(sdFmt.format(new Date(file.lastModified()))).append(" bytes\n"); + sb.append("File Stats: ").append(file.exists() ? "EXISTS " : "").append(file.canRead() ? "READABLE " : "") + .append(file.canWrite() ? "WRITEABLE " : "").append("\n"); + + sb.append("\n"); + sb.append("=== --- --- --- --- --- --- --- --- --- ===\n"); + sb.append("\n"); + + if (file.exists() && file.canRead()) { + readFileToStringBulider(file, sb); + } else { + sb.append("Warning: The file is not readable so it cannot be included.\n"); + } + + printFooter(sb); + } + + public void printFooter(StringBuilder sb) { + + sb.append("\n\n\n"); + sb.append("=== --- === --- === --- === --- === --- ===\n"); + sb.append("=== # # ### # # # ### # # # ### # # # ### # # # ### # # ===\n"); + sb.append("=== --- === --- === --- === --- === --- ===\n"); + sb.append("\n\n"); + + } + + private List convertNamesToFiles(String fileNames) { + List files = new ArrayList<>(); + + File dataFolder = Prison.get().getDataFolder(); + + for (String fileName : fileNames.split(" ")) { + File file = new File(dataFolder, fileName); + files.add(file); + } + + return files; + } + +} 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 ad8d23548..2429735de 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 @@ -51,6 +51,8 @@ public class Text private static final long millisPerMonth = 31 * millisPerDay; private static final long millisPerYear = 365 * millisPerDay; + private static String unitPrefixSpacer = " "; + private static Map unitMillis = CollectionUtil .map( "year:years", millisPerYear, @@ -105,6 +107,8 @@ public static void initialize() { unit_time_text_from_now = coreOutputTextFromNowMsg(); unit_time_text_and = coreOutputTextAndMsg(); + unitPrefixSpacer = coreOutputUnitPrefixSpacer(); + String timeUnitsSingular = coreOutputTextTimeUnitsSingularMsg(); String timeUnitsPlural = coreOutputTextTimeUnitsPluralMsg(); @@ -580,7 +584,7 @@ public static String getTimeUntilString(long millis) { } millisLeft -= unitSize * unitCount; - unitCountParts.add(unitCount + " " + + unitCountParts.add(unitCount + unitPrefixSpacer + ( unitCount == 1 ? unitNameSingular : unitNamePlural) ); } diff --git a/prison-core/src/main/java/tech/mcprison/prison/util/TextMessage.java b/prison-core/src/main/java/tech/mcprison/prison/util/TextMessage.java index f96e520cb..7d3ebf2ce 100644 --- a/prison-core/src/main/java/tech/mcprison/prison/util/TextMessage.java +++ b/prison-core/src/main/java/tech/mcprison/prison/util/TextMessage.java @@ -54,6 +54,14 @@ protected static String coreOutputTextAndMsg() { .localize(); } + protected static String coreOutputUnitPrefixSpacer() { + return Prison.get().getLocaleManager() + .getLocalizable( "core_text__time_units_prefix_spacer" ) + .withReplacements( "%s" ) + .setFailSilently() + .localize(); + } + protected static String coreOutputTextTimeUnitsSingularMsg() { return Prison.get().getLocaleManager() .getLocalizable( "core_text__time_units_singular" ) diff --git a/prison-core/src/main/resources/lang/core/de_DE.properties b/prison-core/src/main/resources/lang/core/de_DE.properties index 217bca79d..deb59d124 100644 --- a/prison-core/src/main/resources/lang/core/de_DE.properties +++ b/prison-core/src/main/resources/lang/core/de_DE.properties @@ -70,7 +70,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -95,6 +95,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=Die Welt %1 wurde nicht gefunden. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/en_GB.properties b/prison-core/src/main/resources/lang/core/en_GB.properties index 1b1ce12b2..f9b079bd7 100644 --- a/prison-core/src/main/resources/lang/core/en_GB.properties +++ b/prison-core/src/main/resources/lang/core/en_GB.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=The world %1 was not found. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/en_US.properties b/prison-core/src/main/resources/lang/core/en_US.properties index 0e19b2347..9533f6cea 100644 --- a/prison-core/src/main/resources/lang/core/en_US.properties +++ b/prison-core/src/main/resources/lang/core/en_US.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=7 +messages__version=9 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -173,6 +174,7 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 core_gui__delay=&3Delay: %1 secs diff --git a/prison-core/src/main/resources/lang/core/es_ES.properties b/prison-core/src/main/resources/lang/core/es_ES.properties index c7633d829..590e00ba6 100644 --- a/prison-core/src/main/resources/lang/core/es_ES.properties +++ b/prison-core/src/main/resources/lang/core/es_ES.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=El mundo %1 no ha sido encontrado. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/hu_HU.properties b/prison-core/src/main/resources/lang/core/hu_HU.properties index e6e453261..6dcfc75bc 100644 --- a/prison-core/src/main/resources/lang/core/hu_HU.properties +++ b/prison-core/src/main/resources/lang/core/hu_HU.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=A(z) %1 világ nem található. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/it_IT.properties b/prison-core/src/main/resources/lang/core/it_IT.properties index a9721af90..3da4b9a97 100644 --- a/prison-core/src/main/resources/lang/core/it_IT.properties +++ b/prison-core/src/main/resources/lang/core/it_IT.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=Il mondo %1 non è stato trovato. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/nl_BE.properties b/prison-core/src/main/resources/lang/core/nl_BE.properties index ab01ed187..6e970e303 100644 --- a/prison-core/src/main/resources/lang/core/nl_BE.properties +++ b/prison-core/src/main/resources/lang/core/nl_BE.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s diff --git a/prison-core/src/main/resources/lang/core/nl_NL.properties b/prison-core/src/main/resources/lang/core/nl_NL.properties index ab01ed187..f8be4ce03 100644 --- a/prison-core/src/main/resources/lang/core/nl_NL.properties +++ b/prison-core/src/main/resources/lang/core/nl_NL.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -139,7 +140,15 @@ worldNotFound=De wereld %1 kon niet worden gevonden. core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/core/pt_PT.properties b/prison-core/src/main/resources/lang/core/pt_PT.properties index 035b42fe8..0a3281bf5 100644 --- a/prison-core/src/main/resources/lang/core/pt_PT.properties +++ b/prison-core/src/main/resources/lang/core/pt_PT.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=2 +messages__version=3 messages__auto_refresh=true @@ -87,6 +87,29 @@ core_output__error_startup_failure=Prison: (Sending to System.err due to Output. core_output__error_incorrect_number_of_parameters=Log Failure (%1): Incorrect number of parameters: [%2] Original raw message: [%3] Arguments: %4 +core_text__prefix=&3 +core_text__just_now=just now +core_text__ago=ago +core_text__from_now=from now +core_text__and=and +core_text__time_units_singular=year,month,week,day,hour,minute,second +core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds +core_text__time_units_short=y,m,w,d,h,m,s + + +core_tokens__name_required=Prison Tokens=A player's name is required when used from console. +core_tokens__cannot_view_others_balances=Prison Tokens: You do not have permission to view other player's balances. +core_tokens__view_balance=&3%1 has %2 tokens. +core_tokens__add_invalid_amount=Prison Tokens: Invalid amount: '%1'. Must be greater than zero. +core_tokens__added_amount=&3%1 now has &7%2 &3tokens after adding &7%3&3. +core_tokens__removed_amount=&3%1 now has &7%2 &3tokens after removing &7%3&3. +core_tokens__set_amount=&3%1 now has &7%2 &3tokens. + + +core_runCmd__name_required=A valid player name is required. +core_runCmd__command_required=A command is required. + + core_prison_utf8_test=Привет! Давай поÑмотрим, работает ли? Test 01 @@ -109,3 +132,52 @@ tooFewCharacters=O parametro [%1] tem de ser maior ou igual a %2 characteres. tooManyCharacters=O parametro [%1] tem de ser menor ou igual a %2 characteres. playerNotOnline=The player %1 is not online. worldNotFound=O mundo %1 não foi encontrado. + + + + + +core_gui__click_to_decrease=&3Clica para aumentar. +core_gui__click_to_increase=&3Clica para diminuir. + + +core_gui__click_to_cancel=&3Clica para cancelar. +core_gui__click_to_close=&3Clica para fechar. +core_gui__click_to_confirm=&3Clica para confirmar. +core_gui__click_to_delete=&3Clica para eleminar. +core_gui__click_to_disable=&3Clica para desativar. +core_gui__click_to_edit=&3Clica para editar. +core_gui__click_to_enable=&3Clica para ativar. +core_gui__click_to_open=&3Clica para abrir. + + +core_gui__left_click_to_confirm=&3Clique-Esquerdo para confirmar. +core_gui__left_click_to_reset=&3Clique-Esquerdo para resetar. +core_gui__left_click_to_open=&3Clique-Esquerdo para abrir. +core_gui__left_click_to_edit=&3Clique-Esquerdo para editar. + + +core_gui__right_click_to_cancel=&3Clique-Direito para cancelar. +core_gui__right_click_to_delete=&3Clique-Direito para eliminar. +core_gui__right_click_to_disable=&3Clique-Direito para desativar. +core_gui__right_click_to_enable=&3Clique-Direito para ativar. +core_gui__right_click_to_toggle=&3Clique-Direito para ativar/desativar. + + +core_gui__right_click_and_shift_to_delete=&3Clique-Direito e shift para eliminar. +core_gui__right_click_and_shift_to_disable=&3Clique-Direito e shift para desativar. +core_gui__right_click_and_shift_to_toggle=&3Clique-Direito e shift para ativar/desativar. + + +core_gui__page_next=&3Pagina seguinte. +core_gui__page_prior=&3Pagina anterior. + + +core_gui__price=&3Preco: %1 +core_gui__confirm=&3Confirmar: %1 %2 +core_gui__delay=&3Atrado: %1 secs +core_gui__multiplier=&3Multiplicador: x %1 +core_gui__value=&3Valor: %1 +core_gui__permission=&3Permissão: &7%1 +core_gui__prestige_name=&3Nome do Prestige: %1 + diff --git a/prison-core/src/main/resources/lang/core/ro_RO.properties b/prison-core/src/main/resources/lang/core/ro_RO.properties index 3693e68b8..ed5bf728a 100644 --- a/prison-core/src/main/resources/lang/core/ro_RO.properties +++ b/prison-core/src/main/resources/lang/core/ro_RO.properties @@ -65,6 +65,57 @@ # ne transmiÈ›i ceva, poÈ›i contacta un membru staff de pe server-ul nostru de Discord. #MulÈ›umim pentru contribuÈ›ii! # + + +messages__version=3 +messages__auto_refresh=true + + +core_output__prefix_template=| %1 | &7 +core_output__prefix_template_prison=Prison +core_output__prefix_template_info=Info +core_output__prefix_template_warning=Warning +core_output__prefix_template_error=Error +core_output__prefix_template_debug=Debug + +core_output__color_code_info=&3 +core_output__color_code_warning=&c +core_output__color_code_error=&c +core_output__color_code_debug=&9 + +core_output__error_startup_failure=Prison: (Sending to System.err due to Output.log Logger failure): +core_output__error_incorrect_number_of_parameters=Log Failure (%1): Incorrect number of parameters: [%2] Original raw message: [%3] Arguments: %4 + + +core_text__prefix=&3 +core_text__just_now=just now +core_text__ago=ago +core_text__from_now=from now +core_text__and=and +core_text__time_units_prefix_spacer= +core_text__time_units_singular=year,month,week,day,hour,minute,second +core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds +core_text__time_units_short=y,m,w,d,h,m,s + + +core_tokens__name_required=Prison Tokens=A player's name is required when used from console. +core_tokens__cannot_view_others_balances=Prison Tokens: You do not have permission to view other player's balances. +core_tokens__view_balance=&3%1 has %2 tokens. +core_tokens__add_invalid_amount=Prison Tokens: Invalid amount: '%1'. Must be greater than zero. +core_tokens__added_amount=&3%1 now has &7%2 &3tokens after adding &7%3&3. +core_tokens__removed_amount=&3%1 now has &7%2 &3tokens after removing &7%3&3. +core_tokens__set_amount=&3%1 now has &7%2 &3tokens. + + +core_runCmd__name_required=A valid player name is required. +core_runCmd__command_required=A command is required. + + +core_prison_utf8_test=\u041F\u0440\u0438\u0432\u0435\u0442! \u0414\u0430\u0432\u0430\u0439 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438\u043C, \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043B\u0438? Test 01 + + +# The following are the original messages and they will eventually be replaced. + includeError=[%1] are o valoare invalidă. excludeError=[%1] are o valoare invalidă. cantAsConsole=Nu poÈ›i face asta drept consolă. @@ -82,3 +133,53 @@ tooFewCharacters=Parametrul [%1] trebuie să aibă mai mult de %2 caractere. tooManyCharacters=Parametrul [%1] trebuie să aiba mai puÈ›in de %2 caractere. playerNotOnline=Jucătorul %1 nu este online. worldNotFound=Lumea %1 nu există. + + + + + +core_gui__click_to_decrease=&3Click to decrease. +core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. +core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. +core_gui__click_to_open=&3Click to open. + + +core_gui__left_click_to_confirm=&3Left-Click to confirm. +core_gui__left_click_to_reset=&3Left-Click to reset. +core_gui__left_click_to_open=&3Left-Click to open. +core_gui__left_click_to_edit=&3Left-Click to edit. + + +core_gui__right_click_to_cancel=&3Right-Click to cancel. +core_gui__right_click_to_delete=&3Right-Click to delete. +core_gui__right_click_to_disable=&3Right-Click to disable. +core_gui__right_click_to_enable=&3Right-Click to enable. +core_gui__right_click_to_toggle=&3Right-Click to toggle. + + +core_gui__right_click_and_shift_to_delete=&3Right-Click and shift to delete. +core_gui__right_click_and_shift_to_disable=&3Right-Click and shift to disable. +core_gui__right_click_and_shift_to_toggle=&3Right-Click and shift to toggle. + + +core_gui__page_next=&3Next page. +core_gui__page_prior=&3Prior page. + + +core_gui__money_earned=&3You earned &a$%1 +core_gui__price=&3Price: %1 +core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs +core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 +core_gui__prestige_name=&3Prestige name: %1 + diff --git a/prison-core/src/main/resources/lang/core/zh_TW.properties b/prison-core/src/main/resources/lang/core/zh_TW.properties index a5da3ad22..7cc23d779 100644 --- a/prison-core/src/main/resources/lang/core/zh_TW.properties +++ b/prison-core/src/main/resources/lang/core/zh_TW.properties @@ -67,7 +67,7 @@ # like to share, please contact a staff member on our Discord server. #Thanks for your contributions! # -messages__version=6 +messages__version=8 messages__auto_refresh=true @@ -92,6 +92,7 @@ core_text__just_now=just now core_text__ago=ago core_text__from_now=from now core_text__and=and +core_text__time_units_prefix_spacer= core_text__time_units_singular=year,month,week,day,hour,minute,second core_text__time_units_plural=years,months,weeks,days,hours,minutes,seconds core_text__time_units_short=y,m,w,d,h,m,s @@ -110,6 +111,11 @@ core_runCmd__name_required=A valid player name is required. core_runCmd__command_required=A command is required. +core_prison_utf8_test=\u041F\u0440\u0438\u0432\u0435\u0442! \u0414\u0430\u0432\u0430\u0439 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438\u043C, \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043B\u0438? Test 01 + + +# The following are the original messages and they will eventually be replaced. + includeError=[%1] 有一個無效值 excludeError=[%1] 有一個無效值 cantAsConsole=您ä¸èƒ½ä½œç‚ºæŽ§åˆ¶å°åŸ·è¡Œæ­¤æ“作 @@ -132,14 +138,17 @@ worldNotFound=世界 %1 無法找到 - - - - - core_gui__click_to_decrease=&3Click to decrease. core_gui__click_to_increase=&3Click to increase. + + +core_gui__click_to_cancel=&3Click to cancel. +core_gui__click_to_close=&3Click to close. +core_gui__click_to_confirm=&3Click to confirm. +core_gui__click_to_delete=&3Click to delete. +core_gui__click_to_disable=&3Click to disable. core_gui__click_to_edit=&3Click to edit. +core_gui__click_to_enable=&3Click to enable. core_gui__click_to_open=&3Click to open. @@ -165,8 +174,12 @@ core_gui__page_next=&3Next page. core_gui__page_prior=&3Prior page. +core_gui__money_earned=&3You earned &a$%1 core_gui__price=&3Price: %1 core_gui__confirm=&3Confirm: %1 %2 +core_gui__delay=&3Delay: %1 secs core_gui__multiplier=&3Multiplier: x %1 +core_gui__value=&3Value: %1 +core_gui__permission=&3Permission: &7%1 core_gui__prestige_name=&3Prestige name: %1 diff --git a/prison-core/src/main/resources/lang/mines/pt_PT.properties b/prison-core/src/main/resources/lang/mines/pt_PT.properties index 199b66191..f37cc42c9 100644 --- a/prison-core/src/main/resources/lang/mines/pt_PT.properties +++ b/prison-core/src/main/resources/lang/mines/pt_PT.properties @@ -51,7 +51,7 @@ -messages__version=2 +messages__version=3 messages__auto_refresh=true @@ -89,3 +89,11 @@ block_deleted=&7Bloco removido &3%1 &7da mina &3%2&7. mine_redefined=&7A mina foi &3redefenida &7com sucesso. missing_world=&7O mundo em que a mina foi criada não consegue ser encontrado. block_search_blank=&7Insere um valor para procurar pelo bloco.&7. + +mines_mtp__unable_to_teleport=Desculpa não és capaz de te teletransportar para alí. +mines_mtp__unable_to_teleport_others=&3Não podes teletransportar outros jogadores para a mina. Ignoring parameter. +mines_mtp__no_target_mine_found=Mina especifica não encontrada. &3Reenvia o pedido de teletransporte com o nome de uma mina. +mines_mtp__player_must_be_in_game=mines_mtp__player_must_be_in_game +mines_mtp__player_must_be_in_game=&3Jogador especifico não está no servidor por isso não pode ser teletransportado. +mines_mtp__cannot_use_virtual_mines=&cOpção invalida a mina é uma mina virtual&7. Usa &a/mines set area &7para ativares a mina. +mines_mtp__teleport_failed=&3O teletransporte falhou. Tens a certeza que és um jogador? diff --git a/prison-core/src/main/resources/lang/ranks/en_US.properties b/prison-core/src/main/resources/lang/ranks/en_US.properties index b0b22b070..02b52d679 100644 --- a/prison-core/src/main/resources/lang/ranks/en_US.properties +++ b/prison-core/src/main/resources/lang/ranks/en_US.properties @@ -50,7 +50,7 @@ ## -messages__version=24 +messages__version=26 messages__auto_refresh=true ranks_rankup__rankup_no_player_name=You have @@ -81,6 +81,7 @@ ranks_rankup__rankup_failure_invalid_ladder=The ladder '%1' does not exist. ranks_rankup__rankup_failure_must_be_online_player=&3You must be a player in the game to run this command, and/or the player must be online. ranks_rankup__no_permission=You need the permission '%1' to rank up on this ladder. ranks_rankup__cannot_run_from_console=&7Cannot run rankup from console. See &3/rankup help&7. +ranks_rankup__invalid_player_name=&7Invalid player name. '%1' ranks_rankup__internal_failure=&7Invalid rankup mode. Internal failure. Please report. ranks_rankup__error_no_default_ladder=&c[ERROR] There isn't a default ladder! Please report this to an admin! ranks_rankup__error_no_lower_rank=&c[ERROR] Can't get the lowest rank! Please report this to an admin! @@ -222,6 +223,8 @@ ranks_LadderCommands__ladder_has_no_perms=&3The ladder '&7%1&3' contains no perm ranks_LadderCommands__ladder_set_rank_cost_multiplier=&3The ladder '&7%1&3' was saved. The Rank Cost Multiplier is now [%2]; was [%3]. ranks_LadderCommands__ladder_rank_cost_multiplier_no_change=&3The ladder '&7%1&3' was not updated. The supplied Rank Cost Multiplier did not change.[%2] ranks_LadderCommands__ladder_rank_cost_multiplier_out_of_range=&3The Rank Cost Multiplier is out of range. It must be between -100% and 100%. [%1] +ranks_LadderCommands__ladder_apply_rank_cost_multiplier_no_change=&3The ladder '&7%1&3' was not updated. The applied rank cost multiplier to this ladder did not change. [%2] +ranks_LadderCommands__ladder_apply_rank_cost_multiplier_saved=&3The ladder '&7%1&3' was saved. The Applying of Rank Cost Multiplier to this ladder is now [%2]; was [%3]. ranks_rankCommands__rank_already_exists=&3The rank named &7%1 &3already exists. Try a different name. @@ -262,6 +265,7 @@ ranks_rankCommands__rank_delete_error=The rank '%1' could not be deleted due to ranks_rankCommands__ranks_list_header=&3Ranks in the &7%1 &3Ladder ranks_rankCommands__ranks_list_ladder_cost_multplier=&3 Ladder Rank Cost Multiplier per Rank: &7%1 +ranks_rankCommands__ranks_list_ladder_apply_ranks_cost_multplier=&3 Apply global Rank Cost Multipliers to this Rank? &7%1 ranks_rankCommands__ranks_list_ladder_edit_cost_multplier=Edit this Ladder's Rank Cost Multiplier. ranks_rankCommands__ranks_list_click_to_edit=&7Click on a rank's name to view more info. diff --git a/prison-core/src/main/resources/lang/ranks/pt_PT.properties b/prison-core/src/main/resources/lang/ranks/pt_PT.properties index 5ae461f60..f75dfecea 100644 --- a/prison-core/src/main/resources/lang/ranks/pt_PT.properties +++ b/prison-core/src/main/resources/lang/ranks/pt_PT.properties @@ -50,61 +50,61 @@ ## -messages__version=3 +messages__version=4 messages__auto_refresh=true -ranks_rankup__rankup_no_player_name=Tu têns -ranks_rankup__rankup_no_player_name_broadcast=Alguém -ranks_rankup__rankup_you_are=Tue és -ranks_rankup__rankup_success=Parabéns! %1 subiste para o rank '%2'. %3 -ranks_rankup__demote_success=Infelismente, %1 desceu de rank '%2'. %3 -ranks_rankup__log_rank_change=%1 iniciado alteraçao de rank: %2 -ranks_rankup__rankup_cant_afford=Não tens dinheiro suficiente para fazeres rankup. O proximo rankup custa %1 %2. -ranks_rankup__rankup_lowest=%1 já estás no rank mais baixo! -ranks_rankup__rankup_highest=%1 já estás no rank mais alto! -ranks_rankup__rankup_failure=Falha genérica no rankup. Reve os detalhes do rankup para identificar o motivo. +ranks_rankup__rankup_no_player_name=Tu têns +ranks_rankup__rankup_no_player_name_broadcast=Alguém +ranks_rankup__rankup_you_are=Tu és +ranks_rankup__rankup_success=Parabéns! %1 subiste para o rank '%2'. %3 +ranks_rankup__demote_success=Infelizmente, %1 desceu para o rank '%2'. %3 +ranks_rankup__log_rank_change=%1 iniciado alteraçao de rank: %2 +ranks_rankup__rankup_cant_afford=Não tens dinheiro suficiente para fazeres rankup. O proximo rankup custa %1 %2. +ranks_rankup__rankup_lowest=%1 já estás no rank mais baixo! +ranks_rankup__rankup_highest=%1 já estás no rank mais alto! +ranks_rankup__rankup_failure=Falha genérica no rankup. Reve os detalhes do rankup para identificar o motivo. ranks_rankup__rankup_failed_to_load_player=Erro a carregar info do player. ranks_rankup__rankup_failed_to_load_ladder=Erro a carregar a ladder. ranks_rankup__rankup_failed_to_assign_rank=Erro a dar o rank. ranks_rankup__rankup_failed_to_save_player_file=Falha ao recuperar ou gravar dados. Os teus arquivos podem estar corrompidos. Reposta a um administrador. -ranks_rankup__rankup_no_ranks=Não existe ranks nesta ladder. -ranks_rankup__rankup_rank_does_not_exist=O rank %1 não existe neste server. -ranks_rankup__rankup_rank_is_not_in_ladder=O rank %1 não existe naladder %2. -ranks_rankup__rankup_currency_is_not_supported=A economia não é suportada, %1. +ranks_rankup__rankup_no_ranks=Não existe ranks nesta ladder. +ranks_rankup__rankup_rank_does_not_exist=O rank %1 não existe neste server. +ranks_rankup__rankup_rank_is_not_in_ladder=O rank %1 não existe naladder %2. +ranks_rankup__rankup_currency_is_not_supported=A economia não é suportada, %1. ranks_rankup__rankup_ladder_removed=A ladder %1 foi removida. ranks_rankup__rankup_failure_removing_ladder=Rankup falhou porque nao foi possivel remover o player da ladder %1. (Players cannot be removed from the 'default' ladder). ranks_rankup__rankup_in_progress_failure=Rankup falhou ser complet corretamente. -ranks_rankup__rankup_failure_to_get_rankplayer=Não existes! O servidor nao têm registo teu. Tenta reentrar no server ou contactar um administrador. -ranks_rankup__rankup_failure_invalid_ladder=A ladder '%1' não existe. +ranks_rankup__rankup_failure_to_get_rankplayer=Não existes! O servidor nao têm registo teu. Tenta reentrar no server ou contactar um administrador. +ranks_rankup__rankup_failure_invalid_ladder=A ladder '%1' não existe. ranks_rankup__rankup_failure_must_be_online_player=&3Tu tens de ser um player para usares este comando, e/ou o player tem de estar online. -ranks_rankup__no_permission=Tu precisas de permissão '%1' para dares rankup nesta ladder. -ranks_rankup__cannot_run_from_console=&7 Não é possível fazer rank na consola. Veja &3/rankup para ajuda&7. +ranks_rankup__no_permission=Tu precisas de permissão '%1' para dares rankup nesta ladder. +ranks_rankup__cannot_run_from_console=&7 Não é possível fazer rank na consola. Veja &3/rankup para ajuda&7. ranks_rankup__internal_failure=&7Modo invalido de rankup. Falha interna. Reporta ao admin. -ranks_rankup__error_no_default_ladder=&c[ERROR] Não existe uma ladder default! Reporta ao admin! -ranks_rankup__error_no_lower_rank=&c[ERROR] Não consgues ter rank mais baixo! Reporta ao admin! +ranks_rankup__error_no_default_ladder=&c[ERROR] Não existe uma ladder default! Reporta ao admin! +ranks_rankup__error_no_lower_rank=&c[ERROR] Não consgues ter rank mais baixo! Reporta ao admin! -ranks_rankup__error_no_ladder=&c[ERROR] A ladder %1 não existe! Reporta ao admin! -ranks_rankup__error_no_lower_rank_on_ladder=&c[ERROR] A ladder %1 não tem ranks! Reporta ao admin! +ranks_rankup__error_no_ladder=&c[ERROR] A ladder %1 não existe! Reporta ao admin! +ranks_rankup__error_no_lower_rank_on_ladder=&c[ERROR] A ladder %1 não tem ranks! Reporta ao admin! -ranks_rankup__error_player_not_on_default_ladder=&c[ERROR] O player nao está numa default ladder. Player: %1 -ranks_rankup__not_at_last_rank=&cTu não estas no rank mais baixo! -ranks_rankup__not_able_to_prestige=&7[&3Sorry&7] &3Tu não conseguiste fazer &6Prestige! +ranks_rankup__error_player_not_on_default_ladder=&c[ERROR] O player nao está numa default ladder. Player: %1 +ranks_rankup__not_at_last_rank=&cTu não estas no rank mais baixo! +ranks_rankup__not_able_to_prestige=&7[&3Sorry&7] &3Tu não conseguiste fazer &6Prestige! ranks_rankup__not_able_to_reset_rank=&7Incapaz de resetar o rank para a default ladder. ranks_rankup__balance_set_to_zero=&7O teu dinheiro foi posto a 0. -ranks_rankup__prestige_successful=&7[&3Parabéns&7] &3Tu consegiste o &6Prestige&3 to %1&c! -ranks_rankup__prestige_failure=&7[&3Sorry&7] &3Não conseguiste fazer o &6Prestige&3 to %1&c! -ranks_rankup__invalid_charge_value=&3Valor inválido para chargePlayer. Os valores válidos são: %1 %2 -ranks_rankup__invalid_refund_value=&3Valor inválido para refundPlayer. Os valores válidos são: %1 %2 +ranks_rankup__prestige_successful=&7[&3Parabéns&7] &3Tu consegiste o &6Prestige&3 to %1&c! +ranks_rankup__prestige_failure=&7[&3Sorry&7] &3Não conseguiste fazer o &6Prestige&3 to %1&c! +ranks_rankup__invalid_charge_value=&3Valor inválido para chargePlayer. Os valores válidos são: %1 %2 +ranks_rankup__invalid_refund_value=&3Valor inválido para refundPlayer. Os valores válidos são: %1 %2 ranks_rankutil__failure_internal=Falha ao executar rankupPlayerInternal verificar logs do servidor para stack trace: %1 ranks_rankutil__failure_saving_player_data=Ocorreu um erro ao salvar os ficheiros do player. -ranks_firstJoinHandler__no_ranks_on_server=Não há ranks no servidor! Novo jogador não tem rank. -ranks_firstJoinHandler__could_not_save_player=Não foi possivel guardar as informações do player. +ranks_firstJoinHandler__no_ranks_on_server=Não há ranks no servidor! Novo jogador não tem rank. +ranks_firstJoinHandler__could_not_save_player=Não foi possivel guardar as informações do player. ranks_firstJoinHandler__success=Welcome! %1 acabou de entrar e recebeu o rank default da ladder. @@ -123,27 +123,27 @@ ranks_prisonRanks__status_loaded_ranks=Ranks %1 Carregado. ranks_prisonRanks__status_loaded_ladders=Ladders %1 Carregado. ranks_prisonRanks__status_loaded_players=Players %1 Carregado. -ranks_prisonRanks__failure_with_ladder=&cFalha ao %1 uma nova %2 ladder, uma pré-existente não foi encontrada. +ranks_prisonRanks__failure_with_ladder=&cFalha ao %1 uma nova %2 ladder, uma pré-existente não foi encontrada. ranks_prisonRanks__failure_with_ladder_create=create ranks_prisonRanks__failure_with_ladder_save=save ranks_prisonRanks__failure_with_ladder_default=default ranks_prisonRanks__failure_with_ladder_prestiges=prestiges -ranks_prisonRanks__added_new_player=&7Prisão: &cNovo jogador adicionado &7à Prison: &3%1 &7foi encontrado no servidor. -ranks_prisonRanks__added_and_fixed_players=Prison Rank Loader: Adicionados %1 jogadores à prison. Corrigidos %2 jogadores que não tinham um rank na default ladder. +ranks_prisonRanks__added_new_player=&7Prisão: &cNovo jogador adicionado &7à Prison: &3%1 &7foi encontrado no servidor. +ranks_prisonRanks__added_and_fixed_players=Prison Rank Loader: Adicionados %1 jogadores à prison. Corrigidos %2 jogadores que não tinham um rank na default ladder. -ranks_rank__failure_loading_ranks=&aFalha: A Carregar Ranks! &7Exceção analisando documentos de classificação. Rank ID= %1 nome= %2 [%3] +ranks_rank__failure_loading_ranks=&aFalha: A Carregar Ranks! &7Exceção analisando documentos de classificação. Rank ID= %1 nome= %2 [%3] -ranks_rankManager__failure_loading_rankManager=&aFalha: A carregar ladder %1 (ID da ladder: %2): &7Não é possível carregar o RankManager, portanto não é possível acessar nenhum rank. -ranks_rankManager__failure_duplicate_rank=&aFailure: Falha ao carregar RankLadder: Rank '%1' já estava ligado à ladder '%2', mas tentou-se adicionar à ladder '%3'. Este rank não será vinculada à ladder '%4' +ranks_rankManager__failure_loading_rankManager=&aFalha: A carregar ladder %1 (ID da ladder: %2): &7Não é possível carregar o RankManager, portanto não é possível acessar nenhum rank. +ranks_rankManager__failure_duplicate_rank=&aFailure: Falha ao carregar RankLadder: Rank '%1' já estava ligado à ladder '%2', mas tentou-se adicionar à ladder '%3'. Este rank não será vinculada à ladder '%4' ranks_rankManager__remove_rank_warning=Remove Rank Warning: No fallback rank exists so players with the rank that is being removed will have no rank on that ladder. -ranks_rankManager__cannot_save_player_file=RemoveRank: Não foi possível salvar o ficheiro do player. +ranks_rankManager__cannot_save_player_file=RemoveRank: Não foi possível salvar o ficheiro do player. ranks_rankManager__player_is_now=Player %1 is now %2 -ranks_rankManager__cannot_save_ladder_file=RemoveRank: Não foi possível salvar a ladder %1. -ranks_rankManager__failure_no_economy=Falha na Economia: &7A moeda &a%1&7 foi registrada com o rank &a%2&7, mas não é suportada por nenhuma integração da Economia. +ranks_rankManager__cannot_save_ladder_file=RemoveRank: Não foi possível salvar a ladder %1. +ranks_rankManager__failure_no_economy=Falha na Economia: &7A moeda &a%1&7 foi registrada com o rank &a%2&7, mas não é suportada por nenhuma integração da Economia. ranks_rankManager__ranks_by_ladders=&7Ranks by ladders: @@ -154,7 +154,7 @@ ranks_playerManager__cannot_save_player_file=Ocorreu um erro ao salvar os fichei ranks_playerManager__cannot_add_new_player=PlayerManager.getPlayer(): Falha ao adicionar novo nome de jogador: %1. %2 ranks_playerManager__cannot_save_new_player_file=Falha ao criar novo ficheiro de dados do jogador para o jogador %1 nome do ficheiro de destino: %2 ranks_playerManager__no_player_name_available= -ranks_playerManager__cannot_load_player_file=Não foi possível carregar o jogador: % 1 +ranks_playerManager__cannot_load_player_file=Não foi possível carregar o jogador: % 1 ranks_playerManager__failed_to_load_economy_currency=Falha ao carregar Economia para obter o saldo do jogador %1 com uma moeda de %2. ranks_playerManager__failed_to_load_economy=Falha ao carregar Economia para obter o saldo do jogador %1. ranks_playerManager__last_rank_message_for__prison_rankup_rank_tag_default= @@ -305,11 +305,11 @@ ranks_rankCommands__set_tag_success=&cThe tag name was changed to %1 for the ran ranks_rankCommands__player_must_be_online=&3You must be a player in the game to run this command, and/or the player must be online. ranks_rankCommands__player_ladder_info=&7Ladder: &b%1 &7Rank Atual: &b%2 -ranks_rankCommands__player_ladder_highest_rank= É o rank mais alto! +ranks_rankCommands__player_ladder_highest_rank= É o rank mais alto! ranks_rankCommands__player_ladder_next_rank=&7 Proximo rank: &b%1&7 &c$&b%2 ranks_rankCommands__player_ladder_next_rank_currency=&7 Moeda: &2%1 -ranks_rankCommands__player_balance_default=&7O dinheiro de momento de &b%1 &7é &b%2 -ranks_rankCommands__player_balance_others=&7O dinheiro de momento de &b%1 &7é &b%2 &2%3 +ranks_rankCommands__player_balance_default=&7O dinheiro de momento de &b%1 &7é &b%2 +ranks_rankCommands__player_balance_others=&7O dinheiro de momento de &b%1 &7é &b%2 &2%3 ranks_rankCommands__player_perms_offline=&7 Notice: &3The player is offline so permissions are not available nor accurate. ranks_rankCommands__player_sellall_multiplier=&7 Multiplicador Sellall: &b%1 %2 ranks_rankCommands__player_not_accurate=&5(&2Not Accurate&5) diff --git a/prison-core/src/main/resources/lang/ranks/zh_TW.properties b/prison-core/src/main/resources/lang/ranks/zh_TW.properties index a76592fe1..69b58c5af 100644 --- a/prison-core/src/main/resources/lang/ranks/zh_TW.properties +++ b/prison-core/src/main/resources/lang/ranks/zh_TW.properties @@ -50,7 +50,7 @@ ## -messages__version=5 +messages__version=7 messages__auto_refresh=true ranks_rankup__rankup_no_player_name=您已經 @@ -73,6 +73,7 @@ ranks_rankup__rankup_rank_does_not_exist=階級 %1 ä¸å­˜åœ¨ ranks_rankup__rankup_rank_is_not_in_ladder=此階級 %1 ä¸å­˜åœ¨æ–¼éšŽ %2 之中 ranks_rankup__rankup_currency_is_not_supported=這個貨幣, %1, 無法使用於此 ranks_rankup__rankup_ladder_removed=階 %1 已經刪除 +ranks_rankup__rankup_failure_removing_ladder=Rankup failed since the player could not be removed from the ladder %1. (Players cannot be removed from the 'default' ladder). ranks_rankup__rankup_in_progress_failure=無法正常的å‡ç´š. 此階級ä¸å­˜åœ¨ ranks_rankup__rankup_failure_to_get_rankplayer=您的資料ä¸å­˜åœ¨æˆ–伺æœå™¨æ²’有您的紀錄,請嘗試é‡æ–°åŠ å…¥,或å‘伺æœå™¨ç®¡ç†å“¡å–å¾—å”助 @@ -80,9 +81,15 @@ ranks_rankup__rankup_failure_invalid_ladder=此階 '%1' ä¸å­˜åœ¨ ranks_rankup__rankup_failure_must_be_online_player=&3您必須是玩家æ‰èƒ½ä½¿ç”¨é€™å€‹æŒ‡ä»¤æˆ–您必須在線上 ranks_rankup__no_permission=您需è¦æ¬Šé™ %1 來å‡ç´šé€™å€‹éšŽ ranks_rankup__cannot_run_from_console=&7console端ä¸èƒ½åŸ·è¡Œå‡éšŽç´šæŒ‡ä»¤ 請查看 &3/rankup help&7 +ranks_rankup__invalid_player_name=&7Invalid player name. '%1' ranks_rankup__internal_failure=&7無效的å‡éšŽç´šæ¨¡å¼,內部錯誤,請通知管ç†å“¡ ranks_rankup__error_no_default_ladder=&c[錯誤] 這邊ä¸æ˜¯é è¨­çš„階! 請通知管ç†å“¡! ranks_rankup__error_no_lower_rank=&c[錯誤] 無法å–得最低等級! 請通知管ç†å“¡! + +ranks_rankup__error_no_ladder=&c[ERROR] The ladder %1 does not exist! Please report this to an admin! +ranks_rankup__error_no_lower_rank_on_ladder=&c[ERROR] The ladder %1 has no ranks! Please report this to an admin! + +ranks_rankup__error_player_not_on_default_ladder=&c[ERROR] The player is not on the default ladder. Player: %1 ranks_rankup__not_at_last_rank=&c你沒有在上個階級! ranks_rankup__not_able_to_prestige=&7[&3抱歉&7] &3您ä¸èƒ½ä½¿ç”¨ &6è²æœ›! ranks_rankup__not_able_to_reset_rank=&7無法é‡è£½æ‚¨çš„é è¨­éšŽ @@ -115,7 +122,6 @@ ranks_prisonRanks__failed_loading_players=&c無法載入玩家: %1 ranks_prisonRanks__failed_to_load_player_file=玩家資料載入失敗. %1 ranks_prisonRanks__status_loaded_ranks=載入 %1 éšŽç´ default ranks: %2 prestige ranks: %3 other ranks: %4 -š ranks_prisonRanks__status_loaded_ladders=載入 %1 階 ranks_prisonRanks__status_loaded_players=載入 %1 玩家 @@ -153,9 +159,11 @@ ranks_playerManager__no_player_name_available=<沒有å稱å¯ä½¿ç”¨> ranks_playerManager__cannot_load_player_file=無法載入玩家: %1 ranks_playerManager__failed_to_load_economy_currency=玩家載入經濟æ’件失敗玩家載入經濟æ’件失敗 %1 貨幣 %2 ranks_playerManager__failed_to_load_economy=玩家載入經濟æ’件失敗%1 +ranks_playerManager__last_rank_message_for__prison_rankup_rank_tag_default= +ranks_commandCommands__command_add_cannot_use_percent_symbols=&7Cannot use percent symbols as placeholder escape characters; must use { } instead. ranks_commandCommands__command_add_placeholders=&7自訂 rank çš„ placeholders 指令為: &3%1 ranks_commandCommands__rank_does_not_exist=階級 '%1' ä¸å­˜åœ¨ ranks_commandCommands__command_add_duplicate=é‡è¤‡çš„指令 '%1' 沒有新增到 '%2' @@ -199,6 +207,7 @@ ranks_LadderCommands__ladder_added_rank=新增 '%1' 至階 '%2' 在ä½ç½® %3 ranks_LadderCommands__ladder_deleted=階 '%1' 已被刪除 ranks_LadderCommands__ladder_cannot_delete_default=您無法刪除é è¨­éšŽ ranks_LadderCommands__ladder_cannot_delete_prestiges=它是必須的,您ä¸èƒ½åˆªé™¤ è²æœ› 階 +ranks_LadderCommands__ladder_cannot_delete_with_ranks=Cannot delete a ladder if it still has ranks tied to it. Remove all ranks and try again. ranks_LadderCommands__ladder_error=刪除階時發生錯誤. &8請查看console端以ç²å¾—更多資訊 ranks_LadderCommands__ladder_error_adding=增加階級至階時發生錯誤. &8請查看console端以ç²å¾—更多資訊 ranks_LadderCommands__ladder_error_removing=刪除階內的階級時發生錯誤. &8請查看console端以ç²å¾—更多資訊 @@ -211,6 +220,11 @@ ranks_LadderCommands__ladder_has_ranks=&7此階包å«ä»¥ä¸‹çš„階級: ranks_LadderCommands__ladder_default_rank=&b(&9é è¨­éšŽç´š&b) &7- ranks_LadderCommands__ladder_see_ranks_list=&3ç€è¦½ &f/階級列表 &b[ladderName] &3以ç²å¾—更多階級細節 ranks_LadderCommands__ladder_has_no_perms=&3階 '&7%1&3' 沒有包å«æ¬Šé™æˆ–權é™ç¾¤çµ„ +ranks_LadderCommands__ladder_set_rank_cost_multiplier=&3The ladder '&7%1&3' was saved. The Rank Cost Multiplier is now [%2]; was [%3]. +ranks_LadderCommands__ladder_rank_cost_multiplier_no_change=&3The ladder '&7%1&3' was not updated. The supplied Rank Cost Multiplier did not change.[%2] +ranks_LadderCommands__ladder_rank_cost_multiplier_out_of_range=&3The Rank Cost Multiplier is out of range. It must be between -100% and 100%. [%1] +ranks_LadderCommands__ladder_apply_rank_cost_multiplier_no_change=&3The ladder '&7%1&3' was not updated. The applied rank cost multiplier to this ladder did not change. [%2] +ranks_LadderCommands__ladder_apply_rank_cost_multiplier_saved=&3The ladder '&7%1&3' was saved. The Applying of Rank Cost Multiplier to this ladder is now [%2]; was [%3]. ranks_rankCommands__rank_already_exists=&3階級å稱&7%1 &3已經存在. 試著用別的å稱 @@ -233,6 +247,8 @@ ranks_rankCommands__auto_config_skip_rank_warning=&a警告! &3階級7%1 &3已存 ranks_rankCommands__auto_config_no_ranks_created=Ranks autoConfigure: 沒有建立任何階級 ranks_rankCommands__auto_config_ranks_created=Ranks autoConfigure: %1 階級已被建立 ranks_rankCommands__auto_config_no_rank_cmds_created=Ranks autoConfigure: 沒有建立任何階級指令 +ranks_rankCommands__auto_config_ladder_rank_cost_multiplier_info=The 'prestiges' ladder has been enabled to apply a Base Rank Cost Multiplier of %1 that will be applied to 'all' rank costs. This multiplier will be increased with each rank on the ladder. +ranks_rankCommands__auto_config_ladder_rank_cost_multiplier_command_example=The Base Rank Cost Multiplier can be adjusted, or disabled, with the command: '/ranks ladder rankCostMultiplier ranks_rankCommands__auto_config_rank_cmds_created=Ranks autoConfigure: %1 階級指令已被建立 ranks_rankCommands__auto_config_no_mines_created=Ranks autoConfigure: 沒有建立任何礦場 @@ -247,7 +263,11 @@ ranks_rankCommands__rank_was_removed=階級 '%1' 已刪除æˆåŠŸ ranks_rankCommands__rank_delete_error=階級 '%1' 因出ç¾éŒ¯èª¤è€Œåˆªé™¤å¤±æ•— -ranks_rankCommands__ranks_list_header=%1 中的 階級 +ranks_rankCommands__ranks_list_header=%1 中的 éšŽç´ +šranks_rankCommands__ranks_list_ladder_cost_multplier=&3 Ladder Rank Cost Multiplier per Rank: &7%1 +ranks_rankCommands__ranks_list_ladder_apply_ranks_cost_multplier=&3 Apply global Rank Cost Multipliers to this Rank? &7%1 +ranks_rankCommands__ranks_list_ladder_edit_cost_multplier=Edit this Ladder's Rank Cost Multiplier. + ranks_rankCommands__ranks_list_click_to_edit=&7點擊階級å稱來ç€è¦½æ›´å¤šè³‡è¨Š ranks_rankCommands__ranks_list_command_count= &7- 指令 &3%1 ranks_rankCommands__ranks_list_currency= &3貨幣&2%1 diff --git a/prison-core/src/main/resources/lang/sellall/pt_PT.properties b/prison-core/src/main/resources/lang/sellall/pt_PT.properties new file mode 100644 index 000000000..56d723001 --- /dev/null +++ b/prison-core/src/main/resources/lang/sellall/pt_PT.properties @@ -0,0 +1,67 @@ +# NOTE: A messages__version is an arbitrary integer that will be manually incremented within Prison +# when there are changes to these messages. This value represents when message content is +# changed, fixed, or added to. This value may not be increased if the change is very small and +# insignificant, such as a space or a couple of letters. +# +# messages__auto_refresh=true indicates that this file will automatically be replaced if +# Prison detects a messages__version difference. The old file will be deleted (renamed) and +# a new copy will be placed in the directory to be used. If this value is set to false, then +# Prison will not refresh this file and there could be issues with the display of other messages. +# If auto refresh is set to false, we are not held responsible for possible issues that can +# arise from inaccurate messages. If set to false, then you are responsible for maintaining +# the messages on your own. +# +# If you make changes to this file, and you have messages__auto_refresh=false, then those +# changes will be replaced when this file is updated. Since the old file is renamed, and +# not deleted, you can manually merge your changes back in to the new update. The old +# renamed files will never be deleted by prison; you can remove them when you feel like it +# is safe to do so. +# +# Please consider helping Prison, and everyone else who may use Prison, by contributing all +# translations to other languages. They should be faithful translations, and not something +# for the sake of humor or changes just for cosmetic styling. If you have something you would +# like to share, please contact a staff member on our Discord server. +#Thanks for your contributions! +# + +## +## Prison Supports Unicode (UTF-8) encoding in these properties files. BUt you must +## follow these instructions to ensure everything works properly. +## +## 1. You should only edit these files using a UTF-8 editor. On windows use NotePad, not WordPad. +## WordPad will save as plain text. To confirm the save was successful: save, close the editor, +## then reopen to confirm the encoding was preserved. +## +## 2. When running on Windows, you must enable utf-8 encoding in minecraft's console. Windows +## defaults to a characterpage 1252. To enable window's use of utf-8, you need to change the +## encoding prior to launching spigot/paper: +## chcp 65001 +## +## Full example of a windows script, which hooks for java debugging: +## rem Note: chcp 65001 enables utf-8 in windows, when normally windows uses characterpage 1252 +## chcp 65001 +## java -Dfile.encoding="UTF-8" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Xms1g -Xmx4g -jar spigot-1.8.8.jar nogui --log-strip-color +## pause +## +## 3. When viewing the logs/latest.log files you must use an editor such as NotePad instead of WordPad. +## +## 4. Unicode is properly displayed in game, in console, in the logs, and with paste.helpch.at when using +## /prison support submit. +## + +messages__version=1 +messages__auto_refresh=true + + +sellall_function__message=&7Message &dde exemplo + + + +sellall_spigot_utils__money_earned=&3Tu ganhas-te &a$%1 +sellall_spigot_utils__only_sellall_signs_are_enabled=&3Tu só podes vender por tabuletas. Comando desativado. +sellall_spigot_utils__rate_limit_exceeded=&3Vai devagar. Velocidade de uso excedido. +sellall_spigot_utils__shop_is_empty=&3Desculpa, esta loja de sellall está vazia. +sellall_spigot_utils__you_have_nothing_to_sell=&3Desculpa, não tens nada para vender. + +sellall_spigot_utils__sellall_is_disabled=&3Desculpa, sellall está desativado.. + diff --git a/prison-core/src/main/resources/lang/spigot/pt_PT.properties b/prison-core/src/main/resources/lang/spigot/pt_PT.properties new file mode 100644 index 000000000..3e8e5cf74 --- /dev/null +++ b/prison-core/src/main/resources/lang/spigot/pt_PT.properties @@ -0,0 +1,320 @@ +# NOTE: A messages__version is an arbitrary integer that will be manually incremented within Prison +# when there are changes to these messages. This value represents when message content is +# changed, fixed, or added to. This value may not be increased if the change is very small and +# insignificant, such as a space or a couple of letters. +# +# messages__auto_refresh=true indicates that this file will automatically be replaced if +# Prison detects a messages__version difference. The old file will be deleted (renamed) and +# a new copy will be placed in the directory to be used. If this value is set to false, then +# Prison will not refresh this file and there could be issues with the display of other messages. +# If auto refresh is set to false, we are not held responsible for possible issues that can +# arise from inaccurate messages. If set to false, then you are responsible for maintaining +# the messages on your own. +# +# If you make changes to this file, and you have messages__auto_refresh=false, then those +# changes will be replaced when this file is updated. Since the old file is renamed, and +# not deleted, you can manually merge your changes back in to the new update. The old +# renamed files will never be deleted by prison; you can remove them when you feel like it +# is safe to do so. +# +# Please consider helping Prison, and everyone else who may use Prison, by contributing all +# translations to other languages. They should be faithful translations, and not something +# for the sake of humor or changes just for cosmetic styling. If you have something you would +# like to share, please contact a staff member on our Discord server. +#Thanks for your contributions! +# + +## +## Prison Supports Unicode (UTF-8) encoding in these properties files. BUt you must +## follow these instructions to ensure everything works properly. +## +## 1. You should only editar these files using a UTF-8 editor. On windows use NotePad, not WordPad. +## WordPad will save as plain text. To confimar the save was successful: save, close the editor, +## then reopen to confimar the encoding was preserved. +## +## 2. When running on Windows, you must ativar utf-8 encoding in minecraft's console. Windows +## defaults to a characterpage 1252. To ativar window's use of utf-8, you need to change the +## encoding prior to launching spigot/paper: +## chcp 65001 +## +## Full example of a windows script, which hooks for java debugging: +## rem Note: chcp 65001 enables utf-8 in windows, when normally windows uses characterpage 1252 +## chcp 65001 +## java -Dfile.encoding="UTF-8" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Xms1g -Xmx4g -jar spigot-1.8.8.jar nogui --log-strip-color +## pause +## +## 3. When viewing the logs/latest.log files you must use an editor such as NotePad instead of WordPad. +## +## 4. Unicode is properly displayed in game, in console, in the logs, and with paste.helpch.at when using +## /prison support submit. +## + +messages__version=1 +messages__auto_refresh=true + +## Click to do something +spigot_gui_lore_click_to_add=Clica para adicionar. +spigot_gui_lore_click_to_add_backpack=Clica para adicionar a Backpack. +#spigot_gui_lore_click_to_cancel=Clica para cancelar. +#spigot_gui_lore_click_to_close=Clica para close. +#spigot_gui_lore_click_to_confirm=Clica para confimar. +#spigot_gui_lore_click_to_decrease=Clica para decrease. +#spigot_gui_lore_click_to_delete=Clica para eliminar. +#spigot_gui_lore_click_to_disable=Clica para desativar. +#spigot_gui_lore_click_to_edit=Clica para editar. +#spigot_gui_lore_click_to_enable=Clica para ativar. +#spigot_gui_lore_click_to_increase=Clica para increase. +spigot_gui_lore_click_to_manage_rank=Clica para gerir Rank. +#spigot_gui_lore_click_to_open=Clica para open. +spigot_gui_lore_click_to_rankup=Clica para Rankup. +spigot_gui_lore_click_to_rename=Clica para renomear. +spigot_gui_lore_click_to_select=Clica para selecionar. +spigot_gui_lore_click_to_start_block_setup=Clica para adicionar um bloco. +spigot_gui_lore_click_to_teleport=Clica para te teletransportar. +spigot_gui_lore_click_to_use=Clica para usar. + +## Left-Clica para do something. +#spigot_gui_lore_click_left_to_confirm=Click esquerdo para confimar. +#spigot_gui_lore_click_left_to_reset=Click esquerdo para resetar. +#spigot_gui_lore_click_left_to_open=Click esquerdo para open. +#spigot_gui_lore_click_left_to_edit=Click esquerdo para editar. + +## Right-Clica para do something. +#spigot_gui_lore_click_right_to_cancel=Click direito para cancelar. +#spigot_gui_lore_click_right_to_delete=Click direito para eliminar. +#spigot_gui_lore_click_right_to_disable=Click direito para desativar. +#spigot_gui_lore_click_right_to_enable=Click direito para ativar. +#spigot_gui_lore_click_right_to_toggle=Click direito para ativar/desativar. + +## Shift and Right-Clica para do something +#spigot_gui_lore_click_right_and_shift_to_delete=Shift e click direito para eliminar. +#spigot_gui_lore_click_right_and_shift_to_disable=Shift e click direito para desativar. +#spigot_gui_lore_click_right_and_shift_to_toggle=Shift e click direito para ativar/desativar. + +## Titles or data naming. +spigot_gui_lore_backpack_id=Backpack ID: +spigot_gui_lore_blocks=Blocos: +spigot_gui_lore_blocktype=Tipo de blocos: +spigot_gui_lore_chance=Probablidade: +spigot_gui_lore_command=Comando: +spigot_gui_lore_currency=Moeda: +#spigot_gui_lore_delay=Delay: +spigot_gui_lore_id=ID: +spigot_gui_lore_info=Info: +spigot_gui_lore_minename=Nome da Mina: +#spigot_gui_lore_multiplier=Multiplier: +spigot_gui_lore_name=Nome: +spigot_gui_lore_owner=Dono: +spigot_gui_lore_percentage=Percentagem: +#spigot_gui_lore_permission=Permission: +spigot_gui_lore_players_at_rank=Jogadores neste rank: +#spigot_gui_lore_prestige_name=Prestige name: +#spigot_gui_lore_price=Price: +spigot_gui_lore_radius=Raio: +spigot_gui_lore_rank_tag=Rank Tag: +spigot_gui_lore_reset_time=Reseta em: +spigot_gui_lore_size=Tamanho: +spigot_gui_lore_show_item=Mostrar tamanho: +spigot_gui_lore_spawnpoint=Spawnpoint: +spigot_gui_lore_volume=Volume: +#spigot_gui_lore_value=Value: +spigot_gui_lore_world=Mundo: + +## Simple actions or status. +spigot_gui_lore_disabled=Disativado. +spigot_gui_lore_enabled=Ativado. +spigot_gui_lore_locked=Bloqueado! +#spigot_gui_lore_next_page=Pagina seguinte. +#spigot_gui_lore_prior_page=Pagina anterior. +spigot_gui_lore_rankup=Rankup. +spigot_gui_lore_selected=Seleciona!. +spigot_gui_lore_unlocked=Desbloqueado! + +## Descriptions. +spigot_gui_lore_add_backpack_instruction_1=Por favor adicionar pelo menos um item +spigot_gui_lore_add_backpack_instruction_2=Se nao a backpack +spigot_gui_lore_add_backpack_instruction_3=Nao vai ser salva. +spigot_gui_lore_prestige_warning_1=Prestiges vao resetar: +spigot_gui_lore_prestige_warning_2=- Rank. +spigot_gui_lore_prestige_warning_3=- Saldo. +spigot_gui_lore_ranks_setup_1=Nao existem ranks! +spigot_gui_lore_ranks_setup_2=Se quiseres continuar a configuração. +spigot_gui_lore_ranks_setup_3=Todos os Ranks e Minas de A a Z vao ser feitos +spigot_gui_lore_ranks_setup_4=Com os &avalores &3default! +spigot_gui_lore_ranks_setup_5=Tambem podes usar: +spigot_gui_lore_ranks_setup_6=/ranks autoConfigure full ! +spigot_gui_lore_ranks_setup_7=Por favor, substitua o X pelo preço inicial e +spigot_gui_lore_ranks_setup_8=multiplicador, preço default = 50.000, multiplicador = 1,5. +spigot_gui_lore_sellall_delay_use_1=Pequeno delay antes de usar novamente. +spigot_gui_lore_sellall_delay_use_2=O &8comando &3/sellall sell &8. +spigot_gui_lore_set_mine_delay_instruction_1=Definir o atraso de uma mina +spigot_gui_lore_set_mine_delay_instruction_2=antes de resetar quando +spigot_gui_lore_set_mine_delay_instruction_3=atingir zero blocos. +spigot_gui_lore_show_item_description_1=Estes é o item +spigot_gui_lore_show_item_description_2=que aparece no Player GUI +spigot_gui_lore_show_item_description_3=or /mines GUI. +spigot_gui_lore_skip_reset_instruction_1=Saltar a reinicialização se +spigot_gui_lore_skip_reset_instruction_2=nao foram minerados +spigot_gui_lore_skip_reset_instruction_3=blocos suficientes. + + +## Button names or single line descriptions. +spigot_gui_lore_autofeatures_button_description=Gerir Recursos Automáticos. +spigot_gui_lore_backpacks_button_description=Gerir backpacks. +spigot_gui_lore_disable_notifications=desativar notificações. +spigot_gui_lore_enable_radius_mode=ativar modo raio. +spigot_gui_lore_enable_within_mode=ativar dentro do modo. +spigot_gui_lore_mines_button_description=Gerir Minas. +spigot_gui_lore_no_multipliers=[!] Não existe multiplicadores! +spigot_gui_lore_ranks_button_description=Gerenciador do Rank GUI. +spigot_gui_lore_rankup_if_enough_money=Se tiveres dinheiro suficiente. +spigot_gui_lore_sellall_button_description=Gerir SellAll. +spigot_gui_lore_sellall_edit_info=Editar SellAll Moeda. +spigot_gui_lore_tp_to_mine=Clica para teletransporte para Minas. + + +## Messages +spigot_message_missing_permission=Desculpa, não tens permissão para usar isso! +spigot_message_chat_event_time_end=Ficaste sem tempo, evento cancelado! +spigot_message_event_cancelled=Evento cancelado. +spigot_message_command_wrong_format=Desculpa, formato de comando errado. +spigot_message_console_error=Desculpa, precisas de ser um jogador para usar isso. + +## Mensagens de escada +spigot_message_ladder_default_empty=Desculpa, a escada padrão está vazia. + + +## Mine Messages +spigot_message_mines_disabled=Desculpa, as minas estão desativadas. +spigot_message_mines_name_chat_1=Por favor escreva o &6mineName &7que deseja usar e &6submit&7. +spigot_message_mines_name_chat_2=Digite &ccclose &7para cancelar ou espere &c30 segundos&7. +spigot_message_mines_name_chat_cancelled=Renomear o meu &cclosed&7, nada foi alterado! +spigot_message_mines_item_show_edit_success=Meu show item editado com sucesso. +spigot_message_mines_or_gui_disabled=Desculpa, Minas ou GUIs estão desativadas. + +## Mensagens de backpack +spigot_message_backpack_cant_own=Desculpa, você não pode possuir Backpack. +spigot_message_backpack_delete_error=Desculpa, não pode eliminar a Backpack. +spigot_message_backpack_delete_success=Backpack deletada com sucesso. +spigot_message_backpack_format_error=Desculpa, o formato do comando não está correto, talvez alguns argumentos estejam faltando. +spigot_message_backpack_limit_decrement_fail=O Limite da Backpack não pode ser negativo. +spigot_message_backpack_limit_edit_success=Limite de Backpack editado com sucesso. +spigot_message_backpack_limit_not_number=Desculpa, o Limite de Backpack não é um número. +spigot_message_backpack_limit_reached=Desculpa, você não pode ter mais Backpack. +spigot_message_backpack_missing_playername=Desculpa, adicione um nome de jogador válido. +spigot_message_backpack_resize_success=Se a Backpack existe, ela foi redimensionada com sucesso. +spigot_message_backpack_size_must_be_multiple_of_9=O tamanho da Backpack deve ser múltiplo de 9 e não exceder 64! + + +## Mensagens de Prestígio +spigot_message_prestiges_disabled=Desculpa, os Prestígios estão desabilitados. +spigot_message_prestiges_empty=Desculpa, não há Prestígios. +spigot_message_prestiges_or_gui_disabled=Desculpa, Prestiges ou GUIs estão desabilitados. +spigot_message_prestiges_confirm=aconfirm&7: Digite a palavra &aconfirm&7 para confimar. +spigot_message_prestiges_cancel=cancel&7: Digite a palavra &ccancel&7 para cancelar, &ctens 30 segundos. +spigot_message_prestiges_cancelled=Prestige cancelado. +spigot_message_prestiges_cancelled_wrong_keyword=Prestige &ccancelado&7, você não digitou a palavra: &aconfirm&7. + + +## Ranks Messages +spigot_message_ranks_disabled=Desculpa, Ranks estão desabilitados. +spigot_message_ranks_or_gui_disabled=Desculpa, Ranks ou GUIs estão desabilitados. +spigot_message_ranks_tag_chat_rename_1=Por favor escreva a &6tag &7que deseja usar e &6submetert&7. +spigot_message_ranks_tag_chat_rename_2=Digite &ccclose &7para cancelar ou aguarde &c30 segundos&7. +spigot_message_ranks_tag_chat_cancelled=Renomear tag &cclosed&7, nada foi alterado! + + +## SellAll Messages +spigot_message_sellall_auto_already_enabled=Venda automática já ativada. +spigot_message_sellall_auto_already_disabled=SellAll Auto já desativado. +spigot_message_sellall_auto_disabled=SellAll Auto desativado com sucesso. +spigot_message_sellall_auto_disabled_cant_use=Desculpa, você precisa ativar o AutoSell para usar isto. +spigot_message_sellall_auto_enabled=SellAll Auto ativada com sucesso. +spigot_message_sellall_auto_perusertoggleable_enabled=Venda automática ativada por utilizador ativada com sucesso. +spigot_message_sellall_auto_perusertoggleable_disabled=Venda automática ativada por utilizador desativada com sucesso. +spigot_message_sellall_auto_perusertoggleable_already_enabled=Venda automática ativada por utilizador já ativada. +spigot_message_sellall_auto_perusertoggleable_already_disabled=Venda automática ativada por utilizador já desativada. +spigot_message_sellall_boolean_input_invalid=O valor booleano não é válido (os valores válidos são True ou False). +spigot_message_sellall_cant_find_item_config=Desculpa, não consigo encontrar o teu item na configuração. +spigot_message_sellall_currency_chat_1=&3Iniciou a configuração da nova moeda para o SellAll! +spigot_message_sellall_currency_chat_2=Digite &ccancel &7para cancelar. +spigot_message_sellall_currency_chat_3=Digite &3default &7para definir a moeda padrão. +spigot_message_sellall_currency_chat_4=Digite o &aNome da moeda &7para definir a nova moeda. +spigot_message_sellall_currency_edit_success=SellAll Moeda editada com sucesso. +spigot_message_sellall_currency_not_found=Desculpa, moeda não encontrada. +spigot_message_sellall_hand_disabled=SellAll Hand desativado com sucesso. +spigot_message_sellall_hand_enabled=Vender Mão ativada com sucesso. +spigot_message_sellall_hand_is_disabled=SellAll Hand está desativada. +spigot_message_sellall_item_add_success=Item adicionado com sucesso. +spigot_message_sellall_item_already_added=Você já adicionou este item, por favor use o comando editar. +spigot_message_sellall_item_delete_success=Item excluído com sucesso. +spigot_message_sellall_item_edit_success=SellAll Item editado com sucesso. +spigot_message_sellall_item_id_not_found=Desculpa, nome/ID do item inválido. +spigot_message_sellall_item_missing_name=Por favor, adicione o argumento Nome/ID do Item. +spigot_message_sellall_item_missing_price=Por favor, adicione o argumento Valor do Item. +spigot_message_sellall_item_not_found=SellAll Item não encontrado na configuração. +spigot_message_sellall_default_values_success=SellAll Valores padrão definidos com sucesso. +spigot_message_sellall_delay_already_enabled=SellAll Delay já ativada. +spigot_message_sellall_delay_already_disabled=SellAll Delay já desativada. +spigot_message_sellall_delay_disabled=SellAll Delay desativado com sucesso. +spigot_message_sellall_delay_disabled_cant_use=Desculpa, por favor ative o SellAll Delay para usar isto. +spigot_message_sellall_delay_edit_success=Atraso de Vendas editado com sucesso. +spigot_message_sellall_delay_enabled=SellAll Delay ativada com sucesso. +spigot_message_sellall_delay_not_number=O número de atraso SellAll não é válido. +#spigot_message_sellall_delay_wait=O atraso de venda está ativado, por favor, diminua a velocidade. +spigot_message_sellall_gui_disabled=A GUI do SellAll está desativada. +#spigot_message_sellall_money_earned=Você ganhou &a$ +spigot_message_sellall_multiplier_add_success=Multiplicador SellAll adicionado com sucesso. +spigot_message_sellall_multiplier_are_disabled=Desculpa, os Multiplicadores SellAll estão desabilitados. +spigot_message_sellall_multiplier_cant_find=Desculpa, não foi possível encontrar o Multiplicador SellAll. +spigot_message_sellall_multiplier_delete_success=Multiplicador SellAll excluído com sucesso. +spigot_message_sellall_multiplier_disabled=Multiplicadores SellAll Desativados com sucesso. +spigot_message_sellall_multiplier_edit_success=Multiplicador SellAll editado com sucesso. +spigot_message_sellall_multiplier_enabled=Multiplicadores SellAll Ativados com sucesso. +#spigot_message_sellall_sell_empty=Desculpa, não há itens na loja SellAll. +#spigot_message_sellall_sell_nothing_sellable=Desculpa, mas você não tem nada para vender. +#spigot_message_sellall_sell_sign_only=Você pode usar SellAll Sell apenas com Signos. +spigot_message_sellall_sell_sign_notify=Você vendeu através de uma placa com sucesso. +spigot_message_sellall_trigger_already_disabled=SellAll Trigger já desativada. +spigot_message_sellall_trigger_already_enabled=SellAll Trigger já ativada. +spigot_message_sellall_trigger_disabled=SellAll Trigger desativado com sucesso. +spigot_message_sellall_trigger_enabled=SellAll Trigger ativada com sucesso. +spigot_message_sellall_trigger_is_disabled=Desculpa, SellAll Trigger está desativada. +spigot_message_sellall_trigger_item_add_success=SellAll Item Trigger adicionado com sucesso. +spigot_message_sellall_trigger_item_cant_find=SellAll Item Disparador não encontrado na configuração. +spigot_message_sellall_trigger_item_delete_success=SellAll Item Trigger excluído com sucesso. +spigot_message_sellall_trigger_item_missing=Por favor, adicione o Nome/ID do Item para o comando. + + +## GUI Messages +spigot_message_gui_backpack_disabled=Impossível abrir GUI, Backpack estão desabilitadas. +spigot_message_gui_backpack_empty=Desculpa, não há Backpack para mostrar. +spigot_message_gui_backpack_too_many=Desculpa, há muitas Backpack e a GUI não pode mostrá-las. +spigot_message_gui_close_success=GUI fechada com sucesso. +spigot_message_gui_error=Impossível abrir GUI, desativada ou erro. +spigot_message_gui_error_empty=Não é possível abrir a GUI, está vazia. +spigot_message_gui_ladder_empty=Desculpa, não há Ladders para show. +spigot_message_gui_ladder_too_many=Desculpa, existem muitas Ladders e a GUI não pode mostrá-las. +spigot_message_gui_mines_empty=Desculpa, não há Minas para show. +spigot_message_gui_mines_too_many=Desculpa, existem muitas minas para a GUI para show. +spigot_message_gui_prestiges_empty=Desculpa, não há Prestígios para mostrar. +spigot_message_gui_prestiges_too_many=Desculpa, há muitos Prestígios e a GUI não pode mostrá-los. +spigot_message_gui_ranks_empty=Desculpa, não há Ranks neste Ladder para show. +spigot_message_gui_ranks_rankup_commands_empty=Desculpa, não há Comandos de Rankup para mostrar. +spigot_message_gui_ranks_rankup_commands_too_many=Desculpa, existem muitos comandos de classificação e a GUI não pode mostrá-los. +spigot_message_gui_ranks_too_many=Desculpa, existem muitos Ranks e a GUI não pode mostrá-los. +spigot_message_gui_reload_success=GUIs recarregadas com sucesso! +#spigot_message_gui_sellall_disabled=Desculpa, SellAll está desativada. +spigot_message_gui_sellall_empty=Desculpa, não há nada para mostrar. +spigot_message_gui_too_high=Desculpa, mas o valor é muito alto (acima do máximo possível). +spigot_message_gui_too_low_value=Desculpa, mas o valor é muito baixo (abaixo do mínimo possível). + +spigot_blockbreak_mines__mine_is_being_reset__please_wait=Mina %1 está a ser resetada... por favor aguarde. + +spigot_blockbreak_core__validate_event__your_tool_is_worn_out=&cA tua ferramenta está gasta e não pode ser utilizada. + +spigot_auto_manager__inventory_is_full=&cAVISO! O teu inventário está cheio! +spigot_auto_manager__is_full_dropping_item__ignore__not_useds=&cAVISO! O teu inventário está cheio e estás deixar cair items! +spigot_auto_manager__inventory_is_full_losing_items=&cAVISO! O teu inventário está cheio e você estás a perder items! + + 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 62916e6c1..ca8897c18 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java +++ b/prison-core/src/test/java/tech/mcprison/prison/TestPlatform.java @@ -48,6 +48,7 @@ import tech.mcprison.prison.modules.ModuleElementType; import tech.mcprison.prison.output.ChatDisplay; import tech.mcprison.prison.placeholders.PlaceholderManager.PlaceholderFlags; +import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.placeholders.Placeholders; import tech.mcprison.prison.store.Storage; import tech.mcprison.prison.util.ChatColor; @@ -495,4 +496,14 @@ public void setActionBar( Player player, String actionBar ) public int compareServerVerisonTo( String comparisonVersion ) { return 0; } + + @Override + public void checkPlayerDefaultRank( RankPlayer rPlayer ) { + + } + + @Override + public void listAllMines(CommandSender sender, Player player) { + + } } 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 3e7e10a06..97a85d83f 100644 --- a/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java +++ b/prison-core/src/test/java/tech/mcprison/prison/TestPlayer.java @@ -26,6 +26,7 @@ import tech.mcprison.prison.cache.PlayerCache; import tech.mcprison.prison.cache.PlayerCachePlayerData; +import tech.mcprison.prison.file.JsonFileIO; import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.block.Block; @@ -52,6 +53,27 @@ public List getInput() { return "Testing"; } + @Override + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public String getPlayerFileName() { + + return JsonFileIO.getPlayerFileName( this ); + } + @Override public void updateInventory() { } 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 9e485c868..ecf00ca7d 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 @@ -21,6 +21,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.File; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -47,7 +49,7 @@ public class SelectionTest { @Before public void setUp() throws Exception { TestPlatform testPlatform = new TestPlatform(temporaryFolder.newFolder("test"), false); Prison.get() - .init(testPlatform, "1.12.X-test.1"); + .init(testPlatform, "1.12.X-test.1", new File("plugins/Prison")); } @Test public void testSelection() throws Exception { 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 d24ee33da..7faefd31b 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 @@ -29,7 +29,6 @@ import java.util.TreeMap; import tech.mcprison.prison.Prison; -import tech.mcprison.prison.PrisonCommand; import tech.mcprison.prison.chat.FancyMessage; import tech.mcprison.prison.commands.Arg; import tech.mcprison.prison.commands.Command; @@ -868,7 +867,7 @@ public void allMinesInfoDetails( StringBuilder sb ) { for ( Mine mine : mines ) { - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); JumboTextFont.makeJumboFontText( mine.getName(), sb ); sb.append( "\n" ); @@ -882,7 +881,7 @@ public void allMinesInfoDetails( StringBuilder sb ) { sb.append( chatDisplay.toStringBuilder() ); } - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); } @@ -1057,7 +1056,7 @@ else if ( !mineAccessByRank && tpAccessByRank ) { RowComponent row = new RowComponent(); double rtMinutes = resetTime / 60.0D; row.addTextComponent( "&3Reset time: &7%s &3Secs (&7%.2f &3Mins)", - Integer.toString(m.getResetTime()), rtMinutes ); + Integer.toString(resetTime), rtMinutes ); chatDisplay.addComponent( row ); } @@ -2884,14 +2883,16 @@ else if ( "enable".equalsIgnoreCase( mineSweeper ) && !m.isMineSweeperEnabled() @Command(identifier = "mines tp", description = "TP to the mine. Will default to the mine's " + - "spawn location if set, but can specify the target [spawn, mine]. OPs and console can " + + "spawn location if set, but can specify the target [spawn, mine]. Instead of a mine " + + "name, 'list' will show all mines you have access to. OPs and console can " + "TP other online players to a specified mine. Access for non-OPs can be setup through " + "'/mines set tpAccessByRank help` is preferred over permissions.", aliases = "mtp", altPermissions = {"access-by-rank", "mines.tp", "mines.tp.[mineName]"}) public void mineTp(CommandSender sender, @Arg(name = "mineName", def="", - description = "The name of the mine to teleport to.") String mineName, + description = "The name of the mine to teleport to, or 'list' to show all " + + "mines that you have access to.") String mineName, @Arg(name = "player", def = "", description = "Player name to TP - " + "Only console or rank command can include this parameter") String playerName, @@ -2902,6 +2903,28 @@ public void mineTp(CommandSender sender, ) { + if ( mineName != null && + "list".equals( mineName )) { + + Player player = getPlayer( sender, playerName ); + +// Player playerAlt = getPlayer( playerName ); +// +// if ( playerAlt != null ) { +// player = playerAlt; +// } + + if ( player == null ) { + Output.get().logInfo( "Mine TP List: You must either be a player, or refer to a valid player." ); + return; + } + + Prison.get().getPlatform().listAllMines( sender, player ); + + return; + } + + if ( mineName != null && ("spawn".equalsIgnoreCase( mineName ) || "mine".equalsIgnoreCase( mineName )) ) { target = mineName; @@ -3389,26 +3412,34 @@ private void generateBlockEventListing( Mine m, ChatDisplay display, boolean inc @Command(identifier = "mines blockEvent remove", description = "Removes a BlockEvent command from a mine.", onlyPlayers = false, permissions = "mines.set") public void blockEventRemove(CommandSender sender, - @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row) { + @Arg(name = "mineName" ) String mineName, + @Arg(name = "row", def = "0") Integer row) { + + if (!performCheckMineExists(sender, mineName)) { + return; + } + + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); + Mine m = pMines.getMine(mineName); if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + "Was row=[&b%d&7]", - (row == null ? "null" : row) )); + (row == null ? "null" : row) ); + + display.send(sender); + return; } - if (!performCheckMineExists(sender, mineName)) { - return; - } - - setLastMineReferenced(mineName); - - PrisonMines pMines = PrisonMines.getInstance(); - Mine m = pMines.getMine(mineName); - if (m.getBlockEvents() == null || m.getBlockEvents().size() == 0) { Output.get().sendInfo(sender, "The mine '%s' contains no BlockEvent commands.", m.getTag()); @@ -3448,7 +3479,7 @@ public void blockEventRemove(CommandSender sender, "For each block that is broke there will be a chance to run one of these commands. \n" + "To send messages use {msg}, {broadcast}, {actionBar}, or {title} followed by the formatted message. " + "Can use placeholders {player} and {player_uid}. Use ; between multiple commands. \n" + - "Example: &7\\Q'token give {player} 1;{msg} &7You got &31 &7token!;tpa a'\\E&3 \n" + + "Example: &7\\Q'token give {player} 1;{msg} &7You got &31 &7token!;mtp a'\\E&3 \n" + "This command defaults to no permission and 'sync' task mode; " + "see '/mines blockEvent' for more blockEvent options.", onlyPlayers = false, permissions = "mines.set", @@ -3616,20 +3647,55 @@ public void blockEventAdd(CommandSender sender, onlyPlayers = false, permissions = "mines.set") public void blockEventPercent(CommandSender sender, @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row, + @Arg(name = "row", def = "0") Integer row, @Arg(name = "percent", - description = "Percent chance between 0.0000 and 100.0") Double chance) { + description = "Percent chance between 0.0000 and 100.0", + def = "100") Double chance) { if (!performCheckMineExists(sender, mineName)) { return; } + + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + "Was row=[&b%d&7]", - (row == null ? "null" : row) )); - return; + (row == null ? "null" : row) ); + + + + String commandRoot = String.format( "" + + "/mines blockEvent percent %s", m.getName() ); + + display.addText( "&7Select a BlockEvent by row number to set the percentage" ); + + // try to "suggest" reading this command: + // mines blockEvent percent [row] [percent] + FancyMessage msgAddBlock = new FancyMessage( String.format( + "&7%s [row] [percent]", + commandRoot ) ) + .suggest( commandRoot + " [row] [percent]" ) + .tooltip("Change the percent for the blockEvent - Click to change"); + + RowComponent rowFancy = new RowComponent(); + rowFancy.addFancy( msgAddBlock ); + display.addComponent( rowFancy ); + + + display.send(sender); + + return; } if ( chance <= 0d || chance > 100.0d ) { @@ -3640,12 +3706,6 @@ public void blockEventPercent(CommandSender sender, return; } - setLastMineReferenced(mineName); - - PrisonMines pMines = PrisonMines.getInstance(); -// MineManager mMan = pMines.getMineManager(); - Mine m = pMines.getMine(mineName); - if ( row > m.getBlockEvents().size() ) { sender.sendMessage( String.format("&7Please provide a valid row number no greater than &b%d&7. " + @@ -3680,9 +3740,9 @@ public void blockEventPercent(CommandSender sender, onlyPlayers = false, permissions = "mines.set") public void blockEventPermission(CommandSender sender, @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row, + @Arg(name = "row", def = "0") Integer row, @Arg(name = "permission", def = "none", - description = "Optional permission that the player must have, or [none] for no perm." + description = "Optional permission that the player must have, or [none] for no perm." ) String perm ) { @@ -3691,12 +3751,48 @@ public void blockEventPermission(CommandSender sender, return; } + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + + if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + "Was row=[&b%d&7]", - (row == null ? "null" : row) )); + (row == null ? "null" : row) ); + + + + String commandRoot = String.format( "" + + "/mines blockEvent permission %s", m.getName() ); + + display.addText( "&7Select a BlockEvent by row number to set the permission" ); + + // try to "suggest" reading this command: + // mines blockEvent permission [row] [permission] + FancyMessage msgAddBlock = new FancyMessage( String.format( + "&7%s [row] [permission]", + commandRoot ) ) + .suggest( commandRoot + " [row] [permission]" ) + .tooltip("Change the permission for the blockEvent - Click to change"); + + RowComponent rowFancy = new RowComponent(); + rowFancy.addFancy( msgAddBlock ); + display.addComponent( rowFancy ); + + + + display.send(sender); + return; } @@ -3705,13 +3801,6 @@ public void blockEventPermission(CommandSender sender, perm = ""; } - - setLastMineReferenced(mineName); - - PrisonMines pMines = PrisonMines.getInstance(); -// MineManager mMan = pMines.getMineManager(); - Mine m = pMines.getMine(mineName); - if ( row > m.getBlockEvents().size() ) { sender.sendMessage( String.format("&7Please provide a valid row number no greater than &b%d&7. " + @@ -3751,7 +3840,7 @@ public void blockEventPermission(CommandSender sender, onlyPlayers = false, permissions = "mines.set") public void blockEventEventType(CommandSender sender, @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row, + @Arg(name = "row", def = "0") Integer row, @Arg(name = "eventType", def = "all", description = "EventType to trigger BlockEvent: " + "[all, blockBreak, PrisonExplosion, PEExplosive, " + @@ -3764,12 +3853,45 @@ public void blockEventEventType(CommandSender sender, return; } + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + "Was row=[&b%d&7]", - (row == null ? "null" : row) )); + (row == null ? "null" : row) ); + + + String commandRoot = String.format( "" + + "/mines blockEvent eventType %s", m.getName() ); + + display.addText( "&7Select a BlockEvent by row number to set the eventType" ); + + // try to "suggest" reading this command: + // mines blockEvent eventType [row] [eventType] + FancyMessage msgAddBlock = new FancyMessage( String.format( + "&7%s [row] [eventType]", + commandRoot ) ) + .suggest( commandRoot + " [row] [eventType]" ) + .tooltip("Change the eventtype for the blockEvent - Click to change"); + + RowComponent rowFancy = new RowComponent(); + rowFancy.addFancy( msgAddBlock ); + display.addComponent( rowFancy ); + + + + display.send(sender); + return; } @@ -3783,12 +3905,6 @@ public void blockEventEventType(CommandSender sender, eventType, BlockEventType.getPrimaryEventTypes() )); } - setLastMineReferenced(mineName); - - PrisonMines pMines = PrisonMines.getInstance(); -// MineManager mMan = pMines.getMineManager(); - Mine m = pMines.getMine(mineName); - if ( row > m.getBlockEvents().size() ) { sender.sendMessage( String.format("&7Please provide a valid row number no greater than &b%d&7. " + @@ -3841,7 +3957,7 @@ public void blockEventEventType(CommandSender sender, onlyPlayers = false, permissions = "mines.set") public void blockEventTriggered(CommandSender sender, @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row, + @Arg(name = "row", def = "0") Integer row, @Arg(name = "triggered", def = "none", description = "The enchantment that Triggered the explosion event. Use 'none' to " + "remove it. [none, ...]" @@ -3853,22 +3969,48 @@ public void blockEventTriggered(CommandSender sender, return; } - - if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + - "Was row=[&b%d&7]", - (row == null ? "null" : row) )); - return; - } - - setLastMineReferenced(mineName); PrisonMines pMines = PrisonMines.getInstance(); // MineManager mMan = pMines.getMineManager(); Mine m = pMines.getMine(mineName); + + if ( row == null || row <= 0 ) { + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + + "Was row=[&b%d&7]", + (row == null ? "null" : row) ); + + + String commandRoot = String.format( "" + + "/mines blockEvent triggered %s", m.getName() ); + + display.addText( "&7Select a BlockEvent by row number to set the triggered source" ); + + // try to "suggest" reading this command: + // mines blockEvent triggered [row] [triggered] + FancyMessage msgAddBlock = new FancyMessage( String.format( + "&7%s [row] [triggered]", + commandRoot ) ) + .suggest( commandRoot + " [row] [triggered]" ) + .tooltip("Change the triggered source for the blockEvent - Click to change"); + + RowComponent rowFancy = new RowComponent(); + rowFancy.addFancy( msgAddBlock ); + display.addComponent( rowFancy ); + + + + display.send(sender); + + return; + } if ( row > m.getBlockEvents().size() ) { @@ -3932,7 +4074,7 @@ public void blockEventTriggered(CommandSender sender, onlyPlayers = false, permissions = "mines.set") public void blockEventJobMode(CommandSender sender, @Arg(name = "mineName") String mineName, - @Arg(name = "row") Integer row, + @Arg(name = "row", def = "0") Integer row, @Arg(name = "taskMode", description = "Processing task mode to run the task: " + "[inline, inlinePlayer, sync, syncPlayer]", def = "inline") String mode @@ -3942,12 +4084,45 @@ public void blockEventJobMode(CommandSender sender, return; } + setLastMineReferenced(mineName); + + PrisonMines pMines = PrisonMines.getInstance(); +// MineManager mMan = pMines.getMineManager(); + Mine m = pMines.getMine(mineName); + if ( row == null || row <= 0 ) { - sender.sendMessage( - String.format("&7Please provide a valid row number greater than zero. " + + + ChatDisplay display = new ChatDisplay("BlockEvent Commands for " + m.getTag()); + display.addText("&8Hover over values for more information and clickable actions."); + generateBlockEventListing( m, display, true ); + + display.addText( + "&7Please provide a valid row number greater than zero. " + "Was row=[&b%d&7]", - (row == null ? "null" : row) )); + (row == null ? "null" : row) ); + + + String commandRoot = String.format( "" + + "/mines blockEvent taskMode % ", m.getName() ); + + display.addText( "&7Select a BlockEvent by row number to set the taskMode" ); + + // try to "suggest" reading this command: + // mines blockEvent taskMode [row] [taskMode] + FancyMessage msgAddBlock = new FancyMessage( String.format( + "&7%s [row] [taskMode]", + commandRoot ) ) + .suggest( commandRoot + " [row] [taskMode]" ) + .tooltip("Change the taskMode for the blockEvent - Click to change"); + + RowComponent rowFancy = new RowComponent(); + rowFancy.addFancy( msgAddBlock ); + display.addComponent( rowFancy ); + + + display.send(sender); + return; } @@ -3971,12 +4146,6 @@ public void blockEventJobMode(CommandSender sender, // } - setLastMineReferenced(mineName); - - PrisonMines pMines = PrisonMines.getInstance(); -// MineManager mMan = pMines.getMineManager(); - Mine m = pMines.getMine(mineName); - if ( row > m.getBlockEvents().size() ) { sender.sendMessage( String.format("&7Please provide a valid row number no greater than &b%d&7. " + @@ -4043,7 +4212,7 @@ public void blockEventBlockAdd(CommandSender sender, String commandRoot = String.format( "" + - "/mines blockEvent block add %s ", m.getName() ); + "/mines blockEvent block add %s", m.getName() ); /// if row is less than 1, then we need to display a list of BlockEvents: if ( rowBlockEvent == null || rowBlockEvent <= 0 ) { @@ -4058,7 +4227,7 @@ public void blockEventBlockAdd(CommandSender sender, display.addText( "&7Select a BlockEvent by row number to add a block" ); // try to "suggest" reading this command: - // mines blockEvent block add [row] [search} [block] + // mines blockEvent block add [row] [search} [block] FancyMessage msgAddBlock = new FancyMessage( String.format( "&7%s [rowBlockEvent] [rowBlockName]", commandRoot ) ) @@ -4202,7 +4371,7 @@ public void blockEventBlockRemove(CommandSender sender, String commandRoot = String.format( "" + - "/mines blockEvent block remove %s ", m.getName() ); + "/mines blockEvent block remove %s", m.getName() ); /// if row is less than 1, then we need to display a list of BlockEvents: @@ -4219,7 +4388,7 @@ public void blockEventBlockRemove(CommandSender sender, display.addText( "&7Select a BlockEvent by row number to remove a block" ); // try to "suggest" reading this command: - // mines blockEvent block add [row] [search} [block] + // mines blockEvent block add [row] [search} [block] FancyMessage msgRemoveBlock = new FancyMessage( String.format( "&7%s [rowBlockEvent] [rowBlockName]", commandRoot ) ) 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 index aaf927e13..249892b60 100644 --- 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 @@ -1053,6 +1053,11 @@ public void setHasSpawn( boolean hasSpawn ) { this.hasSpawn = hasSpawn; } + /* + *

This is the reset time for the mine, in seconds. + * A value of -1 means no timed resets. They will have to be done manually. + *

+ */ public int getResetTime() { return resetTime; } 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 index 693eca1ca..8ded47dfa 100644 --- 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 @@ -339,6 +339,9 @@ public void run() boolean forced = resetScheduleType == MineResetScheduleType.FORCED; + // If not a manual reset, and if the resetTime is -1, which means never reset + // based upon time, then exit and trigger the next action. This should never + // happen, since the task will never advance to this state, but just in case. if ( getResetTime() <= 0 && !forced ) { submitNextAction(); @@ -427,6 +430,8 @@ public void run() // } // + // this may be an issue for disabled resetTimes... may still need to be submitted? + // disabled resets may not need to be submitted at all. if ( getResetTime() > 0 ) { submitNextAction(); 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 index dd87e616b..c93683e37 100644 --- 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 @@ -8,6 +8,7 @@ import org.junit.Test; +@SuppressWarnings("deprecation") public class PrisonSortableMinesTest extends PrisonSortableMines { diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/FirstJoinHandler.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/FirstJoinHandler.java index 7d546d044..a84d4be9e 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/FirstJoinHandler.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/FirstJoinHandler.java @@ -21,7 +21,6 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.ranks.data.RankPlayer; -import tech.mcprison.prison.ranks.data.RankPlayerFactory; import tech.mcprison.prison.ranks.events.FirstJoinEvent; /** @@ -46,17 +45,15 @@ public FirstJoinHandler() { @Subscribe public void onFirstJoin(FirstJoinEvent event) { RankPlayer player = event.getPlayer(); + + PrisonRanks.getInstance().getPlayerManager().checkPlayerDefaultRank(player); - // Try to perform the first join processing to give them the default rank: - RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); - rankPlayerFactory.firstJoin( player ); +// // Try to perform the first join processing to give them the default rank: +// RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); +// rankPlayerFactory.firstJoin( player ); +// +// PrisonRanks.getInstance().getPlayerManager().savePlayer(player); - PrisonRanks.getInstance().getPlayerManager().savePlayer(player); -// try { -// } catch (IOException e) { -// -// Output.get().logError( firstJoinErrorCouldNotSavePlayer(), e ); -// } } } 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 732c3e872..9b585574a 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 @@ -198,12 +198,12 @@ public void enable() { } - // Hook up all players to the ranks: playerManager.connectPlayersToRanks( false ); Output.get().logInfo( "Ranks: Finished Connecting Players to Ranks." ); + // Load up the commands @@ -257,6 +257,12 @@ public void enable() { + // Start up the TopNPlayer's collections after all players have been loaded: + // NOTE: getting the instance of TopNPlayers must be done "after" player validation. + // So that thread needs to initiate it after done validating and fixing all players. +// TopNPlayers.getInstance(); + + // Check all players to see if any need to join: RanksStartupPlayerValidationsAsyncTask.submitTaskSync( this ); // checkAllPlayersForJoin(); @@ -272,7 +278,7 @@ public void checkAllPlayersForJoin() // If there is a default rank on the default ladder, then // check to see if there are any players not in prison: add them: - RankLadder defaultLadder = getLadderManager().getLadder( "default" ); + RankLadder defaultLadder = getLadderManager().getLadder( LadderManager.LADDER_DEFAULT ); if ( defaultLadder != null && defaultLadder.getRanks().size() > 0 ) { int addedPlayers = 0; int fixedPlayers = 0; @@ -295,6 +301,17 @@ public void checkAllPlayersForJoin() // If any player does not have a rank on the default ladder, then add the default // ladder and rank: Rank defaultRank = defaultLadder.getLowestRank().get(); + + if ( defaultRank == null ) { + Output.get().logInfo( + "PrisonRanks.checkAllPlayersForJoin: Warning: No default rank exists, so bypassing " + + "the player checks. There may be players online without a rank which could " + + "cause problems. Create a default rank and then restart the server to validate and " + + "repair all players."); + return; + } + + for ( RankPlayer rPlayer : playerManager.getPlayers() ) { @SuppressWarnings( "unused" ) @@ -372,8 +389,8 @@ private Collection initCollection(String collName) { * 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") == null ) { - RankLadder rankLadder = ladderManager.createLadder("default"); + if ( ladderManager.getLadder(LadderManager.LADDER_DEFAULT) == null ) { + RankLadder rankLadder = ladderManager.createLadder(LadderManager.LADDER_DEFAULT); if ( rankLadder == null ) { @@ -393,8 +410,8 @@ private void createDefaultLadder() { } } - if ( ladderManager.getLadder("prestiges") == null ) { - RankLadder rankLadder = ladderManager.createLadder("prestiges"); + if ( ladderManager.getLadder(LadderManager.LADDER_PRESTIGES) == null ) { + RankLadder rankLadder = ladderManager.createLadder(LadderManager.LADDER_PRESTIGES); if ( rankLadder == null ) { @@ -456,7 +473,11 @@ public PlayerManager getPlayerManager() { } public RankLadder getDefaultLadder() { - return getLadderManager().getLadder("default"); + return getLadderManager().getLadder(LadderManager.LADDER_DEFAULT); + } + + public RankLadder getPrestigesLadder() { + return getLadderManager().getLadder(LadderManager.LADDER_PRESTIGES); } public Database getDatabase() { @@ -482,11 +503,11 @@ private int getLadderRankCount( String ladderName ) { } public int getDefaultLadderRankCount() { - return getLadderRankCount( "default" ); + return getLadderRankCount( LadderManager.LADDER_DEFAULT ); } public int getPrestigesLadderRankCount() { - return getLadderRankCount( "prestiges" ); + return getLadderRankCount( LadderManager.LADDER_PRESTIGES ); } public int getladderCount() { 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 a692f3e43..edc8a3338 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 @@ -33,7 +33,9 @@ import tech.mcprison.prison.ranks.data.RankLadder; import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.ranks.data.RankPlayerFactory; +import tech.mcprison.prison.ranks.data.TopNPlayers; import tech.mcprison.prison.ranks.events.RankUpEvent; +import tech.mcprison.prison.ranks.managers.LadderManager; import tech.mcprison.prison.tasks.PrisonCommandTaskData; import tech.mcprison.prison.tasks.PrisonCommandTaskData.CustomPlaceholders; @@ -315,7 +317,7 @@ private RankupResults rankupPlayer(RankupCommands command, Player player, RankPl // If ladderName is null, then assign it the default ladder: if ( ladderName == null ) { - ladderName = "default"; + ladderName = LadderManager.LADDER_DEFAULT; results.addTransaction(RankupTransactions.assigned_default_ladder); } @@ -409,7 +411,7 @@ private void rankupPlayerInternal(RankupResults results, if ( command == RankupCommands.setrank && "-remove-".equalsIgnoreCase( rankName ) ) { results.addTransaction(RankupTransactions.attempting_to_delete_ladder_from_player); - if ("default".equalsIgnoreCase( ladderName ) ) { + if (LadderManager.LADDER_DEFAULT.equalsIgnoreCase( ladderName ) ) { results.addTransaction(RankupTransactions.cannot_delete_default_ladder); } else { @@ -443,16 +445,34 @@ private void rankupPlayerInternal(RankupResults results, // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank pRankNext = - originalRank == null ? null : - originalRank.getTargetPlayerRankForPlayer( rankPlayer, targetRank ); + + // Warning the following won't work if the player has no default rank since it will become a + // circular reference: +// PlayerRank pRankNext = rankPlayer.getNextPlayerRank(); + + PlayerRank pRankNext = null; + + if ( originalRank == null ) { + Rank nextRank = PrisonRanks.getInstance().getDefaultLadder().getLowestRank().orElse( null ); + pRankNext = rankPlayer.createPlayerRank(nextRank); + } + else { + + pRankNext = rankPlayer.calculateTargetPlayerRank( targetRank ); + } +// originalRank.getTargetPlayerRankForPlayer( rankPlayer, targetRank ); // new PlayerRank( targetRank, originalRank.getRankMultiplier() ); // If player does not have a rank on this ladder, then grab the first rank on the ladder since they need // to be added to the ladder. if ( pRankNext == null ) { - pRankNext = rankPlayerFactory.createPlayerRank( targetRank ); + results.addTransaction( RankupStatus.RANKUP_FAILURE_RANK_DOES_NOT_EXIST, + RankupTransactions.failed_rank_not_in_ladder ); + return; + + +// pRankNext = rankPlayerFactory.createPlayerRank( targetRank ); // pRankNext = originalRank.getTargetPlayerRankForPlayer( rankPlayer, ladder.getLowestRank().get() ); } @@ -463,7 +483,9 @@ private void rankupPlayerInternal(RankupResults results, // String currency = ""; - double nextRankCost = pRankNext.getRankCost(); + double nextRankCost = pRankNext == null || pRankNext.getRankCost() == null ? + 0.0d : pRankNext.getRankCost(); + double currentRankCost = ( results.getPlayerRankOriginal() == null ? 0 : results.getPlayerRankOriginal().getRankCost() ); @@ -700,6 +722,11 @@ else if ( pForceCharge == PromoteForceCharge.refund_player) { rankPlayer.recalculateRankMultipliers(); + // Sort the Top ranked list: +// rankPlayer.forcePlayerToRecalculateRankScore(); + TopNPlayers.getInstance().updatePlayerData(rankPlayer); + + // results.addTransaction( RankupTransactions.fireRankupEvent ); // // // Nothing can cancel a RankUpEvent: @@ -789,7 +816,7 @@ private Rank calculateTargetRank(RankupCommands command, RankupResults results, return targetRank; } - else if ("default".equalsIgnoreCase( results.getLadder().getName() ) && rankName == null ) { + else if (LadderManager.LADDER_DEFAULT.equalsIgnoreCase( results.getLadder().getName() ) && rankName == null ) { Optional lowestRank = results.getLadder().getLowestRank(); if ( lowestRank.isPresent() ) { targetRank = lowestRank.get(); @@ -986,7 +1013,8 @@ private void logTransactionResults( RankupResults results ) (oRank == null || oRank.getCurrency() == null ? "" : " " + oRank.getCurrency()), (tRank == null ? "none" : tRank.getName()), - (tpRank == null ? "" : " " + dFmt.format( tpRank.getRankCost())), + (tpRank == null || tpRank.getRankCost() == null ? + "" : " " + dFmt.format( tpRank.getRankCost())), (tRank == null || tRank.getCurrency() == null ? "" : " " + tRank.getCurrency()), iFmt.format( results.getElapsedTime() ), 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 index 4a6b703ff..f934bd2ab 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankupResults.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/RankupResults.java @@ -164,12 +164,14 @@ public PlayerRank getPlayerRankTarget() { getOriginalRank() != null && getOriginalRank().getRankNext() != null && targetRank != null ) { - RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); +// RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); - PlayerRank pRank = rankPlayerFactory.createPlayerRank( getOriginalRank() ); +// PlayerRank pRank = rankPlayerFactory.createPlayerRank( getOriginalRank() ); // This calculates the target rank, and takes in to consideration the player's existing rank: - playerRankTarget = pRank.getTargetPlayerRankForPlayer( rankPlayer, targetRank ); + playerRankTarget = rankPlayer.calculateTargetPlayerRank( targetRank ); + +// playerRankTarget = pRank.getTargetPlayerRankForPlayer( rankPlayer, targetRank ); // playerRankTarget = PlayerRank.getTargetPlayerRankForPlayer( rankPlayer, targetRank ); // PlayerRank pRank = rankPlayer.getRank( originalRank.getLadder() ); 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 f38402a54..2da228046 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 @@ -10,6 +10,7 @@ import tech.mcprison.prison.ranks.data.PlayerRankRefreshTask; import tech.mcprison.prison.ranks.data.Rank; import tech.mcprison.prison.ranks.data.RankLadder; +import tech.mcprison.prison.ranks.managers.LadderManager; /** * @author Faizaan A. Datoo @@ -67,12 +68,12 @@ public void ladderRemove(CommandSender sender, @Arg(name = "ladderName") String return; } - if (ladder.getName().equalsIgnoreCase( "default" )) { + if (ladder.getName().equalsIgnoreCase( LadderManager.LADDER_DEFAULT )) { ladderDeleteCannotDeleteDefaultMsg( sender ); return; } - if (ladder.getName().equalsIgnoreCase( "prestiges" )) { + if (ladder.getName().equalsIgnoreCase( LadderManager.LADDER_PRESTIGES )) { ladderDeleteCannotDeletePrestigesMsg( sender ); return; } @@ -94,11 +95,42 @@ public void ladderRemove(CommandSender sender, @Arg(name = "ladderName") String onlyPlayers = false, permissions = "ranks.ladder") public void ladderList(CommandSender sender) { ChatDisplay display = new ChatDisplay("Ladders"); + BulletedListComponent.BulletedListBuilder list = - new BulletedListComponent.BulletedListBuilder(); + new BulletedListComponent.BulletedListBuilder(); + +// DecimalFormat dFmt = new DecimalFormat( "#,##0.0000" ); + +// String header = String.format( +// "&d%-12s %16s %5s %12s %12s", +// "Ladder", +// "Rank Cost Mult", +// "Ranks", +// "First Rank", +// "Last Rank" +// ); + + list.add( PrisonRanks.getInstance().getLadderManager().printRankLadderInfoHeader() ); + for (RankLadder ladder : PrisonRanks.getInstance().getLadderManager().getLadders()) { - list.add(ladder.getName()); + +// int rankCount = ladder.getRanks() == null ? 0 : ladder.getRanks().size(); +// +// Rank firstRank = rankCount == 0 ? null : ladder.getRanks().get(0); +// Rank lastRank = rankCount == 0 ? null : ladder.getRanks().get( rankCount - 1 ); +// +// String ladderInfo = String.format( +// "&7%-12s %16s %4d %-12s %-12s", +// ladder.getName(), +// dFmt.format( ladder.getRankCostMultiplierPerRank() ), +// rankCount, +// firstRank.getName(), +// lastRank.getName() +// ); + + list.add( PrisonRanks.getInstance().getLadderManager().printRankLadderInfoDetail( ladder ) ); } + display.addComponent(list.build()); display.send(sender); @@ -345,5 +377,60 @@ public void ladderSetRankCostMultiplier( CommandSender sender, } } + + @Command( identifier = "ranks ladder applyRankCostMultiplier", + description = "Controls if the rank costs multiplier should apply to the" + + "ranks on this ladder. If the ladder has a rank cost multipiler " + + "enabled, this setting will not effect its contribution to other " + + "the multiplier.", + onlyPlayers = false, permissions = "ranks.ladder" ) + public void ladderApplyRankCostMultiplier( CommandSender sender, + @Arg( name = "ladderName" ) String ladderName, + @Arg( name = "applyRankCostMultiplier", def = "apply", + description = "Applies or disables the ranks on this ladder " + + "from applying the rank multiplier to the rank cost for players." + ) + String applyRankCostMultiplier ) + { + RankLadder ladder = PrisonRanks.getInstance().getLadderManager().getLadder( ladderName ); + + if ( ladder == null ) + { + ladderDoesNotExistsMsg( sender, ladderName ); + return; + } + + boolean applyRCM = applyRankCostMultiplier != null && + applyRankCostMultiplier.equalsIgnoreCase( "apply" ); + + boolean applyRCMOld = ladder.isApplyRankCostMultiplierToLadder(); + + + if ( applyRCMOld == applyRCM ) { + // No change: + + ladderApplyRankCostMultiplierNoChangeMsg( sender, ladderName, applyRCM ); + + return; + } + + ladder.setApplyRankCostMultiplierToLadder(applyRCM); + + + if ( PrisonRanks.getInstance().getLadderManager().save( ladder ) ) + { + ladderApplyRankCostMultiplierSavedMsg( sender, ladderName, + applyRCM, applyRCMOld ); + + // Recalculate the ladder's base rank cost multiplier: + PlayerRankRefreshTask rankRefreshTask = new PlayerRankRefreshTask(); + rankRefreshTask.submitAsyncTPSTask(); + } + else + { + ladderErrorSavingMsg( sender ); + } + } + } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommandsMessages.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommandsMessages.java index 0a1ca1aa6..c08c64ce6 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommandsMessages.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/LadderCommandsMessages.java @@ -216,4 +216,27 @@ protected void ladderSetRankCostMultiplierOutOfRangeMsg( CommandSender sender, .sendTo( sender ); } + protected void ladderApplyRankCostMultiplierNoChangeMsg( CommandSender sender, String ladderName, + boolean applyRCM ) { + + PrisonRanks.getInstance().getRanksMessages() + .getLocalizable( "ranks_LadderCommands__ladder_apply_rank_cost_multiplier_no_change" ) + .withReplacements( + ladderName, + applyRCM ? "apply" : "disabled" ) + .sendTo( sender ); + } + + protected void ladderApplyRankCostMultiplierSavedMsg( CommandSender sender, String ladderName, + boolean applyRCM, boolean applyRCMOld ) { + + PrisonRanks.getInstance().getRanksMessages() + .getLocalizable( "ranks_LadderCommands__ladder_apply_rank_cost_multiplier_saved" ) + .withReplacements( + ladderName, + applyRCM ? "apply" : "disabled", + applyRCMOld ? "apply" : "disabled" ) + .sendTo( sender ); + } + } 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 44d7f6b04..bfd5072b1 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 @@ -83,7 +83,7 @@ public void rankUpMax(CommandSender sender, List cmdTasks = new ArrayList<>(); - rankUpPrivate(sender, ladder, RankupModes.MAX_RANKS, "ranks.rankupmax.", cmdTasks ); + rankUpPrivate(sender, "", ladder, RankupModes.MAX_RANKS, "ranks.rankupmax.", cmdTasks ); // submit cmdTasks Player player = getPlayer( sender, null ); @@ -102,37 +102,47 @@ public void rankUpMax(CommandSender sender, @Command(identifier = "rankup", description = "Ranks up to the next rank.", permissions = "ranks.user", altPermissions = "ranks.rankup.[ladderName]", onlyPlayers = false) public void rankUp(CommandSender sender, - @Arg(name = "ladder", description = "The ladder to rank up on.", def = "default") String ladder + @Arg(name = "ladder", description = "The ladder to rank up on.", def = "default") String ladder, + @Arg(name = "playerName", description = "Provides the player's name for the rankup, but" + + "this can only be provided by a non-player such as console or ran from a script.", def = "") String playerName ) { - if ( !sender.isPlayer() ) { + if ( sender.isPlayer() ) { + playerName = ""; + } + + if ( !sender.isPlayer() && playerName.length() == 0 ) { Output.get().logInfo( rankupCannotRunFromConsoleMsg() ); return; } + Output.get().logDebug( DebugTarget.rankup, - "Rankup: cmd '/rankup %s' Processing ranks.rankup.%s", - ladder, ladder ); + "Rankup: cmd '/rankup %s%s' Processing ranks.rankup.%s", + ladder, + ( playerName.length() == 0 ? "" : " " + playerName ), + ladder + ); List cmdTasks = new ArrayList<>(); - rankUpPrivate(sender, ladder, RankupModes.ONE_RANK, "ranks.rankup.", cmdTasks ); + rankUpPrivate(sender, playerName, ladder, RankupModes.ONE_RANK, "ranks.rankup.", cmdTasks ); // submit cmdTasks - Player player = getPlayer( sender, null ); + Player player = getPlayer( sender, playerName ); submitCmdTasks( player, cmdTasks ); } - private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode, + private void rankUpPrivate(CommandSender sender, String playerName, String ladder, RankupModes mode, String permission, List cmdTasks ) { // RETRIEVE THE LADDER // This player has to have permission to rank up on this ladder. - if (!(ladder.equalsIgnoreCase("prestiges") && + if (!(ladder.equalsIgnoreCase(LadderManager.LADDER_PRESTIGES) && (Prison.get().getPlatform().getConfigBooleanFalse( "prestiges" ) || Prison.get().getPlatform().getConfigBooleanFalse( "prestige.enabled" ))) && - !ladder.equalsIgnoreCase("default") && + !ladder.equalsIgnoreCase(LadderManager.LADDER_DEFAULT) && !sender.hasPermission(permission + ladder.toLowerCase())) { Output.get().logDebug( DebugTarget.rankup, @@ -149,13 +159,24 @@ private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode return; } - // Player will always be the player since they have to be online and must be a player: - Player player = getPlayer( sender, null ); - if ( !sender.isPlayer() ) { + if ( sender.isPlayer() ) { + playerName = ""; + } + + if ( !sender.isPlayer() && playerName.length() == 0 ) { Output.get().logInfo( rankupCannotRunFromConsoleMsg() ); return; } + + + // Player will always be the player since they have to be online and must be a player: + Player player = getPlayer( sender, playerName ); + + if ( player == null ) { + rankupInvalidPlayerNameMsg( sender, playerName ); + return; + } //UUID playerUuid = player.getUUID(); @@ -191,12 +212,17 @@ private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode // default rank for the ladder to be their next rank. if ( playerRankCurrent == null ) { - playerRankTarget = rankPlayerFactory.createPlayerRank( + playerRankTarget = rankPlayer.calculateTargetPlayerRank( targetLadder.getLowestRank().get() ); + +// playerRankTarget = rankPlayerFactory.createPlayerRank( +// targetLadder.getLowestRank().get() ); } else { - playerRankTarget = playerRankCurrent.getTargetPlayerRankForPlayer( rankPlayer, - playerRankCurrent.getRank() ); + + playerRankTarget = rankPlayer.calculateTargetPlayerRank( playerRankCurrent.getRank() ); +// playerRankTarget = playerRankCurrent.getTargetPlayerRankForPlayer( rankPlayer, +// playerRankCurrent.getRank() ); } @@ -213,9 +239,9 @@ private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode boolean canPrestige = false; // If the player is trying to prestige, then the following must be ran to setup the prestige checks: - if (ladder.equalsIgnoreCase("prestiges")) { + if (ladder.equalsIgnoreCase(LadderManager.LADDER_PRESTIGES)) { - RankLadder rankLadder = lm.getLadder("default"); + RankLadder rankLadder = lm.getLadder(LadderManager.LADDER_DEFAULT); if ( rankLadder == null ){ rankupErrorNoDefaultLadderMsg( sender ); @@ -228,7 +254,7 @@ private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode } // gets the rank on the default ladder. Used if ladder is not default. - PlayerRank pRankDefaultLadder = rankPlayerFactory.getRank( rankPlayer, "default"); + PlayerRank pRankDefaultLadder = rankPlayerFactory.getRank( rankPlayer, LadderManager.LADDER_DEFAULT); if ( pRankDefaultLadder == null ) { rankupErrorPlayerNotOnDefaultLadder( sender, rankPlayer ); } @@ -262,8 +288,8 @@ private void rankUpPrivate(CommandSender sender, String ladder, RankupModes mode // If the last rankup attempt was successful and they are trying to rankup as many times as possible: if (results.getStatus() == RankupStatus.RANKUP_SUCCESS && mode == RankupModes.MAX_RANKS && - !ladder.equals("prestiges")) { - rankUpPrivate( sender, ladder, mode, permission, cmdTasks ); + !ladder.equals(LadderManager.LADDER_PRESTIGES)) { + rankUpPrivate( sender, playerName, ladder, mode, permission, cmdTasks ); } if (results.getStatus() == RankupStatus.RANKUP_SUCCESS){ rankupWithSuccess = true; @@ -331,7 +357,9 @@ private void prestigePlayer(CommandSender sender, Player player, RankPlayer rank // Set the player rank to the first one of the default ladder // Call the function directly and skip using dispatch commands: - setRank( sender, player.getName(), lm.getLadder("default").getLowestRank().get().getName(), "default" ); + setRank( sender, player.getName(), + lm.getLadder(LadderManager.LADDER_DEFAULT).getLowestRank().get().getName(), + LadderManager.LADDER_DEFAULT ); // PrisonAPI.dispatchCommand("ranks set rank " + player.getName() + " " + // lm.getLadder("default").getLowestRank().get().getName() + " default"); @@ -339,13 +367,13 @@ private void prestigePlayer(CommandSender sender, Player player, RankPlayer rank RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); - PlayerRank playerRankSecond = rankPlayerFactory.getRank( rankPlayer, "default"); + PlayerRank playerRankSecond = rankPlayerFactory.getRank( rankPlayer, LadderManager.LADDER_DEFAULT); if ( playerRankSecond != null ) { Rank pRankSecond = playerRankSecond.getRank(); // Check if the ranks match - if (pRankSecond != lm.getLadder("default").getLowestRank().get()) { + if (pRankSecond != lm.getLadder(LadderManager.LADDER_DEFAULT).getLowestRank().get()) { rankupNotAbleToResetRankMsg( sender ); success = false; @@ -585,6 +613,35 @@ public void setPlayerRank( RankPlayer rankPlayer, Rank pRank ) { } } + /** + * Added on 2022-07-04... called from: + * tech.mcprison.prison.ranks.data.RankPlayerFactory.firstJoin(RankPlayer) + * + * @param rankPlayer + * @param pRank + */ + public void setPlayerRankFirstJoin( RankPlayer rankPlayer, Rank pRank ) { + + if ( rankPlayer != null ) { + + List cmdTasks = new ArrayList<>(); + + RankupResults results = + new RankUtil().setRank(rankPlayer, rankPlayer, + pRank.getLadder().getName(), pRank.getName(), + rankPlayer.getName(), "FirstJoinEvent", + cmdTasks ); + + // submit cmdTasks + Player player = getPlayer( null, rankPlayer.getName() ); + submitCmdTasks( player, cmdTasks ); + + processResults( rankPlayer, rankPlayer.getName(), results, + pRank.getName(), pRank.getLadder().getName(), + pRank.getCurrency() ); + } + } + private void setPlayerRank( Player player, String rank, String ladderName, CommandSender sender ) { UUID playerUuid = player.getUUID(); diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommandMessages.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommandMessages.java index 217e7394c..f66e78bdb 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommandMessages.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RankUpCommandMessages.java @@ -37,6 +37,13 @@ protected String rankupCannotRunFromConsoleMsg() { .localize(); } + protected void rankupInvalidPlayerNameMsg(CommandSender sender, String playerName) { + PrisonRanks.getInstance().getRanksMessages() + .getLocalizable( "ranks_rankup__invalid_player_name" ) + .withReplacements( playerName ) + .sendTo(sender); + } + protected String rankupInternalFailureMsg() { return PrisonRanks.getInstance().getRanksMessages() .getLocalizable( "ranks_rankup__internal_failure" ) 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 1b09ac5b4..f3f3abe79 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 @@ -13,7 +13,6 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; -import tech.mcprison.prison.PrisonCommand; import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; import tech.mcprison.prison.autofeatures.AutoFeaturesWrapper; import tech.mcprison.prison.cache.PlayerCache; @@ -46,6 +45,7 @@ import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.ranks.data.RankPlayerFactory; import tech.mcprison.prison.ranks.data.RankPlayerName; +import tech.mcprison.prison.ranks.data.TopNPlayers; import tech.mcprison.prison.ranks.managers.LadderManager; import tech.mcprison.prison.ranks.managers.PlayerManager; import tech.mcprison.prison.ranks.managers.RankManager; @@ -406,7 +406,8 @@ public void autoConfigureRanks(CommandSender sender, boolean forceRank = force && PrisonRanks.getInstance().getRankManager().getRank( rankName ) != null; if ( forceRank || - createRank(sender, rankName, price, "default", tag, "noPlaceholderUpdate") ) { + createRank(sender, rankName, price, + LadderManager.LADDER_DEFAULT, tag, "noPlaceholderUpdate") ) { if ( forceRank ) { countRanksForced++; @@ -495,7 +496,7 @@ public void autoConfigureRanks(CommandSender sender, for ( int i = 0; i < 10; i++ ) { String name = "P" + (i + 1); String tag = "&5[&d+" + (i > 0 ? i + 1 : "" ) + "&5]"; - createRank(sender, name, (prestigeCost * (i + 1) ), "prestiges", tag, "noPlaceholderUpdate"); + createRank(sender, name, (prestigeCost * (i + 1) ), LadderManager.LADDER_PRESTIGES, tag, "noPlaceholderUpdate"); prestigesCount++; } @@ -516,7 +517,7 @@ public void autoConfigureRanks(CommandSender sender, // Set the prestiges ladder with a 10% base rank cost multiplier double rankCostMultiplier = 0.10; - RankLadder prestiges = PrisonRanks.getInstance().getLadderManager().getLadder( "prestiges" ); + RankLadder prestiges = PrisonRanks.getInstance().getLadderManager().getLadder( LadderManager.LADDER_PRESTIGES ); prestiges.setRankCostMultiplierPerRank( rankCostMultiplier ); PrisonRanks.getInstance().getLadderManager().save( prestiges ); @@ -793,7 +794,7 @@ public void listAllRanksByInfo( StringBuilder sb ) for ( Rank rank : ladderRanks ) { - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); JumboTextFont.makeJumboFontText( rank.getName(), sb ); sb.append( "\n" ); @@ -816,7 +817,7 @@ public void listAllRanksByInfo( StringBuilder sb ) for ( Rank rank : ranksExcluded ) { - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); JumboTextFont.makeJumboFontText( rank.getName(), sb ); sb.append( "\n" ); @@ -835,9 +836,16 @@ private ChatDisplay listRanksOnLadder( RankLadder ladder, boolean hasPerm, RankP String rankHeader = ranksListHeaderMsg( ladder.getName() ); ChatDisplay display = new ChatDisplay( rankHeader ); + display.addText( " " + PrisonRanks.getInstance().getLadderManager().printRankLadderInfoHeader() ); + display.addText( " " + PrisonRanks.getInstance().getLadderManager().printRankLadderInfoDetail(ladder) ); + display.addText( ranksListLadderCostMultiplierMsg( ladder.getRankCostMultiplierPerRank() ) ); + display.addText( ranksListLadderApplyRankCostMultiplierMsg( + ladder.isApplyRankCostMultiplierToLadder() )); + + if ( hasPerm ) { display.addText( ranksListClickToEditMsg() ); } @@ -874,7 +882,7 @@ private ChatDisplay listRanksOnLadder( RankLadder ladder, boolean hasPerm, RankP boolean first = true; for (Rank rank : ladder.getRanks()) { - boolean defaultRank = ("default".equalsIgnoreCase( ladder.getName() ) && first); + boolean defaultRank = (LadderManager.LADDER_DEFAULT.equalsIgnoreCase( ladder.getName() ) && first); // Since the formatting gets confused with color formatting, we must @@ -906,14 +914,19 @@ private ChatDisplay listRanksOnLadder( RankLadder ladder, boolean hasPerm, RankP rankCost = rank.getRawRankCost(); pRank = rankPlayerFactory.createPlayerRank( rank ); +// pRank = rankPlayerFactory.createPlayerRank( rank ); rMulti = pRank.getLadderBasedRankMultiplier(); } else { - pRank = rankPlayerFactory.createPlayerRank( rank ); - - pRank = pRank.getTargetPlayerRankForPlayer( pRank, rPlayer, rank ); + + pRank = rPlayer.calculateTargetPlayerRank( rank ); + +// pRank = rankPlayerFactory.createPlayerRank( rank ); +// +// +// pRank = pRank.getTargetPlayerRankForPlayer( pRank, rPlayer, rank ); rankCost = pRank.getRankCost(); rMulti = pRank.getRankMultiplier(); @@ -1045,7 +1058,8 @@ private ChatDisplay listRanksOnLadder( RankLadder ladder, boolean hasPerm, RankP Rank r = rPlayer.getLadderRanks().get( rLadder ).getRank(); - PlayerRank rpRank = rankPlayerFactory.createPlayerRank( r ); + PlayerRank rpRank = rPlayer.calculateTargetPlayerRank( r ); +// PlayerRank rpRank = rankPlayerFactory.createPlayerRank( r ); display.addText( "&3 BaseMult: &7%7s &3CurrMult: &7%7s &7%s &7%s ", fFmt.format( rLadder.getRankCostMultiplierPerRank() ), @@ -1127,7 +1141,7 @@ public void allRanksInfoDetails( StringBuilder sb ) { for ( Rank rank : rMan.getRanks() ) { - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); JumboTextFont.makeJumboFontText( rank.getName(), sb ); sb.append( "\n" ); @@ -1137,7 +1151,7 @@ public void allRanksInfoDetails( StringBuilder sb ) { sb.append( chatDisplay.toStringBuilder() ); } - PrisonCommand.printFooter( sb ); + Prison.get().getPrisonStatsUtil().printFooter( sb ); } @@ -1202,9 +1216,13 @@ private ChatDisplay rankInfoDetails( CommandSender sender, Rank rank, String opt // The following is the rank adjusted rank multiplier +// PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); +// RankPlayer rPlayer = pm.getPlayer(player.getUUID(), player.getName()); + RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); PlayerRank pRank = rankPlayerFactory.createPlayerRank( rank ); + double rankCostMultiplier = pRank.getLadderBasedRankMultiplier(); double ladderBaseMultiplier = rank.getLadder() == null ? 0 : rank.getLadder().getRankCostMultiplierPerRank(); @@ -1508,7 +1526,7 @@ public void rankPlayer(CommandSender sender, Set currencies = new LinkedHashSet<>(); LadderManager lm = PrisonRanks.getInstance().getLadderManager(); - for ( Rank rank : lm.getLadder( "default" ).getRanks() ) { + for ( Rank rank : lm.getLadder( LadderManager.LADDER_DEFAULT ).getRanks() ) { if ( rank.getCurrency() != null && !currencies.contains( rank.getCurrency() )) { currencies.add( rank.getCurrency() ); } @@ -1527,7 +1545,9 @@ public void rankPlayer(CommandSender sender, Rank nextRank = rank.getRankNext(); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); + + // PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = PlayerRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = nextRank == null ? null : @@ -2044,20 +2064,34 @@ public void rankTopN(CommandSender sender, description = "Page number [1]") String pageNumber, @Arg(name = "pageSize", def = "10", description = "Page size [10]") String pageSizeNumber, + @Wildcard(join=true) @Arg(name = "options", def = ".", - description = "Options: 'alt' displays a shorter format. [alt]") String options ){ + description = "Options: 'alt' displays a shorter format. " + + "'archived' shows all of the archvied players. " + + "'forceReload' forces the reloading of all players; must be OPd or console. " + + "[alt archived forceReload]") String options ){ int page = 1; int pageSize = 10; - - boolean alt = false; - if ( pageNumber.toLowerCase().contains("alt") || - pageSizeNumber.toLowerCase().contains("alt") || - options.toLowerCase().contains("alt") ) { - alt = true; + + if ( contains( "forceReload", pageNumber, pageSizeNumber, options ) ) { + + TopNPlayers.getInstance().forceReloadAllPlayers(); } + boolean alt = contains( "alt", pageNumber, pageSizeNumber, options ); +// if ( pageNumber.toLowerCase().contains("alt") || +// pageSizeNumber.toLowerCase().contains("alt") || +// options.toLowerCase().contains("alt") ) { +// alt = true; +// } + + // Since it's contains, "archive" will hit on archived, archives, etc... + boolean archived = contains( "archive", pageNumber, pageSizeNumber, options ); + +// boolean sort = contains( "sort", pageNumber, pageSizeNumber, options ); + try { page = Integer.parseInt(pageNumber); } @@ -2078,10 +2112,15 @@ public void rankTopN(CommandSender sender, pageSize = 10; } - int totalPlayers = PrisonRanks.getInstance().getPlayerManager().getPlayers().size(); + int totalPlayers = + archived ? + TopNPlayers.getInstance().getArchivedSize() : + TopNPlayers.getInstance().getTopNSize(); + +// int totalPlayers = PrisonRanks.getInstance().getPlayerManager().getPlayers().size(); int totalPages = (totalPlayers / pageSize) + (totalPlayers % pageSize == 0 ? 0 : 1); - if ( page > totalPages ) { + if ( page > 1 && totalPages > 1 && page > totalPages ) { page = totalPages; } @@ -2090,28 +2129,74 @@ public void rankTopN(CommandSender sender, // DecimalFormat dFmt = new DecimalFormat("#,##0.00"); - List topN = PrisonRanks.getInstance().getPlayerManager().getPlayersByTop(); + +// if ( sort ) { +// +// if ( sender.isOp() || !sender.isPlayer() ) { +// +//// PrisonRanks.getInstance().getPlayerManager().sortPlayerByTopRanked(); +//// PrisonRanks.getInstance().getPlayerManager().sortPlayerByTopRankedNoRankScoreUpdate(); +// sender.sendMessage( "&3Sorting has been submitted." ); +// } +// else { +// sender.sendMessage( "&3Only admins can force a sorting of the topn players." ); +// +// } +// +// } + + + + +// List topN = PrisonRanks.getInstance().getPlayerManager().getTopNPlayers().getTopNList(); String header = alt ? RankPlayer.printRankScoreLine2Header() : RankPlayer.printRankScoreLine1Header(); sender.sendMessage( header ); - for ( int i = posStart; i < posEnd && i < topN.size(); i++ ) { - RankPlayer rPlayer = topN.get(i); + for ( int i = posStart; i < posEnd; i++ ) { - String message = alt ? - rPlayer.printRankScoreLine2( i + 1 ) : - rPlayer.printRankScoreLine1( i + 1 ); + RankPlayer rPlayer = + archived ? + TopNPlayers.getInstance().getTopNRankArchivedPlayer( i ) : + TopNPlayers.getInstance().getTopNRankPlayer( i ); + + +// PrisonRanks.getInstance().getPlayerManager().getTopNRankPlayer( i ); - sender.sendMessage(message); + if ( rPlayer != null ) { + + String message = alt ? + rPlayer.printRankScoreLine2( i + 1 ) : + rPlayer.printRankScoreLine1( i + 1 ); + + sender.sendMessage(message); + } } - } + private boolean contains( String search, String... values ) { + boolean results = false; + + if ( search != null && values != null ) { + + search = search.trim().toLowerCase(); + + for (String val : values) { + if ( val.toLowerCase().contains(search) ) { + results = true; + break; + } + } + } + + return results; + } + // /** // * This function is just an arbitrary test to access the various components. diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommandsMessages.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommandsMessages.java index eca72d87b..4fbcc41a0 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommandsMessages.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/commands/RanksCommandsMessages.java @@ -254,6 +254,19 @@ protected String ranksListLadderCostMultiplierMsg( double multiplier ) { .localize(); } + protected String ranksListLadderApplyRankCostMultiplierMsg( boolean applyRankCostMultiplier ) { + + // Apply global Rank Cost Multipliers to this Rank? + + return PrisonRanks.getInstance().getRanksMessages() + .getLocalizable( "ranks_rankCommands__ranks_list_ladder_apply_ranks_cost_multplier" ) + .withReplacements( + Boolean.toString( applyRankCostMultiplier ) ) + .localize(); + } + + + protected String ranksListEditLadderCostMultiplierMsg() { return PrisonRanks.getInstance().getRanksMessages() 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 index 58c6b5f4c..cdd2dc265 100644 --- 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 @@ -6,6 +6,7 @@ import java.util.TreeSet; import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.managers.LadderManager; import tech.mcprison.prison.sorting.PrisonSorter; public class PrisonSortableLadders @@ -26,10 +27,10 @@ public int compare( RankLadder l1, RankLadder l2 ) { else if ( l2 == null ) { results = 1; } - else if ( "default".equalsIgnoreCase( l1.getName() ) ) { + else if ( LadderManager.LADDER_DEFAULT.equalsIgnoreCase( l1.getName() ) ) { results = -999999; } - else if ( "prestige".equalsIgnoreCase( l1.getName() )) { + else if ( LadderManager.LADDER_PRESTIGES.equalsIgnoreCase( l1.getName() )) { results = 999999; } else { diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadderFactory.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadderFactory.java index 370e82f2e..e3964f7cf 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadderFactory.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankLadderFactory.java @@ -114,7 +114,18 @@ else if ( rankPrison != null) { Double rankCostMultiplier = (Double) document.get( "rankCostMultiplierPerRank" ); rankLadder.setRankCostMultiplierPerRank( rankCostMultiplier == null ? 0 : rankCostMultiplier ); + Boolean applyRankCostMultiplierToLadder = (Boolean) document.get( "applyRankCostMultiplierToLadder" ); + if ( applyRankCostMultiplierToLadder != null ) { + + rankLadder.setApplyRankCostMultiplierToLadder( applyRankCostMultiplierToLadder ); + } + else { + rankLadder.setApplyRankCostMultiplierToLadder( true ); + isDirty = true; + } + + // getPermissions().clear(); // Object perms = document.get( "permissions" ); // if ( perms != null ) { diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerFactory.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerFactory.java index 4e5316303..96fb1163c 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerFactory.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerFactory.java @@ -12,7 +12,9 @@ import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.FirstJoinHandlerMessages; import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.commands.RankUpCommand; import tech.mcprison.prison.ranks.events.FirstJoinEvent; +import tech.mcprison.prison.ranks.managers.LadderManager; import tech.mcprison.prison.store.Document; import tech.mcprison.prison.util.ConversionUtil; @@ -103,7 +105,8 @@ public Document toDocument( RankPlayer rankPlayer ) { * the default ladder. If not, then it will add them. *

* - *

This is safe to run on anyone, even if they already are on the default ladder. + *

This is safe to run on anyone, even if they already are on the default ladder + * since it will skip processing for them. *

* *

Note, this will not save the player's new rank. The save function must be @@ -119,9 +122,15 @@ public void firstJoin( RankPlayer rankPlayer) { Optional firstRank = defaultLadder.getLowestRank(); if ( firstRank.isPresent() ) { - Rank rank = firstRank.get(); + Rank defaultRank = firstRank.get(); - rankPlayer.addRank( rank ); + + RankUpCommand rankupCommands = PrisonRanks.getInstance().getRankManager().getRankupCommands(); + + rankupCommands.setPlayerRankFirstJoin( rankPlayer, defaultRank ); + + +// rankPlayer.addRank( defaultRank ); Prison.get().getEventBus().post(new FirstJoinEvent( rankPlayer )); @@ -146,12 +155,12 @@ public void firstJoin( RankPlayer rankPlayer) { */ public boolean removeLadder( RankPlayer rankPlayer, String ladderName ) { boolean results = false; - if ( !ladderName.equalsIgnoreCase("default") ) { + if ( !ladderName.equalsIgnoreCase(LadderManager.LADDER_DEFAULT) ) { Integer id = rankPlayer.getRanksRefs().remove(ladderName); results = (id != null); RankLadder ladder = PrisonRanks.getInstance().getLadderManager().getLadder( ladderName ); - if ( ladder != null && !ladder.getName().equalsIgnoreCase( "default" ) ) { + if ( ladder != null && !ladder.getName().equalsIgnoreCase( LadderManager.LADDER_DEFAULT ) ) { rankPlayer.getLadderRanks().remove( ladder ); } } @@ -253,6 +262,15 @@ public PlayerRank getRank( RankPlayer rankPlayer, RankLadder ladder ) { // } + /** + *

This function is used when setting up a RankPlayer after loading from the + * file system. This takes the magic numbers that are used for ranks/ladders and + * finds the correct matches, which results in actual Rank objects. These ranks are + * then saved within the RankPlayer object for each player. + *

+ * + * @param rankPlayer + */ public void setupLadderRanks( RankPlayer rankPlayer ) { if ( rankPlayer.getLadderRanks().isEmpty() && !rankPlayer.getRanksRefs().isEmpty() ) { @@ -270,7 +288,8 @@ public void setupLadderRanks( RankPlayer rankPlayer ) { for ( Rank rank : ladder.getRanks() ) { if ( rank.getId() == rankId ) { - PlayerRank pRank = createPlayerRank( rank ); + PlayerRank pRank = rankPlayer.calculateTargetPlayerRank( rank ); +// PlayerRank pRank = createPlayerRank( rank ); rankPlayer.getLadderRanks().put( ladder, pRank ); break; @@ -279,7 +298,7 @@ public void setupLadderRanks( RankPlayer rankPlayer ) { } } - // Need to recalculate all rank multipliers: + // Need to recalculate all rank multipliers: This may be redundant. rankPlayer.recalculateRankMultipliers(); } @@ -307,6 +326,19 @@ public PlayerRank getRank( RankPlayer rankPlayer, String ladderName ) { } + /** + *

This function will create a PlayerRank without a player. This is to be + * used only with caution and where a player cannot be created, such as + * a command to list generic PlayerRanks. + *

+ * + *

This is only used in one location: + * tech.mcprison.prison.ranks.commands.RanksCommands.rankInfoDetails(CommandSender, Rank, String) + *

+ * + * @param rank + * @return + */ public PlayerRank createPlayerRank( Rank rank ) { PlayerRank results = new PlayerRank( rank ); @@ -317,37 +349,37 @@ public PlayerRank createPlayerRank( Rank rank ) { return results; } - private PlayerRank createPlayerRank( Rank rank, double rankMultiplier ) { - PlayerRank results = new PlayerRank( rank, rankMultiplier ); - - return results; - } +// private PlayerRank createPlayerRank( Rank rank, double rankMultiplier ) { +// PlayerRank results = new PlayerRank( rank, rankMultiplier ); +// +// return results; +// } public PlayerRank getTargetPlayerRankForPlayer( PlayerRank playerRank, RankPlayer player, Rank targetRank ) { - PlayerRank targetPlayerRank = null; + PlayerRank targetPlayerRank = player.calculateTargetPlayerRank( targetRank ); - if ( targetRank != null ) { - - double targetRankMultiplier = playerRank.getLadderBasedRankMultiplier( targetRank ); - - PlayerRank pRankForPLayer = getRank( player, targetRank.getLadder() ); - double existingRankMultiplier = pRankForPLayer == null ? 0 : - playerRank.getLadderBasedRankMultiplier( pRankForPLayer.getRank() ); - - // Get the player's total rankMultiplier from the default ladder - // because they will always have a rank there: - PlayerRank pRank = getRank( player, "default" ); - double playerMultipler = pRank == null ? 0 : pRank.getRankMultiplier(); - - // So the actual rank multiplier that needs to be used, is based upon the - // Player's current multiplier PLUS the multiplier for the target rank - // AND MINUS the multiplier for the current rank the player has within the - // target rank's ladder. - double rankMultiplier = playerMultipler + targetRankMultiplier - existingRankMultiplier; - - targetPlayerRank = createPlayerRank( targetRank, rankMultiplier ); - } +// if ( targetRank != null ) { +// +// double targetRankMultiplier = playerRank.getLadderBasedRankMultiplier( targetRank ); +// +// PlayerRank pRankForPLayer = getRank( player, targetRank.getLadder() ); +// double existingRankMultiplier = pRankForPLayer == null ? 0 : +// playerRank.getLadderBasedRankMultiplier( pRankForPLayer.getRank() ); +// +// // Get the player's total rankMultiplier from the default ladder +// // because they will always have a rank there: +// PlayerRank pRank = getRank( player, LadderManager.LADDER_DEFAULT ); +// double playerMultipler = pRank == null ? 0 : pRank.getRankMultiplier(); +// +// // So the actual rank multiplier that needs to be used, is based upon the +// // Player's current multiplier PLUS the multiplier for the target rank +// // AND MINUS the multiplier for the current rank the player has within the +// // target rank's ladder. +// double rankMultiplier = playerMultipler + targetRankMultiplier - existingRankMultiplier; +// +// targetPlayerRank = createPlayerRank( targetRank, rankMultiplier ); +// } return targetPlayerRank; } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerSortableLadderRankBalance.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerSortableLadderRankBalance.java index a0505eb78..8a5185a09 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerSortableLadderRankBalance.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/RankPlayerSortableLadderRankBalance.java @@ -165,7 +165,8 @@ private double calculateTopScore( RankPlayer rp1, Rank rank, double balance ) { // PlayerRank pRank = rp1.getRank( rank.getLadder() ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank pRankNext = pRank.getTargetPlayerRankForPlayer( rp1, nextRank ); + PlayerRank pRankNext = rp1.calculateTargetPlayerRank( nextRank ); +// PlayerRank pRankNext = pRank.getTargetPlayerRankForPlayer( rp1, nextRank ); // PlayerRank pRankNext = nextRank == null ? null : // new PlayerRank( nextRank, pRank.getRankMultiplier() ); diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/TopNPlayers.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/TopNPlayers.java new file mode 100644 index 000000000..e19114800 --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/TopNPlayers.java @@ -0,0 +1,591 @@ +package tech.mcprison.prison.ranks.data; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.TreeMap; +import java.util.UUID; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.file.FileIOData; +import tech.mcprison.prison.file.JsonFileIO; +import tech.mcprison.prison.internal.Player; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.tasks.TopNPlayerUpdateAsyncTask; + +public class TopNPlayers + implements FileIOData { + + private static TopNPlayers instance = null; + + public static final transient String PATH__TOP_N_PLAYERS = "data_storage"; + public static final transient String FILE_NAME__TOP_N_PLAYERS_JSON = "prisonTopN.json"; + + public static final transient long DELAY_THIRTY_SECONDS_TICKS = 20 * 30; + public static final transient long INTERVAL_FIVE_MINUTES_TICKS = 20 * 60 * 5; + + public static final long ONE_DAY_MS = 24 * 60 * 60 * 1000; // 1 day in ms + + public static final long ARCHIVE_CUTOFF_DAYS = 90; // 90 days + + private transient long archiveCutoffDaysMS; + + public transient File saveFile = null; + + private ArrayList topNList; + private transient TreeMap topNMap; + + private ArrayList archivedList; + private transient TreeMap archivedMap; + + private transient boolean calculatedRankScores = false; + + private transient boolean dirty = false; + + private TopNPlayers() { + super(); + + this.topNList = new ArrayList<>(); + this.topNMap = new TreeMap<>(); + + this.archivedList = new ArrayList<>(); + this.archivedMap = new TreeMap<>(); + + this.dirty = false; + + this.archiveCutoffDaysMS = + ONE_DAY_MS * Prison.get().getPlatform().getConfigLong( + "topNPlayers.archive.cutoff-days", ARCHIVE_CUTOFF_DAYS ); + +// launchTopNPlayerUpdateAsyncTask(); + + } + + public static TopNPlayers getInstance() { + + if ( instance == null ) { + synchronized ( TopNPlayers.class ) { + if ( instance == null ) { + instance = new TopNPlayers(); + + instance.loadSaveFile(); + + instance.launchTopNPlayerUpdateAsyncTask(); + } + } + } + + return instance; + } + + /** + *

The PlayerState will identify + * @author Blue + * + */ + public enum PlayerState { +// unknown, + offline, + online, + archived; + } + + public File getSaveFile() { + if ( this.saveFile == null ) { + + File directory = new File( Prison.get().getDataFolder(), PATH__TOP_N_PLAYERS ); + + this.saveFile = new File( directory, FILE_NAME__TOP_N_PLAYERS_JSON ); + } + return saveFile; + } + + private void launchTopNPlayerUpdateAsyncTask() { + + Long delayTicks = Prison.get().getPlatform().getConfigLong( + "topNPlayers.refresh.delay-ticks", DELAY_THIRTY_SECONDS_TICKS ); + Long intervalTicks = Prison.get().getPlatform().getConfigLong( + "topNPlayers.refresh.interval-ticks", INTERVAL_FIVE_MINUTES_TICKS ); + + + TopNPlayerUpdateAsyncTask.submitTaskTimerAsync( this, delayTicks, intervalTicks ); + } + + /** + *

Upon server startup, in an asynch thread, this function should be called + * to load the saved data from the file system. If there is no saved data, + * then this function will access the PlayerManager and build an initial + * collection from the existing players. + *

+ * + *

If any changes were made to any collections, or preexisting topN entries, + * then this class will be marked as dirty so this loader would know that + * it needs to update the save file. It will then save it changes. + *

+ */ + public void loadSaveFile() { + JsonFileIO jfio = new JsonFileIO(); + + TopNPlayers temp = (TopNPlayers) jfio.readJsonFile( getSaveFile(), this ); + + if ( temp != null && + (temp.getTopNList().size() > 0 || + temp.getArchivedList().size() > 0 )) { + + // Load from file was successful! + setTopNList( temp.getTopNList() ); + setTopNMap( temp.getTopNMap() ); + setArchivedList( temp.getArchivedList() ); + setArchivedMap( temp.getArchivedMap() ); + + // Since loading from a file, some players may now need to be archived: + checkArchives(); + } + else { + // load from file was not successful, probably because there is no file. + // So create a new collection of players from the PlayerManager: + List players = PrisonRanks.getInstance().getPlayerManager().getPlayers(); + + for (RankPlayer rankPlayer : players) { + + addPlayerData( rankPlayer ); + } + + // Do not need to check archives since the last seen date is processed + // when adding the player data. + } + + + // Sort: + sortTopN(); + + + if ( isDirty() ) { + saveToJson(); + } + } + + public void forceReloadAllPlayers() { + + getTopNList().clear(); + getTopNMap().clear(); + + getArchivedList().clear(); + getArchivedMap().clear(); + + + // load from file was not successful, probably because there is no file. + // So create a new collection of players from the PlayerManager: + List players = PrisonRanks.getInstance().getPlayerManager().getPlayers(); + + for (RankPlayer rankPlayer : players) { + + addPlayerData( rankPlayer ); + } + + this.dirty = true; + + // Sort: + sortTopN(); + + saveToJson(); + + } + + public void saveToJson() { + JsonFileIO jfio = new JsonFileIO(); + + jfio.saveJsonFile( getSaveFile(), this ); + } + + private void checkArchives() { + + ArrayList temp = new ArrayList<>(); + + long archiveDate = System.currentTimeMillis() - archiveCutoffDaysMS; + + // Locate the entries that need to be archived: + for ( TopNPlayersData topN : topNList ) { + if ( topN.getLastSeen() < archiveDate ) { + temp.add(topN); + } + } + + // Now move them to the archived state: + for (TopNPlayersData topN : temp) { + + // remove from list and map: + getTopNList().remove(topN); + getTopNMap().remove( topN.getKey() ); + + // Change the status: + topN.setPlayerState( PlayerState.archived ); + + getArchivedList().add(topN); + getArchivedMap().put( topN.getKey(), topN ); + } + + if ( temp.size() > 0 ) { + + setDirty( true ); + } + + } + + + /** + *

This adds the topN player data. This player may already be in the + * collection, so if they are, then this is treated more like an update. + *

+ * + * @param topN + */ + private void addPlayerData( TopNPlayersData topN, PlayerState activePlayerState ) { + + long archiveDate = System.currentTimeMillis() - archiveCutoffDaysMS; + + // First remove the player from all collections since it will be added back. + // Since the last seen date may have changed, it may be added to a different + // collection, hence why it needs to be first removed. + if ( getTopNMap().containsKey( topN.getKey() ) ) { + + TopNPlayersData temp = getTopNMap().remove( topN.getKey() ); + getTopNList().remove( temp ); + + setDirty( true ); + } + + // Remove the player from both the archive map and list: + if ( getArchivedMap().containsKey( topN.getKey() ) ) { + + TopNPlayersData temp = getArchivedMap().remove( topN.getKey() ); + getArchivedList().remove( temp ); + + setDirty( true ); + } + + + // If they were last seen past the archive date, then archive them: + if ( topN.getLastSeen() < archiveDate ) { + topN.setPlayerState( PlayerState.archived ); + + getArchivedList().add(topN); + getArchivedMap().put( topN.getKey(), topN ); + + setDirty( true ); + } + else { + + topN.setPlayerState( activePlayerState ); + + getTopNList().add(topN); + getTopNMap().put( topN.getKey(), topN ); + + setDirty( true ); + } + + } + + public void refreshAndSort() { + + if ( !calculatedRankScores ) { + + calculateAllRankScores( getTopNList() ); + calculateAllRankScores( getArchivedList() ); + + calculatedRankScores = true; + } + + // Get online players: + List onlinePlayer = Prison.get().getPlatform().getOnlinePlayers(); + + + + // Set all topNList entries to offline: + for ( TopNPlayersData topN : topNList ) { + if ( topN.getPlayerState() == PlayerState.online ) { + + topN.setPlayerState( PlayerState.offline ); + } + } + + // Apply online only to online players: + for (Player player : onlinePlayer) { + + RankPlayer rPlayer = PrisonRanks.getInstance().getPlayerManager().getPlayer(player); + + // Recalculate rankScore: + rPlayer.calculateRankScore(); + + TopNPlayersData topN = null; + + String key = player.getPlayerFileName(); + if ( getTopNMap().containsKey(key) ) { + topN = getTopNMap().get(key); + + // Set the RankPlayer object if it has not been set already: + if ( topN.getrPlayer() == null ) { + topN.setrPlayer( rPlayer ); + } + + topN.updateRankPlayer( rPlayer ); + } + if ( getArchivedMap().containsKey(key) ) { + // The player was archived. Remove them from the archive and add them back + // to the topN: + topN = getArchivedMap().get(key); + + // Set the RankPlayer object if it has not been set already: + if ( topN.getrPlayer() == null ) { + topN.setrPlayer( rPlayer ); + } + + topN.updateRankPlayer( rPlayer ); + + } + else { + // Player is online, but yet they are not in the topN: + topN = new TopNPlayersData( rPlayer ); + } + + // Set last seen date: + topN.setLastSeen( System.currentTimeMillis() ); + + addPlayerData( topN, PlayerState.online ); + + // Add player will always set the PlayerState to offline, so need to set it to + // online after addPlayerData() is called; +// topN.setPlayerState( PlayerState.online ); + + + setDirty( true ); + + } + + ArrayList newTopNList = new ArrayList<>(); + newTopNList.addAll( getTopNMap().values() ); + + ArrayList newArchivedList = new ArrayList<>(); + newArchivedList.addAll( getArchivedMap().values() ); + + + TopNPlayersData comparator = new TopNPlayersData(); + + Collections.sort( newTopNList, comparator ); + Collections.sort( newArchivedList, comparator ); + + setTopNList(newTopNList); + setArchivedList(newArchivedList); + +// // sort: +// sortTopN(); + + // If there has been any changes since the last save, then + // save it: + if ( isDirty() ) { + setDirty( false ); + saveToJson(); + } + } + + private void calculateAllRankScores( ArrayList topNList ) { + + for ( TopNPlayersData topN : topNList ) { + + RankPlayer rPlayer = topN.getrPlayer(); + + if ( rPlayer == null ) { + UUID nullUuid = null; + rPlayer = PrisonRanks.getInstance().getPlayerManager().getPlayer( nullUuid, topN.getName() ); + topN.setrPlayer(rPlayer); + } + + if ( rPlayer != null ) { + rPlayer.calculateRankScore(); + + // This will not update lastSeen: + topN.updateRankPlayer( rPlayer ); + } + + } + } + + /** + *

This sorts both the topNList and the archivedList. + *

+ */ + private void sortTopN() { + + TopNPlayersData comparator = new TopNPlayersData(); + + Collections.sort( getTopNList(), comparator ); + Collections.sort( getArchivedList(), comparator ); + } + + + /** + *

This function will add the RankPlayer data to the topN collections. + * This does NOT sort any of the results, so this is to be used to add a lot + * of entries, such as on server startup, or with updating with OnlinePlayers. + *

+ * + *

See updatePlayerData() if being used with a single player, such as with rankup. + *

+ * + * @param rPlayer + */ + public void addPlayerData( RankPlayer rPlayer ) { + + // Recalculate the rankScore for the player: + rPlayer.calculateRankScore(); + + TopNPlayersData topN = getTopNPlayer( rPlayer ); + + // Since this is used when the players are being loaded, assume all are offline for now. + // The recurring task of processing online players will set them to online. + addPlayerData( topN, PlayerState.offline ); + + } + + private TopNPlayersData getTopNPlayer(RankPlayer rPlayer) { + + TopNPlayersData topN = null; + + String key = rPlayer.getPlayerFileName(); + + if ( getTopNMap().containsKey( key ) ) { + + topN = getTopNMap().get( key ); + } + else if ( getArchivedMap().containsKey( key ) ) { + + topN = getArchivedMap().get( key ); + } + else { + + topN = new TopNPlayersData( rPlayer ); + } + + return topN; + } + + /** + *

This function will update, or add, a player's information within topN. and when + * The first thing this function does, is to calculate the rankScore for the RankPlayer. + * It will then add the player to the topN. When finished it will provide a + * simple sort of the topN results. This will allow the player + * who just ranked up to reflect their changes in topN without having to + * wait until the whole topN set is refreshed. The sorting used here will not update + * any of the other player's balances or status, so this has a low-cost sorting. + *

+ * + * @param rPlayer + */ + public void updatePlayerData( RankPlayer rPlayer ) { + + addPlayerData( rPlayer); + + sortTopN(); + } + + public int getTopNSize() { + return getTopNList().size(); + } + public int getArchivedSize() { + return getArchivedList().size(); + } + + public RankPlayer getTopNRankPlayer( int rankPosition ) { + return getTopNRankPlayer( rankPosition, false ); + } + + public RankPlayer getTopNRankArchivedPlayer( int rankPosition ) { + return getTopNRankPlayer( rankPosition, true ); + } + + /** + *

This function will return the RankPlayer that is at the given rankPosition + * within the topN collection. If rankPosition is out of range, then it will return + * a null value. + *

+ * + * @param rankPosition + * @return + */ + private RankPlayer getTopNRankPlayer( int rankPosition, boolean archived ) { + RankPlayer rPlayer = null; + + ArrayList tList = + archived ? + getArchivedList() : + getTopNList(); + + if ( rankPosition >= 0 && tList.size() > rankPosition ) { + + TopNPlayersData topN = tList.get( rankPosition ); + + rPlayer = topN.getrPlayer(); + + if ( rPlayer == null ) { + + UUID nullUuid = null; + rPlayer = PrisonRanks.getInstance().getPlayerManager() + .getPlayer( nullUuid, topN.getName() ); + } + + // The topN has the last extracted values, so copy them to the rPlayer if + // it has not been updated. This would be good for the archives. + if ( rPlayer != null && topN.getRankScore() != 0 && rPlayer.getRankScore() == 0 ) { + + rPlayer.setRankScore( topN.getRankScore() ); + rPlayer.setRankScorePenalty( topN.getRankScorePenalty() ); + + rPlayer.setRankScoreBalance( topN.getBalance() ); + rPlayer.setRankScoreCurrency( topN.getBalanceCurrency() ); + + } + + } + + + + return rPlayer; + } + + + public ArrayList getTopNList() { + return topNList; + } + public void setTopNList(ArrayList topNList) { + this.topNList = topNList; + } + + public TreeMap getTopNMap() { + return topNMap; + } + public void setTopNMap(TreeMap topNMap) { + this.topNMap = topNMap; + } + + public ArrayList getArchivedList() { + return archivedList; + } + public void setArchivedList(ArrayList archivedList) { + this.archivedList = archivedList; + } + + public TreeMap getArchivedMap() { + return archivedMap; + } + public void setArchivedMap(TreeMap archivedMap) { + this.archivedMap = archivedMap; + } + + 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/TopNPlayersData.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/TopNPlayersData.java new file mode 100644 index 000000000..10aa7d42e --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/data/TopNPlayersData.java @@ -0,0 +1,234 @@ +package tech.mcprison.prison.ranks.data; + +import java.text.SimpleDateFormat; +import java.util.Comparator; +import java.util.Date; + +import tech.mcprison.prison.ranks.data.TopNPlayers.PlayerState; + +/** + *

This class represent a player within the topN rankings. + * Since PlayerState has three values: + *

+ * + *

The way that the rank scores are calculated are rather complex, but this class + * tries to simplify the details. + *

+ * + *

The primary way topN is sorted is by **current** rank positions for both the + * default ladder and the prestiges ladder. For the prestiges ladder, if they do not have + * a current rank on that ladder, then they would have a rank-ladder position of zero. + *

+ * + *

+ * These rank position values become the first and second tier of sorting. + *

+ * + * + * @author Blue + * + */ +public class TopNPlayersData + implements Comparator, + Comparable +{ + + private String name; + private String playerFileName; + + private PlayerState playerState; + + // For report generation, not sorting: + private transient RankPlayer rPlayer; + + private long lastSeen; + private String lastSeenFormatted; + + private String balanceCurrency; + private double balance; + + private double rankScore; + private double rankScorePenalty; + + private int rankPositionDefault; + private int rankPositionPrestiges; + + private static transient SimpleDateFormat sdFmt = new SimpleDateFormat( "yyyy-MM-dd kk:mm:ss" ); + + public TopNPlayersData() { + super(); + + this.playerState = PlayerState.offline; + } + + public TopNPlayersData( RankPlayer rPlayer) { + this(); + + this.rPlayer = rPlayer; + + this.name = rPlayer.getName(); + this.playerFileName = rPlayer.getPlayerFileName(); + +// // Note: the nextPlayer rank could be in either the default ladder, +// // or the next prestige rank if at end of default ladder. +// PlayerRank nextRank = rPlayer.getNextPlayerRank(); + + + // This may be expensive getting lastSeen from the player's cache data: + setLastSeen( rPlayer.getPlayerCachePlayerData().getLastSeenDate() ); + +// this.lastSeenFormatted = sdFmt.format( new Date( this.lastSeen ) ); + + updateRankPlayer( rPlayer ); + + } + + public void updateRankPlayer( RankPlayer rPlayer ) { + + setRankScore( rPlayer.getRankScore() ); + setRankScorePenalty( rPlayer.getRankScorePenalty() ); + + setBalance( rPlayer.getRankScoreBalance() ); + setBalanceCurrency( rPlayer.getRankScoreCurrency() ); + + setRankPositionDefault( rPlayer.getRankPositonDefault() ); + setRankPositionPrestiges( rPlayer.getRankPositonPrestiges() ); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append( getPlayerState().name() ).append( " " ); + + sb.append( getName() ).append( " " ); + sb.append( getRankPositionPrestiges() ).append( " " ); + sb.append( getRankPositionDefault() ).append( " " ); + + sb.append( getRankScore() ).append( " " ); + sb.append( getLastSeenFormatted() ).append( " " ); + + return sb.toString(); + } + + @Override + public int compareTo(TopNPlayersData o) { + + int results = Integer.compare( o.getRankPositionPrestiges(), getRankPositionPrestiges() ); + + if ( results == 0 ) { + + results = Integer.compare( o.getRankPositionDefault(), getRankPositionDefault() ); + + if ( results == 0 ) { + results = Double.compare( o.getRankScore(), getRankScore() ); + + if ( results == 0 ) { + results = o.getName().compareToIgnoreCase( getName() ); + } + } + } + + return results; + } + + @Override + public int compare(TopNPlayersData o1, TopNPlayersData o2) { + + int results = o1.compareTo( o2 ); + return results; + } + + public String getKey() { + return getPlayerFileName(); + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getPlayerFileName() { + return playerFileName; + } + public void setPlayerFileName(String playerFileName) { + this.playerFileName = playerFileName; + } + + public PlayerState getPlayerState() { + return playerState; + } + public void setPlayerState(PlayerState playerState) { + this.playerState = playerState; + } + + public RankPlayer getrPlayer() { + return rPlayer; + } + public void setrPlayer(RankPlayer rPlayer) { + this.rPlayer = rPlayer; + } + + public long getLastSeen() { + return lastSeen; + } + public void setLastSeen(long lastSeen) { + + this.lastSeen = lastSeen; + + this.lastSeenFormatted = sdFmt.format( new Date( lastSeen) ); + } + + public String getLastSeenFormatted() { + return lastSeenFormatted; + } + public void setLastSeenFormatted(String lastSeenFormatted) { + this.lastSeenFormatted = lastSeenFormatted; + } + + public double getBalance() { + return balance; + } + public void setBalance(double balance) { + this.balance = balance; + } + + public String getBalanceCurrency() { + return balanceCurrency; + } + public void setBalanceCurrency(String balanceCurrency) { + this.balanceCurrency = balanceCurrency; + } + + public double getRankScore() { + return rankScore; + } + public void setRankScore(double rankScore) { + this.rankScore = rankScore; + } + + public double getRankScorePenalty() { + return rankScorePenalty; + } + public void setRankScorePenalty(double rankScorePenalty) { + this.rankScorePenalty = rankScorePenalty; + } + + public int getRankPositionDefault() { + return rankPositionDefault; + } + public void setRankPositionDefault(int rankPositionDefault) { + this.rankPositionDefault = rankPositionDefault; + } + + public int getRankPositionPrestiges() { + return rankPositionPrestiges; + } + public void setRankPositionPrestiges(int rankPositionPrestiges) { + this.rankPositionPrestiges = rankPositionPrestiges; + } + + +} 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 399452417..2e9d420f0 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 @@ -18,6 +18,7 @@ package tech.mcprison.prison.ranks.managers; import java.io.IOException; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -39,6 +40,9 @@ */ public class LadderManager extends LadderManagerMessages { + + public static final String LADDER_DEFAULT = "default"; + public static final String LADDER_PRESTIGES = "prestiges"; /* * Fields & Constants @@ -329,4 +333,40 @@ public RankLadder getLadder( Rank rank ) { return results; } + + public String printRankLadderInfoHeader() { + + String header = String.format( + "&d%-12s %16s %5s %-12s %-12s", + "Ladder", + "Rank Cost Mult", + "Ranks", + "First Rank", + "Last Rank" + ); + + return header; + } + + public String printRankLadderInfoDetail( RankLadder ladder ) { + + DecimalFormat dFmt = new DecimalFormat( "#,##0.0000" ); + + int rankCount = ladder.getRanks() == null ? 0 : ladder.getRanks().size(); + + Rank firstRank = rankCount == 0 ? null : ladder.getRanks().get(0); + Rank lastRank = rankCount == 0 ? null : ladder.getRanks().get( rankCount - 1 ); + + String ladderInfo = String.format( + "&7%-12s %16s %5d %-12s %-12s", + ladder.getName(), + dFmt.format( ladder.getRankCostMultiplierPerRank() ), + rankCount, + (firstRank == null ? "" : firstRank.getName()), + (lastRank == null ? "" : lastRank.getName()) + ); + + return ladderInfo; + } + } 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 4ee6189a9..e8d0e4c6e 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 @@ -20,7 +20,6 @@ import java.io.IOException; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -71,9 +70,6 @@ public class PlayerManager private List players; private TreeMap playersByName; - private RankPlayerSortOrderTopRanked sorterTopN; - private List playersByTop; - private List translatedPlaceHolderKeys; @@ -87,11 +83,9 @@ public PlayerManager(Collection collection) { this.players = new ArrayList<>(); this.playersByName = new TreeMap<>(); - this.sorterTopN = new RankPlayerSortOrderTopRanked(); - this.playersByTop= new ArrayList<>(); - this.playerErrors = new HashSet<>(); - + + Prison.get().getEventBus().register(this); } @@ -149,25 +143,17 @@ public void loadPlayers() throws IOException { } - playersByTop.add( rankPlayer ); - } - // NOTE: The following is very expensive operation if the players balance - // needs to be retrieved: - // sortPlayerByTopRanked(); - // players.forEach( // document -> // this.players.add( // rankPlayerFactory.createRankPlayer(document) )); + } - public void sortPlayerByTopRanked() { - - Collections.sort( playersByTop, sorterTopN ); - } + /** * Saves a {@link RankPlayer} to disk. @@ -225,6 +211,26 @@ public void savePlayers() throws IOException { } } + /** + *

If the player does not have a default rank, then assign it to them and + * then save their new settings. + *

+ * + * @param rPlayer + */ + public void checkPlayerDefaultRank( RankPlayer rPlayer ) { + + if ( rPlayer.getPlayerRankDefault() == null ) { + + // Try to perform the first join processing to give them the default rank: + RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); + rankPlayerFactory.firstJoin( rPlayer ); + + PrisonRanks.getInstance().getPlayerManager().savePlayer( rPlayer ); + + } + } + /** *

This function will add all the players to all of the ranks they * are associated with. @@ -253,9 +259,9 @@ public TreeMap getPlayersByName() { return playersByName; } - public List getPlayersByTop() { - return playersByTop; - } +// public List getPlayersByTop() { +// return playersByTop; +// } public Set getPlayerErrors() { return playerErrors; @@ -582,7 +588,7 @@ public String getPlayerNextRankCost( RankPlayer rankPlayer, String ladderName, if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank nextRank = pRank.getRank().getRankNext(); @@ -593,7 +599,8 @@ public String getPlayerNextRankCost( RankPlayer rankPlayer, String ladderName, nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); //PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -635,7 +642,7 @@ private Rank getNextPrestigeRank( RankPlayer rankPlayer, boolean isDefault, Rank isDefault && Prison.get().getPlatform().getConfigBooleanFalse( "prestige.enabled" ) ) { - RankLadder rLadder = PrisonRanks.getInstance().getLadderManager().getLadder( "prestiges" ); + RankLadder rLadder = PrisonRanks.getInstance().getLadderManager().getLadder( LadderManager.LADDER_PRESTIGES ); if ( rLadder != null ) { @@ -668,7 +675,7 @@ public String getPlayerNextRankCostPercent( RankPlayer rankPlayer, String ladder if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank nextRank = pRank.getRank().getRankNext(); @@ -679,7 +686,8 @@ public String getPlayerNextRankCostPercent( RankPlayer rankPlayer, String ladder nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -723,7 +731,7 @@ public String getPlayerNextRankCostBar( RankPlayer rankPlayer, String ladderName if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank rank = pRank.getRank(); @@ -735,7 +743,8 @@ public String getPlayerNextRankCostBar( RankPlayer rankPlayer, String ladderName nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -788,7 +797,7 @@ public String getPlayerNextRankCostRemaining( RankPlayer rankPlayer, String ladd if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank rank = pRank.getRank(); @@ -800,7 +809,8 @@ public String getPlayerNextRankCostRemaining( RankPlayer rankPlayer, String ladd nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -857,7 +867,7 @@ public String getPlayerNextRankCostRemainingPercent( RankPlayer rankPlayer, Stri if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank rank = pRank.getRank(); @@ -869,7 +879,8 @@ public String getPlayerNextRankCostRemainingPercent( RankPlayer rankPlayer, Stri nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -920,7 +931,7 @@ public String getPlayerNextRankCostRemainingBar( RankPlayer rankPlayer, String l if ( ladderName == null || ladderName != null && ladder.getName().equalsIgnoreCase( ladderName )) { - boolean isDefault = ladder.getName().equals( "default" ) ; + boolean isDefault = ladder.getName().equals( LadderManager.LADDER_DEFAULT ) ; PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, ladder ); Rank rank = pRank.getRank(); @@ -933,7 +944,8 @@ public String getPlayerNextRankCostRemainingBar( RankPlayer rankPlayer, String l nextRank = getNextPrestigeRank( rankPlayer, isDefault, nextRank ); // This calculates the target rank, and takes in to consideration the player's existing rank: - PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); + PlayerRank nextPRank = rankPlayer.calculateTargetPlayerRank( nextRank ); +// PlayerRank nextPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); // PlayerRank nextPRank = new PlayerRank( nextRank, pRank.getRankMultiplier() ); @@ -1234,6 +1246,112 @@ public String getPlayerNextRankName( RankPlayer rankPlayer, String ladderName ) return sb.toString(); } + + public String getPlayerNextLinkedRankTag( RankPlayer rankPlayer, String ladderName ) { + StringBuilder sb = new StringBuilder(); + + // Must always have a default rank: + PlayerRank pRankDefault = rankPlayer.getPlayerRankDefault(); + + // Prestiges ladder may not be enabled, or this may be null because they have not yet prestiged: + PlayerRank pRankPrestiges = rankPlayer.getPlayerRankPrestiges(); + + if ( ladderName == null || ladderName.equalsIgnoreCase( LadderManager.LADDER_PRESTIGES ) ) { + + + // If default rank is the last rank, or at the last prestiges rank, + // then get next prestige rank for player: + if ( pRankDefault.getRank().getRankNext() == null ) { + + // If the player does not have a prestiges rank, then get the first one: + if ( pRankPrestiges == null && PrisonRanks.getInstance().getPrestigesLadder() != null ) { + + Rank firstPrestigeRank = PrisonRanks.getInstance().getPrestigesLadder().getLowestRank().orElse(null); + if ( firstPrestigeRank != null ) { + sb.append( firstPrestigeRank.getTag() ); + } + } + // Else if player has a prestige rank, and there is a next prestige rank, get it's tag: + else if ( pRankPrestiges != null && pRankPrestiges.getRank().getRankNext() != null ) { + sb.append( pRankPrestiges.getRank().getRankNext().getTag() ); + + } + + // else, if player has a prestige rank, and it's the last one, then just get that tag: + else if ( pRankPrestiges != null ) { + sb.append( pRankPrestiges.getRank().getTag() ); + + } + + } + // else just get current prestige rank for player: + else if ( pRankPrestiges != null ) { + + sb.append( pRankPrestiges.getRank().getTag() ); + } + } + if ( ladderName == null || ladderName.equalsIgnoreCase( LadderManager.LADDER_DEFAULT ) ) { + + boolean showFirstRank = false; + boolean showNextRank = true; + + + // If at last default rank, then get the default ladder's first rank's tag or just show current tag: + if ( pRankDefault.getRank().getRankNext() == null ) { + + // Since the current default rank is the last, cannot show next default rank: + showNextRank = false; + + Rank firstPrestigeRank = PrisonRanks.getInstance().getPrestigesLadder().getLowestRank().orElse(null); + + // If firstPrestigeRank is null, then prestiges is not enabled, so cannot show first rank: + if ( firstPrestigeRank == null ) { + showFirstRank = false; +// showNextRank = false; + } + + // If current presetiges is null, then use the first prestiges rank: + else if ( pRankPrestiges == null ) { + showFirstRank = true; +// showNextRank = false; + + } + else if ( pRankPrestiges.getRank().getRankNext() == null ) { + // At the last presetiges rank... so do not reset the default ranks: + showFirstRank = false; + } + + else { + // Presetige is possible, so show first default rank: + showFirstRank = true; + + } + + } + + + if ( !showFirstRank && !showNextRank ) { + // Show current rank: + + sb.append( pRankDefault.getRank().getTag() ); + } + else if ( showFirstRank ) { + + Rank firstDefaultRank = PrisonRanks.getInstance().getDefaultLadder().getLowestRank().orElse(null); + sb.append( firstDefaultRank.getTag() ); + } + else { + // Show next rank: + + // Not at last default rank, so get next rank tag: + sb.append( pRankDefault.getRank().getRankNext().getTag() ); + } + + } + + return sb.toString(); + } + public String getPlayerNextRankTag( RankPlayer rankPlayer, String ladderName ) { StringBuilder sb = new StringBuilder(); @@ -1292,7 +1410,7 @@ public String getPlayerNextRankTag( RankPlayer rankPlayer, String ladderName ) { // from the language file to display in the place of the empty tag. // The idea is that if prestiges is enabled, then this is a way to // indicate the player could prestige as the next step. - if ( sb.length() == 0 && "default".equalsIgnoreCase( ladderName ) ) { + if ( sb.length() == 0 && LadderManager.LADDER_DEFAULT.equalsIgnoreCase( ladderName ) ) { String replacementText = lastRankMessageForDefaultLadder(); if ( replacementText != null && !replacementText.trim().isEmpty() ) { @@ -1458,6 +1576,13 @@ public String getTranslatePlayerPlaceHolder( PlaceholderIdentifier identifier ) results = getPlayerNextRankTag( rankPlayer, ladderName ); break; + case prison_rlrt: + case prison_rankup_linked_rank_tag: + case prison_rlrt_laddername: + case prison_rankup_linked_rank_tag_laddername: + results = getPlayerNextLinkedRankTag( rankPlayer, ladderName ); + break; + case prison_pb: case prison_player_balance: case prison_pb_laddername: @@ -1837,6 +1962,15 @@ public List getTranslatedPlaceHolderKeys() { List ladders = PrisonRanks.getInstance().getLadderManager().getLadders(); for ( RankLadder ladder : ladders ) { for ( PrisonPlaceHolders ph : placeHolders ) { + + if ( ph.hasFlag( PlaceholderFlags.ONLY_DEFAULT_OR_PRESTIGES ) && + !ladder.getName().equalsIgnoreCase( LadderManager.LADDER_DEFAULT ) && + !ladder.getName().equalsIgnoreCase( LadderManager.LADDER_PRESTIGES ) + ) { + // Placeholder is invalid for ladders that are not default or prestiges, so skip: + continue; + } + String key = ph.name().replace( PlaceholderManager.PRISON_PLACEHOLDER_LADDERNAME_SUFFIX, "_" + ladder.getName() ). toLowerCase(); @@ -1876,4 +2010,6 @@ public void reloadPlaceholders() { // 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 3bd6f03ba..71db7695a 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 @@ -54,6 +54,7 @@ import tech.mcprison.prison.ranks.data.RankPlayer; import tech.mcprison.prison.ranks.data.RankPlayerFactory; import tech.mcprison.prison.ranks.data.StatsRankPlayerBalanceData; +import tech.mcprison.prison.ranks.data.TopNPlayers; import tech.mcprison.prison.store.Collection; import tech.mcprison.prison.store.Document; @@ -843,26 +844,26 @@ public String getTranslateRanksPlaceHolder( PlaceholderIdentifier identifier ) { - default: - - identifier.setFoundAMatch( false ); - - Output.get().logInfo( - "RankManager TranslateRanksPlaceHolder: Warning: a placeholder '%s' (%s) was selected " + - "to be processed in this manager, but the placeholder is not included in the swich. " + - "Please report to support team.", - identifier.getPlaceholderKey().getPlaceholder().name(), - PlaceholderFlags.STATSPLAYERS.name() ); - - break; - } - } - - else if ( placeHolder.hasFlag( PlaceholderFlags.STATSPLAYERS ) ) { - - identifier.setFoundAMatch( true ); - - switch ( placeHolder ) { +// default: +// +// identifier.setFoundAMatch( false ); +// +// Output.get().logInfo( +// "RankManager TranslateRanksPlaceHolder: Warning: a placeholder '%s' (%s) was selected " + +// "to be processed in this manager, but the placeholder is not included in the swich. " + +// "Please report to support team. (1)", +// identifier.getPlaceholderKey().getPlaceholder().name(), +// PlaceholderFlags.STATSPLAYERS.name() ); +// +// break; +// } +// } +// +// else if ( placeHolder.hasFlag( PlaceholderFlags.STATSPLAYERS ) ) { +// +// identifier.setFoundAMatch( true ); +// +// switch ( placeHolder ) { case prison_top_player_line1_headers__tp: case prison_tpl1h__tp: @@ -1081,34 +1082,81 @@ else if ( placeHolder == PrisonPlaceHolders.prison_top_player_penalty_raw_nnn_tp break; - - - default: - - identifier.setFoundAMatch( false ); - - break; - } - - if ( attributeText != null ) { - results = attributeText.format( results ); + case prison_top_rank_balance_name_nnn_rankname: + case prison_trbn_nnn_rankname: + { + StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); + if ( stats != null ) { + + results = stats.getPlayer() == null ? "" : stats.getPlayer().getName(); + } + else { + results = ""; + } + } - Output.get().logInfo( - "RankManager TranslateRanksPlaceHolder: Warning: a placeholder '%s' (%s) was selected " + - "to be processed in this manager, but the placeholder is not included in the swich. " + - "Please report to support team.", - identifier.getPlaceholderKey().getPlaceholder().name(), - PlaceholderFlags.STATSPLAYERS.name() ); - } - } - - else if ( rank != null ) { - - identifier.setFoundAMatch( true ); - - switch ( placeHolder ) { + break; + + case prison_top_rank_balance_score_nnn_rankname: + case prison_trbs_nnn_rankname: + { + StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); + if ( stats != null ) { + + results = dFmt.format( stats.getScore()); + } + else { + results = ""; + } + } + break; + + case prison_top_rank_balance_balance_nnn_rankname: + case prison_trbb_nnn_rankname: + { + StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); + if ( stats != null ) { + + results = stats.getPlayer() == null ? "" : + dFmt.format( stats.getPlayer().getBalance( rank.getCurrency()) ); + } + else { + results = ""; + } + } + + break; + + + +// default: +// +// identifier.setFoundAMatch( false ); +// +// break; +// } +// +// if ( attributeText != null ) { +// +// results = attributeText.format( results ); +// +// Output.get().logInfo( +// "x` Warning: a placeholder '%s' (%s) was selected " + +// "to be processed in this manager, but the placeholder is not included in the swich. " + +// "Please report to support team. (2)", +// identifier.getPlaceholderKey().getPlaceholder().name(), +// PlaceholderFlags.STATSPLAYERS.name() ); +// } +// } +// +// else if ( rank != null ) { +// +// identifier.setFoundAMatch( true ); +// +// switch ( placeHolder ) { +// case prison_rank__name_rankname: case prison_r_n_rankname: @@ -1143,8 +1191,10 @@ else if ( rank != null ) { case prison_rank__cost_multiplier_rankname: case prison_r_cm_rankname: - RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); - PlayerRank pRank = rankPlayerFactory.createPlayerRank( rank ); + PlayerRank pRank = rankPlayer.calculateTargetPlayerRank( rank ); + +// RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); +// PlayerRank pRank = rankPlayerFactory.createPlayerRank( rank ); results = Double.toString( pRank.getLadderBasedRankMultiplier() ); break; @@ -1169,63 +1219,34 @@ else if ( rank != null ) { case prison_rank__linked_mines_rankname: case prison_r_lm_rankname: - StringBuilder sb = new StringBuilder(); - for ( ModuleElement mine : rank.getMines() ) { - if ( sb.length() > 0 ) { - sb.append( ", " ); - } - sb.append( mine.getName() ); - } - - results = sb.toString(); - break; - - - case prison_top_rank_balance_name_nnn_rankname: - case prison_trbn_nnn_rankname: { - StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); - if ( stats != null ) { - - results = stats.getPlayer() == null ? "" : stats.getPlayer().getName(); - } - else { - results = ""; + StringBuilder sb = new StringBuilder(); + for ( ModuleElement mine : rank.getMines() ) { + if ( sb.length() > 0 ) { + sb.append( ", " ); + } + sb.append( mine.getName() ); } + + results = sb.toString(); } - break; - case prison_top_rank_balance_score_nnn_rankname: - case prison_trbs_nnn_rankname: + case prison_rank__linked_mines_tag_rankname: + case prison_r_lmt_rankname: { - StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); - if ( stats != null ) { - - results = dFmt.format( stats.getScore()); - } - else { - results = ""; + StringBuilder sb = new StringBuilder(); + for ( ModuleElement mine : rank.getMines() ) { + if ( sb.length() > 0 ) { + sb.append( ", " ); + } + sb.append( mine.getTag() ); } + + results = sb.toString(); } - break; - case prison_top_rank_balance_balance_nnn_rankname: - case prison_trbb_nnn_rankname: - { - StatsRankPlayerBalanceData stats = rank.getStatsPlayerBlance().getTopStats( 1 ); - if ( stats != null ) { - - results = stats.getPlayer() == null ? "" : - dFmt.format( stats.getPlayer().getBalance( rank.getCurrency()) ); - } - else { - results = ""; - } - } - - break; default: @@ -1235,7 +1256,7 @@ else if ( rank != null ) { Output.get().logInfo( "RankManager TranslateRanksPlaceHolder: Warning: a placeholder '%s' was selected " + "to be processed in this manager, but the placeholder is not included in the swich. " + - "Please report to support team.", + "Please report to support team. (3)", identifier.getPlaceholderKey().getPlaceholder().name() ); @@ -1258,61 +1279,202 @@ else if ( rank != null ) { private RankPlayer getTopNRankPlayer( int rankPosition ) { - RankPlayer topRankPlayer = null; - PlayerManager pm = PrisonRanks.getInstance().getPlayerManager(); - if ( rankPosition >= 0 && rankPosition < pm.getPlayersByTop().size() ) { - - topRankPlayer = pm.getPlayersByTop().get(rankPosition); - } - - return topRankPlayer; + return TopNPlayers.getInstance().getTopNRankPlayer( rankPosition ); } - private double calculateRankCost( RankPlayer rankPlayer, Rank rank ) + private double calculateRankCost( RankPlayer rankPlayer, Rank targetRank ) { double cost = 0; // Get player's rank: RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); - PlayerRank playerRank = rankPlayerFactory.getRank( rankPlayer, rank.getLadder() ); - if ( playerRank != null ) { + Rank rankDefault = rankPlayer.getPlayerRankDefault().getRank(); + + Rank rankPrestige = null; + PlayerRank prPrestige = rankPlayer.getPlayerRankPrestiges(); + if ( prPrestige != null ) { + rankPrestige = prPrestige.getRank(); + } + + // Only continue with the processing if the target rank is a higher rank than + // either the current default rank or the current prestige rank. If it's not, + // then the player has already bought that rank so their cost will be zero: + if ( rankDefault.getLadder().equals( targetRank.getLadder()) && + rankDefault.getPosition() < targetRank.getPosition() || + + // They have not yet prestiged: + rankPrestige == null || + + // Trying to calculate a prestige rank higher than their current prestige rank: + rankPrestige.getLadder().equals( targetRank.getLadder()) && + rankPrestige.getPosition() < targetRank.getPosition() + ) { + + ArrayList ranksList = getAllRanks( rankPlayer, rankDefault, rankPrestige, targetRank ); - // If the player is at a higher rank, or the same rank, then the cost will be - // zero for the rank that is being passed in, since the player has - // already paid for that rank. - if ( rank.getPosition() <= playerRank.getRank().getPosition() ) { - cost = 0; - } - else { - //cost = playerRank.getRankCost(); - Rank nextRank = playerRank.getRank(); + for (Rank rank : ranksList) { - while ( nextRank != null && - nextRank.getPosition() <= rank.getPosition() ) { - - // Need to calculate the next PlayerRank value for the next rank: - - // This calculates the target rank, and takes in to consideration the player's existing rank: - playerRank = playerRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); - - -// playerRank = rankPlayerFactory.createPlayerRank( nextRank ); -// playerRank = new PlayerRank(nextRank); + PlayerRank pR = rankPlayerFactory.getTargetPlayerRankForPlayer( + rankPlayer.getPlayerRankDefault(), rankPlayer, rank ); + + if ( pR != null ) { - cost += playerRank.getRankCost(); - nextRank = nextRank.getRankNext(); + cost += pR.getRankCost(); } } + } + +// PlayerRank playerRank = rankPlayerFactory.getRank( rankPlayer, targetRank.getLadder() ); + + +// if ( playerRank != null ) { +// +// +//// List +// +// +// // If the player is at a higher rank, or the same rank, then the cost will be +// // zero for the rank that is being passed in, since the player has +// // already paid for that rank. +// if ( rank.getPosition() <= playerRank.getRank().getPosition() ) { +// cost = 0; +// } +// else { +// //cost = playerRank.getRankCost(); +// Rank nextRank = playerRank.getRank(); +// +// while ( nextRank != null && +// nextRank.getPosition() <= targetRank.getPosition() ) { +// +// // Need to calculate the next PlayerRank value for the next rank: +// +// // This calculates the target rank, and takes in to consideration the player's existing rank: +// playerRank = playerRank.getTargetPlayerRankForPlayer( rankPlayer, nextRank ); +// +// +//// playerRank = rankPlayerFactory.createPlayerRank( nextRank ); +//// playerRank = new PlayerRank(nextRank); +// +// cost += playerRank.getRankCost(); +// nextRank = nextRank.getRankNext(); +// } +// } +// } return cost; } + + private ArrayList getAllRanks( RankPlayer rPlayer, + Rank rankDefault, Rank rankPrestige, Rank targetRank ) { + + ArrayList totalRanks = new ArrayList<>(); + + // rankDefault is the current default rank for the player. So if rankDefault + // is the same as the targetRank, then exit because they already paid for the + // current rank, so there is no need to calculate anything else. + if ( !rankDefault.equals( targetRank ) ) { + + // We cannot add the current default rank to the totalRanks, so get the next + // rank then continue. From here on out, all ranks have to be added to the + // the totalRanks including the targetRank. + Rank nextRank = rankDefault.getRankNext(); + + findNextRank( nextRank, rankPrestige, targetRank, totalRanks); + } + + return totalRanks; + } + + + /** + *

This function will take the default rank (result parameter) and the rankPrestige + * and use them to talk the ladders to find a match for the targetRank. + *

+ * + *

The result Rank that is returned from this function is only useful for + * internal use since it will prevent the clearing of the totalRanks list + * when coming out of recursion. When outside of this function, then real + * value is within totalRanks. + *

+ * + * @param result + * @param rankPrestige + * @param targetRank + * @param totalRanks + * @return + */ + private Rank findNextRank( Rank result, Rank rankPrestige, Rank targetRank, ArrayList totalRanks) { + + // Search for the targetRank in the rest of the default ranks: + result = findNextRanksOnDefaultLadder( result, targetRank, totalRanks ); + + // if result is null, then not found on the default ladder, so then + // we must jump to the next prestige rank: + if ( result == null ) { + rankPrestige = rankPrestige == null ? + PrisonRanks.getInstance().getLadderManager() + .getLadderPrestiges().getLowestRank().orElse(null) : + rankPrestige.getRankNext(); + result = rankPrestige; + + if ( result != null ) { + + // Need to add the result rank to the totalRanks: + totalRanks.add( result ); + + if ( !result.equals( targetRank ) ) { + + // Have not found it... Set result to rank A and start over searching: + result = PrisonRanks.getInstance().getLadderManager() + .getLadderDefault().getLowestRank().orElse(null); + + if ( result != null ) { + // recursively call it again... + result = findNextRank( result, rankPrestige, targetRank, totalRanks); + } + } + } + } + + if ( result == null || !result.equals( targetRank ) ) { + // Warning: We have gone through all ranks and all prestige ranks and have not + // found the targetRank. That means the player has already reached + // the targetRank. So we MUST clear the totalRanks list so there will + // be no charges. + totalRanks.clear(); + } + + return result; + } + + private Rank findNextRanksOnDefaultLadder( Rank rankDefault, Rank targetRank, ArrayList totalRanks ) { + + Rank result = rankDefault; + + if ( rankDefault != null ) { + + // We need to add the current rankDefault before getting the next ranks: + totalRanks.add( result ); + + while ( result != null && !result.equals( targetRank ) ) { + result = result.getRankNext(); + + if ( result != null ) { + + totalRanks.add( result ); + } + } + } + + return result; + } @Override public List getTranslatedPlaceHolderKeys() { diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankPlayerSortOrderTopRanked.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankPlayerSortOrderTopRanked.java deleted file mode 100644 index a11570c67..000000000 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/managers/RankPlayerSortOrderTopRanked.java +++ /dev/null @@ -1,88 +0,0 @@ -package tech.mcprison.prison.ranks.managers; - -import java.util.Comparator; - -import tech.mcprison.prison.ranks.data.RankPlayer; - -public class RankPlayerSortOrderTopRanked - implements Comparator -{ - - - public RankPlayerSortOrderTopRanked() { - super(); - - } - - - @Override - public int compare( RankPlayer rp1, RankPlayer rp2 ) - { - int results = 0; - - if ( rp1 == null && rp2 == null ) { - results = 0; - } - else if ( rp1 == null ) { - results = -1; - } - else if ( rp2 == null ) { - results = 1; - } - else { - results = compareLadderPrestiges( rp1, rp2 ); - - if ( results == 0 ) { - - results = compareLadderDefault( rp1, rp2 ); - - if ( results == 0 ) { - results = Double.compare( rp1.getRankScore(), rp2.getRankScore() ); - } - } - } - - return results; - } - - private int compareLadderPrestiges( RankPlayer rp1, RankPlayer rp2 ) { - int results = 0; - - if ( rp1.getPlayerRankPrestiges() == null && rp2.getPlayerRankPrestiges() == null ) { - results = 0; - } - else if ( rp2.getPlayerRankPrestiges() == null ) { -// Handled when comparing PlayerRanks... ?? - results = -1; - } - else if ( rp1.getPlayerRankPrestiges() == null ) { - results = 1; - } - else { - results = rp2.getPlayerRankPrestiges().compareTo( rp1.getPlayerRankPrestiges() ); - } - - return results; - } - - private int compareLadderDefault( RankPlayer rp1, RankPlayer rp2 ) { - int results = 0; - - if ( rp1.getPlayerRankDefault() == null && rp2.getPlayerRankDefault() == null ) { - results = 0; - } - else if ( rp2.getPlayerRankDefault() == null ) { -// Handled when comparing PlayerRanks... - results = -1; - } - else if ( rp1.getPlayerRankDefault() == null ) { - results = 1; - } - else { - results = rp2.getPlayerRankDefault().compareTo( rp1.getPlayerRankDefault() ); - } - - return results; - } - -} diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/RanksStartupPlayerValidationsAsyncTask.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/RanksStartupPlayerValidationsAsyncTask.java index be2d02fae..a4cdf427c 100644 --- a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/RanksStartupPlayerValidationsAsyncTask.java +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/RanksStartupPlayerValidationsAsyncTask.java @@ -1,6 +1,8 @@ package tech.mcprison.prison.ranks.tasks; +import tech.mcprison.prison.output.Output; import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.TopNPlayers; import tech.mcprison.prison.tasks.PrisonRunnable; import tech.mcprison.prison.tasks.PrisonTaskSubmitter; @@ -17,10 +19,18 @@ public RanksStartupPlayerValidationsAsyncTask( PrisonRanks pRanks ) { public static void submitTaskSync( PrisonRanks pRanks ) { - RanksStartupPlayerValidationsAsyncTask rspvaTask = - new RanksStartupPlayerValidationsAsyncTask( pRanks ); - - PrisonTaskSubmitter.runTaskLaterAsync( rspvaTask, 0 ); + if ( PrisonRanks.getInstance().getDefaultLadderRankCount() != 0 ) { + + RanksStartupPlayerValidationsAsyncTask rspvaTask = + new RanksStartupPlayerValidationsAsyncTask( pRanks ); + + PrisonTaskSubmitter.runTaskLaterAsync( rspvaTask, 0 ); + } + else { + Output.get().logInfo( "Bypassing player validation task since no ranks have been " + + "defined yet."); + } + } @Override @@ -29,10 +39,16 @@ public void run() { pRanks.checkAllPlayersForJoin(); - // The following can take awhile to run if there are a lot of players - // and if they need to load their balance. This is impacted moreso if - // there is a high cost to get the balance. - pRanks.getPlayerManager().sortPlayerByTopRanked(); + // Start up the TopNPlayer's collections after all players have been loaded: + // NOTE: getting the instance of TopNPlayers must be done "after" player validation. + // So that thread needs to initiate it after done validating and fixing all players. + TopNPlayers.getInstance(); + + +// // The following can take awhile to run if there are a lot of players +// // and if they need to load their balance. This is impacted more so if +// // there is a high cost to get the balance. +// pRanks.getPlayerManager().sortPlayerByTopRanked(); } } diff --git a/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/TopNPlayerUpdateAsyncTask.java b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/TopNPlayerUpdateAsyncTask.java new file mode 100644 index 000000000..f9be9af9a --- /dev/null +++ b/prison-ranks/src/main/java/tech/mcprison/prison/ranks/tasks/TopNPlayerUpdateAsyncTask.java @@ -0,0 +1,42 @@ +package tech.mcprison.prison.ranks.tasks; + +import tech.mcprison.prison.ranks.data.TopNPlayers; +import tech.mcprison.prison.tasks.PrisonRunnable; +import tech.mcprison.prison.tasks.PrisonTaskSubmitter; + +public class TopNPlayerUpdateAsyncTask + implements PrisonRunnable { + + private TopNPlayers topNPlayers; + +// private boolean startup = true; + + public TopNPlayerUpdateAsyncTask( TopNPlayers topNPlayers ) { + super(); + + this.topNPlayers = topNPlayers; + } + + public static void submitTaskTimerAsync( TopNPlayers topNPlayers, + Long delayTicks, long intervalTicks ) { + + TopNPlayerUpdateAsyncTask asyncTask = + new TopNPlayerUpdateAsyncTask( topNPlayers ); + + PrisonTaskSubmitter.runTaskTimerAsync( asyncTask, delayTicks, intervalTicks ); + } + + @Override + public void run() { + +// if ( startup ) { +// startup = false; +// +// topNPlayers.loadSaveFile(); +// } + + topNPlayers.refreshAndSort(); + + } + +} 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 index fdb23d1b0..a2bb33628 100644 --- 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 @@ -8,6 +8,8 @@ import org.junit.Test; +import tech.mcprison.prison.ranks.managers.LadderManager; + public class PrisonSortableLaddersTest extends PrisonSortableLadders { @@ -16,9 +18,9 @@ public class PrisonSortableLaddersTest public void testGetSortedSet() { RankLadder ladderDefault = new RankLadder(); - ladderDefault.setName( "default" ); + ladderDefault.setName( LadderManager.LADDER_DEFAULT ); RankLadder ladderPrestige = new RankLadder(); - ladderPrestige.setName( "prestige" ); + ladderPrestige.setName( LadderManager.LADDER_PRESTIGES ); RankLadder ladderMods = new RankLadder(); ladderMods.setName( "mods" ); @@ -41,7 +43,7 @@ public void testGetSortedSet() assertEquals( "ZaPpS", unsortedList.get( 0 ).getName() ); assertEquals( "Donors", unsortedList.get( 1 ).getName() ); - assertEquals( "prestige", unsortedList.get( 2 ).getName() ); + assertEquals( "prestiges", unsortedList.get( 2 ).getName() ); assertEquals( "mods", unsortedList.get( 3 ).getName() ); assertEquals( "Animals", unsortedList.get( 4 ).getName() ); assertEquals( "default", unsortedList.get( 5 ).getName() ); @@ -62,7 +64,7 @@ public void testGetSortedSet() assertEquals( "Donors", sortedList.get( 2 ).getName() ); assertEquals( "mods", sortedList.get( 3 ).getName() ); assertEquals( "ZaPpS", sortedList.get( 4 ).getName() ); - assertEquals( "prestige", sortedList.get( 5 ).getName() ); + assertEquals( "prestiges", sortedList.get( 5 ).getName() ); } diff --git a/prison-spigot/build.gradle b/prison-spigot/build.gradle index c92ce18ff..b7349d3bb 100644 --- a/prison-spigot/build.gradle +++ b/prison-spigot/build.gradle @@ -116,7 +116,8 @@ dependencies { // implementation 'com.github.cryptomorin:xseries:b95d195482' // https://mvnrepository.com/artifact/com.github.cryptomorin/XSeries - implementation 'com.github.cryptomorin:XSeries:8.8.0' + implementation 'com.github.cryptomorin:XSeries:9.0.0' + //implementation 'com.github.cryptomorin:XSeries:8.8.0' @@ -208,7 +209,8 @@ shadowJar { include(dependency('me.clip:placeholderapi:2.10.9')) - include(dependency('com.github.cryptomorin:XSeries:8.8.0')) + include(dependency('com.github.cryptomorin:XSeries:9.0.0')) + //include(dependency('com.github.cryptomorin:XSeries:8.8.0')) include(dependency('de.tr7zw:item-nbt-api-plugin:2.10.0')) 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 f4bdcb071..772c02ab8 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 @@ -303,7 +303,7 @@ public class OnPlayerChatListener @EventHandler(priority=EventPriority.NORMAL) public void onPlayerChat(AsyncPlayerChatEvent e) { - String message = e.getMessage(); +// String message = e.getMessage(); String format = e.getFormat(); SpigotPlayer p = new SpigotPlayer( e.getPlayer() ); 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 31ff7da59..9e945d4da 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 @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; @@ -40,6 +41,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.SimpleCommandMap; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.EventPriority; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerChatEvent; @@ -53,8 +55,10 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonCommand; +import tech.mcprison.prison.PrisonCommand.RegisteredPluginsData; import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig.AutoFeatures; import tech.mcprison.prison.autofeatures.AutoFeaturesWrapper; +import tech.mcprison.prison.chat.FancyMessage; import tech.mcprison.prison.commands.PluginCommand; import tech.mcprison.prison.convert.ConversionManager; import tech.mcprison.prison.convert.ConversionResult; @@ -88,10 +92,12 @@ import tech.mcprison.prison.output.DisplayComponent; import tech.mcprison.prison.output.LogLevel; import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.output.RowComponent; import tech.mcprison.prison.ranks.PrisonRanks; import tech.mcprison.prison.ranks.commands.RanksCommands; import tech.mcprison.prison.ranks.data.PlayerRank; 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.RankPlayerFactory; import tech.mcprison.prison.ranks.managers.PlayerManager; @@ -287,7 +293,8 @@ public void getWorldLoadErrors( ChatDisplay display ) { @Override public List getOnlinePlayers() { return Bukkit.getOnlinePlayers().stream() - .map(player -> getPlayer(player.getUniqueId()).get()).collect(Collectors.toList()); + .map(player -> getPlayer(player.getUniqueId()).get()) + .collect(Collectors.toList()); } @Override @@ -714,8 +721,29 @@ private boolean isDoor(Material block) { return capabilities; } + /** + *

This can be useful to see if a given plugin is active. The returned + * data, RegisteredPluginData, has additional information pertaining to the + * plugin. If the plugin is not found, then this will return a null value. + *

+ * + * @param pluginName + * @return + */ + public RegisteredPluginsData identifyRegisteredPlugin( String pluginName ) { + identifyRegisteredPlugins( false ); + + RegisteredPluginsData plugin = Prison.get().getPrisonCommands().getRegisteredPluginData().get( pluginName ); + + return plugin; + } + @Override public void identifyRegisteredPlugins() { + identifyRegisteredPlugins( true ); + } + + public void identifyRegisteredPlugins( boolean checkForWarnings ) { PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); // reset so it will reload cleanly: @@ -752,7 +780,7 @@ public void identifyRegisteredPlugins() { } } - if ( isPlugManPresent ) { + if ( checkForWarnings && isPlugManPresent ) { ChatDisplay chatDisplay = new ChatDisplay("&d* *&5 WARNING: &d PlugMan &5 Detected! &d* *"); chatDisplay.addText( "&7The use of PlugMan on this Prison server will corrupt internals" ); chatDisplay.addText( "&7of Prison and may lead to a non-functional state, or even total" ); @@ -1531,7 +1559,10 @@ public void autoCreateMineBlockAssignment( List rankMineNames, boolean f // Turn on sellall: - SpigotPrison.getInstance().getConfig().set( "sellall", true ); + YamlConfiguration modulesConf = SpigotPrison.getInstance().loadConfig("modules.yml"); + modulesConf.set( "sellall", Boolean.TRUE ); + SpigotPrison.getInstance().saveConfig("modules.yml", modulesConf ); + PrisonSpigotSellAllCommands sellall = PrisonSpigotSellAllCommands.get(); @@ -2236,7 +2267,7 @@ public List getActiveFeatures( boolean showLaddersAndRanks ) { results.add( String.format("Sellall Enabled:&b %s", - getConfigBooleanFalse( "sellall" )) ); + Boolean.toString( SpigotPrison.getInstance().isSellAllEnabled() )) ); results.add( String.format("Backpacks Enabled:&b %s", @@ -2299,6 +2330,18 @@ else if ( !isBasic ) { display.addText("&7Integrations:"); IntegrationManager im = Prison.get().getIntegrationManager(); + +// Set inTypeKeys = im.getIntegrations().keySet(); +// for (IntegrationType inTypeKey : inTypeKeys ) { +// List integrations = im.getIntegrations().get( inTypeKey ); +// +// for (Integration integration : integrations) { +// +// +// +// } +// } + String permissions = (im.hasForType(IntegrationType.PERMISSION) ? " " + im.getForType(IntegrationType.PERMISSION).get().getDisplayName() : @@ -2771,4 +2814,95 @@ public void setActionBar( Player player, String actionBar ) { public int compareServerVerisonTo( String comparisonVersion ) { return new BluesSpigetSemVerComparator().compareMCVersionTo( comparisonVersion ); } + + @Override + public void checkPlayerDefaultRank( RankPlayer rPlayer ) { + + PrisonRanks.getInstance().getPlayerManager().checkPlayerDefaultRank( rPlayer ); + } + + + @Override + public void listAllMines(tech.mcprison.prison.internal.CommandSender sender, Player player) { + + RankPlayer rPlayer = PrisonRanks.getInstance().getPlayerManager().getPlayer(player); + List mines = new ArrayList<>(); + + if ( rPlayer != null ) { + + Set keys = rPlayer.getLadderRanks().keySet(); + for ( RankLadder key : keys ) { + PlayerRank pRank = rPlayer.getLadderRanks().get(key); + + listMines( sender, player, pRank, mines ); + } + + + if ( mines.size() > 0 ) { + + // String builder will be used to track what the "real" text width is: + StringBuilder sb = new StringBuilder(); + + RowComponent row = new RowComponent(); + row.addTextComponent( "&3Mines: " ); + sb.append( "&3Mines: " ); + + for ( Mine mine : mines ) { + + FancyMessage msgMine = new FancyMessage( String.format( "%s", mine.getTag() ) ) + .suggest( "/mines tp " + mine.getName() ) + .tooltip("Click to teleport to mine"); + + row.addFancy( msgMine ); + sb.append( mine.getTag() ); + + row.addTextComponent( " " ); + sb.append( " " ); + + String noColor = Text.stripColor( sb.toString() ); + + if ( noColor.length() > 50 ) { + + // Send the player the mines list: + row.send( sender ); + + // Reset the row and sb to start on the next row of mines: + row = new RowComponent(); + sb.setLength( 0 ); + + // Setup the start of the row: + row.addTextComponent( "&3Mines: " ); + sb.append( "&3Mines: " ); + + } + } + if ( sb.length() > 0 ) { + // Send the player the mines list: + row.send( sender ); + } + } + + } + } + + private void listMines(tech.mcprison.prison.internal.CommandSender sender, + Player player, PlayerRank pRank, List mines ) { + + Rank rank = pRank.getRank(); + + while ( rank != null ) { + + for ( ModuleElement mElement : rank.getMines() ) { + Mine mine = PrisonMines.getInstance().getMine( mElement.getName() ); + + if ( mine.hasMiningAccess( player ) ) { + mines.add( mine ); + } + } + + rank = rank.getRankPrior(); + } + } + + } 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 5bdd5e890..0500e4926 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 @@ -19,18 +19,11 @@ package tech.mcprison.prison.spigot; import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Callable; - -import org.bstats.bukkit.Metrics; -import org.bstats.charts.MultiLineChart; -import org.bstats.charts.SimpleBarChart; -import org.bstats.charts.SimplePie; + import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.SimpleCommandMap; @@ -45,7 +38,9 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.PrisonCommand; +import tech.mcprison.prison.PrisonCommand.RegisteredPluginsData; import tech.mcprison.prison.alerts.Alerts; +import tech.mcprison.prison.backups.PrisonBackups; import tech.mcprison.prison.integration.Integration; import tech.mcprison.prison.integration.IntegrationType; import tech.mcprison.prison.internal.block.PrisonBlockTypes; @@ -69,6 +64,7 @@ import tech.mcprison.prison.spigot.autofeatures.events.AutoManagerBlockBreakEvents; import tech.mcprison.prison.spigot.backpacks.BackpacksListeners; import tech.mcprison.prison.spigot.block.OnBlockBreakEventListener; +import tech.mcprison.prison.spigot.bstats.PrisonBStats; import tech.mcprison.prison.spigot.commands.PrisonSpigotBackpackCommands; import tech.mcprison.prison.spigot.commands.PrisonSpigotGUICommands; import tech.mcprison.prison.spigot.commands.PrisonSpigotMinesCommands; @@ -141,7 +137,13 @@ public class SpigotPrison private File moduleDataFolder; private List registeredBlockListeners; + + + private PrisonBStats prisonBStats; +// private Metrics bStatsMetrics = null; +// private PrisonMetrics bStatsMetrics = null; + public static SpigotPrison getInstance(){ return config; } @@ -158,6 +160,11 @@ public SpigotPrison() { @Override public void onLoad() { + // Startup bStats: Not needed with the new PrisonMetrics class. + this.prisonBStats = new PrisonBStats( this ); + prisonBStats.initMetricsOnLoad(); + + /** * 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. @@ -213,7 +220,16 @@ public void onEnable() { // Show Prison's splash screen and setup the core components: Prison.get() - .init(platform, Bukkit.getVersion()); + .init( platform, Bukkit.getVersion(), getDataFolder() ); + + + + // If prison version is new, then make a copy of all config files that may change on startup: + PrisonBackups backups = new PrisonBackups(); + backups.initialStartupVersionCheck(); + + + // Enable the spigot locale manager: getLocaleManager(); @@ -235,8 +251,26 @@ public void onEnable() { boolean delayedPrisonStartup = getConfig().getBoolean("delayedPrisonStartup.enabled", false); + if ( !delayedPrisonStartup ) { - onEnableStartup(); + + // Check to see if CMI is an active plugin. If it is, then let's enable the delayed startup. + // It should be noted that just because CMI is detected, it does not mean that the CMI Economy + // is being used. Use a flexible startup which means it will start if any vault economy + // is found. + RegisteredPluginsData cmiPlugin = platform.identifyRegisteredPlugin( "CMI" ); + if ( cmiPlugin != null ) { + String cmiMessage = String.format( + "CMI was detected and Prison's delayed startup has been enabled: %s - %s ", + cmiPlugin.getPluginName(), cmiPlugin.getPluginVersion() ); + Output.get().logInfo( cmiMessage ); + + onEnableDelayedStartFlexible(); + } + else { + + onEnableStartup(); + } } else { onEnableDelayedStart(); @@ -245,6 +279,13 @@ public void onEnable() { } + protected void onEnableDelayedStartFlexible() { + + SpigotPrisonDelayedStartupTask delayedStartupTask = new SpigotPrisonDelayedStartupTask( this ); + delayedStartupTask.setUseAnyVaultEconomy( true ); + delayedStartupTask.submit(); + } + protected void onEnableDelayedStart() { SpigotPrisonDelayedStartupTask delayedStartupTask = new SpigotPrisonDelayedStartupTask( this ); @@ -284,20 +325,14 @@ public void onEnableStartup() { Bukkit.getPluginManager().registerEvents(new BackpacksListeners(), this); } - try { - isSellAllEnabled = getConfig().getBoolean("sellall"); - } catch (NullPointerException ignored){} - - if (isSellAllEnabled){ - SellAllUtil.get(); - } - initIntegrations(); - + // Sellall set to disabled since it will be set to the correct value in enableModulesAndCommands(): + isSellAllEnabled = false; // This is the loader for modules and commands: enableModulesAndCommands(); + // // NOTE: Put all commands within the initModulesAndCommands() function. // initModulesAndCommands(); @@ -314,14 +349,12 @@ public void onEnableStartup() { getBlockBreakEventListeners().registerAllBlockBreakEvents( this ); - initMetrics(); - - // These stats are displayed within the initDeferredModules(): //Prison.get().getPlatform().getPlaceholders().printPlaceholderStats(); - PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); + @SuppressWarnings("unused") + PrisonCommand cmdVersion = Prison.get().getPrisonCommands(); // if (doAlertAboutConvert) { // Alerts.getInstance().sendAlert( @@ -358,6 +391,18 @@ public void onEnableStartup() { if ( getConfig().getBoolean("prison-block-compatibility-report") ) { SpigotUtil.testAllPrisonBlockTypes(); } + + + // Startup bStats: + prisonBStats.initMetricsOnEnable(); + + + + // Force a backup if prison version is new: + PrisonBackups backups = new PrisonBackups(); + backups.serverStartupVersionCheck(); + + Output.get().logInfo( "Prison - Finished loading." ); @@ -510,79 +555,302 @@ public static String stripColor(String format){ // return format == null ? null : ChatColor.stripColor(format); } - /** - *

bStats reporting

- * - * https://github.com/Bastian/bStats-Metrics/tree/master/base/src/main/java/org/bstats/charts - * - */ - private void initMetrics() { - if (!getConfig().getBoolean("send-metrics", true)) { - return; // Don't check if they don't want it - } - - int pluginId = 657; - Metrics metrics = new Metrics( this, pluginId ); - - // Report the modules being used - SimpleBarChart sbcModulesUsed = new SimpleBarChart("modules_used", () -> { - Map valueMap = new HashMap<>(); - for (Module m : PrisonAPI.getModuleManager().getModules()) { - valueMap.put(m.getName(), 1); - } - return valueMap; - }); - metrics.addCustomChart( sbcModulesUsed ); - - // Report the API level - SimplePie spApiLevel = - new SimplePie("api_level", () -> - "API Level " + Prison.API_LEVEL + "." + Prison.API_LEVEL_MINOR ); - metrics.addCustomChart( spApiLevel ); - - - 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 defaultRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getDefaultLadderRankCount()).orElse(0); - int prestigesRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPrestigesLadderRankCount()).orElse(0); - int otherRankCount = rankCount - defaultRankCount - prestigesRankCount; - - int ladderCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getladderCount()).orElse(0); - int playerCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPlayersCount()).orElse(0); - - MultiLineChart mlcMinesRanksAndLadders = - new 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); - valueMap.put("players", playerCount); - return valueMap; - } - }); - metrics.addCustomChart( mlcMinesRanksAndLadders ); - - MultiLineChart mlcPrisonRanks = new MultiLineChart("prison_ranks", new Callable>() { - @Override - public Map call() throws Exception { - Map valueMap = new HashMap<>(); - valueMap.put("ranks", rankCount); - valueMap.put("defaultRanks", defaultRankCount); - valueMap.put("prestigesRanks", prestigesRankCount); - valueMap.put("otherRanks", otherRankCount); - return valueMap; - } - }); - metrics.addCustomChart( mlcPrisonRanks ); - } +// /** +// *

bStats reporting

+// * +// * https://github.com/Bastian/bStats-Metrics/tree/master/base/src/main/java/org/bstats/charts +// * +// */ +// private void initMetricsOnLoad() { +// if (!getConfig().getBoolean("send-metrics", true)) { +// return; // Don't check if they don't want it +// } +// +// int pluginId = 657; +// bStatsMetrics = new Metrics( this, pluginId ); +//// bStatsMetrics = new PrisonMetrics( this, pluginId ); +// +//// Metrics metrics = new Metrics( this, pluginId ); +// } +// +// private void initMetricsOnEnable() { +// if (!getConfig().getBoolean("send-metrics", true)) { +// return; // Don't check if they don't want it +// } +// +// +// if ( bStatsMetrics == null ) { +// int pluginId = 657; +// +// bStatsMetrics = new Metrics( this, pluginId ); +//// bStatsMetrics = new PrisonMetrics( this, pluginId ); +// } +// +// // Report the modules being used +// SimpleBarChart sbcModulesUsed = new SimpleBarChart("modules_used", () -> { +// Map valueMap = new HashMap<>(); +// for (Module m : PrisonAPI.getModuleManager().getModules()) { +// valueMap.put(m.getName(), 1); +// } +// return valueMap; +// }); +// bStatsMetrics.addCustomChart( sbcModulesUsed ); +// +// // Report the API level +// SimplePie spApiLevel = +// new SimplePie("api_level", () -> +// "API Level " + Prison.API_LEVEL + "." + Prison.API_LEVEL_MINOR ); +// bStatsMetrics.addCustomChart( spApiLevel ); +// +// +// 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 defaultRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getDefaultLadderRankCount()).orElse(0); +// int prestigesRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPrestigesLadderRankCount()).orElse(0); +// int otherRankCount = rankCount - defaultRankCount - prestigesRankCount; +// +// int ladderCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getladderCount()).orElse(0); +// int playerCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPlayersCount()).orElse(0); +// +// +// +// DrilldownPie mlcPrisonRanksAndLadders = new DrilldownPie("mines_ranks_and_ladders", () -> { +// Map> map = new HashMap<>(); +// +// Map ranks = new HashMap<>(); +// ranks.put( Integer.toString( mineCount ), 1 ); +// map.put( "mines", ranks ); +// +// Map defRanks = new HashMap<>(); +// defRanks.put( Integer.toString( rankCount ), 1 ); +// map.put( "ranks", defRanks ); +// +// Map prestigesRanks = new HashMap<>(); +// prestigesRanks.put( Integer.toString( ladderCount ), 1 ); +// map.put( "ladders", prestigesRanks ); +// +// Map otherRanks = new HashMap<>(); +// otherRanks.put( Integer.toString( playerCount ), 1 ); +// map.put( "players", otherRanks ); +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonRanksAndLadders ); +// +//// MultiLineChart mlcMinesRanksAndLadders = +//// new 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); +//// valueMap.put("players", playerCount); +//// return valueMap; +//// } +//// }); +//// bStatsMetrics.addCustomChart( mlcMinesRanksAndLadders ); +// +// +// +// DrilldownPie mlcPrisonPrisonRanks = new DrilldownPie("prison_ranks", () -> { +// Map> map = new HashMap<>(); +// +// Map ranks = new HashMap<>(); +// ranks.put( Integer.toString( rankCount ), 1 ); +// map.put( "ranks", ranks ); +// +// Map defRanks = new HashMap<>(); +// defRanks.put( Integer.toString( defaultRankCount ), 1 ); +// map.put( "defaultRanks", defRanks ); +// +// Map prestigesRanks = new HashMap<>(); +// prestigesRanks.put( Integer.toString( prestigesRankCount ), 1 ); +// map.put( "prestigesRanks", prestigesRanks ); +// +// Map otherRanks = new HashMap<>(); +// otherRanks.put( Integer.toString( otherRankCount ), 1 ); +// map.put( "otherRanks", otherRanks ); +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPrisonRanks ); +// +//// MultiLineChart mlcPrisonRanks = new MultiLineChart("prison_ranks", new Callable>() { +//// @Override +//// public Map call() throws Exception { +//// Map valueMap = new HashMap<>(); +//// valueMap.put("ranks", rankCount); +//// valueMap.put("defaultRanks", defaultRankCount); +//// valueMap.put("prestigesRanks", prestigesRankCount); +//// valueMap.put("otherRanks", otherRankCount); +//// return valueMap; +//// } +//// }); +//// bStatsMetrics.addCustomChart( mlcPrisonRanks ); +// +// +// DrilldownPie mlcPrisonPrisonLadders = new DrilldownPie("prison_ladders", () -> { +// Map> map = new HashMap<>(); +// +// +// PrisonRanks pRanks = (PrisonRanks) prisonRanksOpt.orElseGet( null ); +// for ( RankLadder ladder : pRanks.getLadderManager().getLadders() ) { +// +// Map entry = new HashMap<>(); +// entry.put( Integer.toString( ladder.getRanks().size() ), 1 ); +// +// map.put( ladder.getName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPrisonLadders ); +// +// +//// MultiLineChart mlcPrisonladders = new MultiLineChart("prison_ladders", new Callable>() { +//// @Override +//// public Map call() throws Exception { +//// Map valueMap = new HashMap<>(); +//// +//// PrisonRanks pRanks = (PrisonRanks) prisonRanksOpt.orElseGet( null ); +//// for ( RankLadder ladder : pRanks.getLadderManager().getLadders() ) { +//// +//// valueMap.put( ladder.getName(), ladder.getRanks().size() ); +//// } +//// +//// return valueMap; +//// } +//// }); +//// bStatsMetrics.addCustomChart( mlcPrisonladders ); +// +// TreeMap plugins = Prison.get().getPrisonCommands().getRegisteredPluginData(); +// +// TreeMap pluginsAtoE = getSubsetOfPlugins(plugins, 'a', 'f', false ); +// TreeMap pluginsFtoM = getSubsetOfPlugins(plugins, 'f', 'n', false ); +// TreeMap pluginsNtoS = getSubsetOfPlugins(plugins, 'n', 't', false ); +// TreeMap pluginsTto9 = getSubsetOfPlugins(plugins, 't', 'z', true ); +// +// DrilldownPie mlcPrisonPlugins = new DrilldownPie("plugins", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : plugins.keySet() ) { +// RegisteredPluginsData pluginData = plugins.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPlugins ); +// +// +// DrilldownPie mlcPrisonPluginsAtoE = new DrilldownPie("plugins_a_to_e", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : pluginsAtoE.keySet() ) { +// RegisteredPluginsData pluginData = pluginsAtoE.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPluginsAtoE ); +// +// +// DrilldownPie mlcPrisonPluginsFtoM = new DrilldownPie("plugins_f_to_m", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : pluginsFtoM.keySet() ) { +// RegisteredPluginsData pluginData = pluginsFtoM.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPluginsFtoM ); +// +// +// DrilldownPie mlcPrisonPluginsNtoS = new DrilldownPie("plugins_n_to_s", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : pluginsNtoS.keySet() ) { +// RegisteredPluginsData pluginData = pluginsNtoS.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPluginsNtoS ); +// +// +// DrilldownPie mlcPrisonPluginsTto9 = new DrilldownPie("plugins_t_to_z_plus_others", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : pluginsTto9.keySet() ) { +// RegisteredPluginsData pluginData = pluginsTto9.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// bStatsMetrics.addCustomChart( mlcPrisonPluginsTto9 ); +// +// +// +// } + +// private TreeMap getSubsetOfPlugins( +// TreeMap plugins, +// char rangeLow, char rangeHigh, +// boolean includeNonAlpha ) { +// TreeMap results = new TreeMap<>(); +// +// Set keys = plugins.keySet(); +// for (String key : keys) { +// char keyFirstChar = key.toLowerCase().charAt(0); +// +// if ( Character.isAlphabetic(keyFirstChar) ) { +// +// if ( Character.compare(keyFirstChar, rangeLow) >= 0 && Character.compare( keyFirstChar, rangeHigh) < 0 ) { +// +// results.put( key, plugins.get(key) ); +// } +// } +// else { +// +// // Add all non-alpha plugins to this result: +// results.put( key, plugins.get(key) ); +// } +// } +// +// return results; +// } - /** + /** * Checks to see if there is a newer version of prison that has been released. * It checks based upon what is deployed to spigotmc.org. */ @@ -782,20 +1050,30 @@ private void initModulesAndCommands() { // If the sellall module is defined in modules.yml, then use that setting, otherwise // use the sellall config setting within the config.yml file. - String moduleName = PrisonSellall.MODULE_NAME.toLowerCase(); - boolean isDefined = modulesConf.contains(moduleName); + String sellallModuleName = PrisonSellall.MODULE_NAME.toLowerCase(); + boolean isDefined = modulesConf.contains(sellallModuleName); - if ( isDefined && modulesConf.getBoolean(moduleName) || + // First check to see if the module is enabled (sellall): + if ( isDefined && modulesConf.getBoolean(sellallModuleName) || + // if not, then check to see if sellall is enabled within config.yml: !isDefined && getConfig().contains("sellall") && getConfig().isBoolean("sellall") ) { -// modulesConf.getBoolean( PrisonSellall.MODULE_NAME.toLowerCase(), true ) || -// getConfig().contains("sellall") && getConfig().isBoolean("sellall")) { PrisonSellall sellallModule = new PrisonSellall(getDescription().getVersion() ); - // Register and enable Ranks: + // Register and enable the sellall module: Prison.get().getModuleManager().registerModule( sellallModule ); Prison.get().getCommandHandler().registerCommands( new SellallCommands() ); + + + isSellAllEnabled = true; + + + // If sellall is enabled, then allow it to initialize. + if (isSellAllEnabled){ + SellAllUtil.get(); + } + } else { @@ -974,9 +1252,24 @@ private File getBundledFile(String name) { return file; } - private YamlConfiguration loadConfig(String file) { + public YamlConfiguration loadConfig(String file) { return YamlConfiguration.loadConfiguration(getBundledFile(file)); } + + public void saveConfig(String fileName, YamlConfiguration config ) { + if ( config != null ) { + File file = getBundledFile(fileName); + try { + config.save( file ); + } + catch (IOException e) { + String message = String.format( "Error saving config file: %s [%s]", + file.getAbsoluteFile(), e.getMessage() ); + + Output.get().logError( message ); + } + } + } File getDataDirectory() { return dataDirectory; @@ -1024,4 +1317,5 @@ public PrisonBlockTypes getPrisonBlockTypes() { public List getRegisteredBlockListeners() { return registeredBlockListeners; } + } 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 ba1beeb16..40d0eab94 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 @@ -278,7 +278,14 @@ public static HashMap addItemToPlayerInventory( // Cannot stick it anywhere else, so return the extras: for ( Integer key : overflow.keySet() ) { - results.put(key, new SpigotItemStack(overflow.get(key))); + ItemStack iStack = overflow.get(key); + if ( iStack != null ) { + SpigotItemStack siStack = new SpigotItemStack(iStack); + if ( siStack != null ) { + + results.put(key, siStack); + } + } } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/Updater.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/Updater.java index 60e722811..d9d60d5ee 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/Updater.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/Updater.java @@ -1,795 +1,784 @@ package tech.mcprison.prison.spigot; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitRunnable; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.util.Enumeration; -import java.util.logging.Level; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Check for updates on BukkitDev for a given plugin, and download the updates if needed. - *

- * VERY, VERY IMPORTANT: Because there are no standards for adding auto-update toggles in your plugin's config, this system provides NO CHECK WITH YOUR CONFIG to make sure the user has allowed auto-updating. - *
- * It is a BUKKIT POLICY that you include a boolean value in your config that prevents the auto-updater from running AT ALL. - *
- * If you fail to include this option in your config, your plugin will be REJECTED when you attempt to submit it to dev.bukkit.org. - *

- * An example of a good configuration option would be something similar to 'auto-update: true' - if this value is set to false you may NOT run the auto-updater. - *
- * If you are unsure about these rules, please read the plugin submission guidelines: http://goo.gl/8iU5l - * - * @author Gravity - * @version 2.4 - */ +///** +// * Check for updates on BukkitDev for a given plugin, and download the updates if needed. +// *

+// * VERY, VERY IMPORTANT: Because there are no standards for adding auto-update toggles in your plugin's config, this system provides NO CHECK WITH YOUR CONFIG to make sure the user has allowed auto-updating. +// *
+// * It is a BUKKIT POLICY that you include a boolean value in your config that prevents the auto-updater from running AT ALL. +// *
+// * If you fail to include this option in your config, your plugin will be REJECTED when you attempt to submit it to dev.bukkit.org. +// *

+// * An example of a good configuration option would be something similar to 'auto-update: true' - if this value is set to false you may NOT run the auto-updater. +// *
+// * If you are unsure about these rules, please read the plugin submission guidelines: http://goo.gl/8iU5l +// * +// * @author Gravity +// * @version 2.4 +// */ public class Updater { - /* Constants */ - - // Remote file's title - private static final String TITLE_VALUE = "name"; - // Remote file's download link - private static final String LINK_VALUE = "downloadUrl"; - // Remote file's release type - private static final String TYPE_VALUE = "releaseType"; - // Remote file's build version - private static final String VERSION_VALUE = "gameVersion"; - // Path to GET - private static final String QUERY = "/servermods/files?projectIds="; - // Slugs will be appended to this to get to the project's RSS feed - private static final String HOST = "https://api.curseforge.com"; - // User-agent when querying Curse - private static final String USER_AGENT = "Updater (by Gravity)"; - // Used for locating version numbers in file names - private static final String DELIMETER = "^v|[\\s_-]v"; - // If the version number contains one of these, don't update. - private static final String[] NO_UPDATE_TAG = {"-DEV", "-PRE", "SNAPSHOT"}; - // Used for downloading files - private static final int BYTE_SIZE = 1024; - // Config key for api key - private static final String API_KEY_CONFIG_KEY = "api-key"; - // Config key for disabling Updater - private static final String DISABLE_CONFIG_KEY = "disable"; - // Default api key value in config - private static final String API_KEY_DEFAULT = "PUT_API_KEY_HERE"; - // Default disable value in config - private static final boolean DISABLE_DEFAULT = false; - - /* User-provided variables */ - - // Plugin running Updater - private final Plugin plugin; - // Type of update check to run - private final UpdateType type; - // Whether to announce file downloads - private final boolean announce; - // The plugin file (jar) - private final File file; - // The folder that downloads will be placed in - private final File updateFolder; - // The provided callback (if any) - private final UpdateCallback callback; - // Project's Curse ID - private int id = -1; - // BukkitDev ServerMods API key - private String apiKey = null; - - /* Collected from Curse API */ - - private String versionName; - private String versionLink; - private String versionType; - private String versionGameVersion; - - /* Update process variables */ - - // Connection to RSS - private URL url; - // Updater thread - private Thread thread; - // Used for determining the outcome of the update process - private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; - - - /** - * Gives the developer the result of the update process. Can be obtained by called {@link #getResult()} - */ - public enum UpdateResult { - /** - * The updater found an update, and has readied it to be loaded the next time the server restarts/reloads. - */ - SUCCESS, /** - * The updater did not find an update, and nothing was downloaded. - */ - NO_UPDATE, /** - * The server administrator has disabled the updating system. - */ - DISABLED, /** - * The updater found an update, but was unable to download it. - */ - FAIL_DOWNLOAD, /** - * For some reason, the updater was unable to contact dev.bukkit.org to download the file. - */ - FAIL_DBO, /** - * When running the version check, the file on DBO did not contain a recognizable version. - */ - FAIL_NOVERSION, /** - * The id provided by the plugin running the updater was invalid and doesn't exist on DBO. - */ - FAIL_BADID, /** - * The server administrator has improperly configured their API key in the configuration. - */ - FAIL_APIKEY, /** - * The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded. - */ - UPDATE_AVAILABLE - } - - - /** - * Allows the developer to specify the type of update that will be run. - */ - public enum UpdateType { - /** - * Run a version check, and then if the file is out of date, download the newest version. - */ - DEFAULT, /** - * Don't run a version check, just find the latest update and download it. - */ - NO_VERSION_CHECK, /** - * Get information about the version and the download size, but don't actually download anything. - */ - NO_DOWNLOAD - } - - - /** - * Represents the various release types of a file on BukkitDev. - */ - public enum ReleaseType { - /** - * An "alpha" file. - */ - ALPHA, /** - * A "beta" file. - */ - BETA, /** - * A "release" file. - */ - RELEASE - } - - /** - * Initialize the updater. - * - * @param plugin The plugin that is checking for an update. - * @param id The dev.bukkit.org id of the project. - * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. - * @param type Specify the type of update this will be. See {@link UpdateType} - * @param announce True if the program should announce the progress of new updates in console. - */ - public Updater(Plugin plugin, int id, File file, UpdateType type, boolean announce) { - this(plugin, id, file, type, null, announce); - } - - /** - * Initialize the updater with the provided callback. - * - * @param plugin The plugin that is checking for an update. - * @param id The dev.bukkit.org id of the project. - * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. - * @param type Specify the type of update this will be. See {@link UpdateType} - * @param callback The callback instance to notify when the Updater has finished - */ - public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback) { - this(plugin, id, file, type, callback, false); - } - - /** - * Initialize the updater with the provided callback. - * - * @param plugin The plugin that is checking for an update. - * @param id The dev.bukkit.org id of the project. - * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. - * @param type Specify the type of update this will be. See {@link UpdateType} - * @param callback The callback instance to notify when the Updater has finished - * @param announce True if the program should announce the progress of new updates in console. - */ - public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback, - boolean announce) { - this.plugin = plugin; - this.type = type; - this.announce = announce; - this.file = file; - this.id = id; - this.updateFolder = this.plugin.getServer().getUpdateFolderFile(); - this.callback = callback; - - final File pluginFile = this.plugin.getDataFolder().getParentFile(); - final File updaterFile = new File(pluginFile, "Updater"); - final File updaterConfigFile = new File(updaterFile, "config.yml"); - - YamlConfiguration config = new YamlConfiguration(); - config.options().header( - "This configuration file affects all plugins using the Updater system (version 2+ - http://forums.bukkit.org/threads/96681/ )" - + '\n' - + "If you wish to use your API key, read http://wiki.bukkit.org/ServerMods_API and place it below." - + '\n' - + "Some updating systems will not adhere to the disabled value, but these may be turned off in their plugin's configuration."); - config.addDefault(API_KEY_CONFIG_KEY, API_KEY_DEFAULT); - config.addDefault(DISABLE_CONFIG_KEY, DISABLE_DEFAULT); - - if (!updaterFile.exists()) { - this.fileIOOrError(updaterFile, updaterFile.mkdir(), true); - } - - boolean createFile = !updaterConfigFile.exists(); - try { - if (createFile) { - this.fileIOOrError(updaterConfigFile, updaterConfigFile.createNewFile(), true); - config.options().copyDefaults(true); - config.save(updaterConfigFile); - } else { - config.load(updaterConfigFile); - } - } catch (final Exception e) { - final String message; - if (createFile) { - message = "The updater could not create configuration at " + updaterFile - .getAbsolutePath(); - } else { - message = - "The updater could not load configuration at " + updaterFile.getAbsolutePath(); - } - this.plugin.getLogger().log(Level.SEVERE, message, e); - } - - if (config.getBoolean(DISABLE_CONFIG_KEY)) { - this.result = UpdateResult.DISABLED; - return; - } - - String key = config.getString(API_KEY_CONFIG_KEY); - if (API_KEY_DEFAULT.equalsIgnoreCase(key) || "".equals(key)) { - key = null; - } - - this.apiKey = key; - - try { - this.url = new URL(Updater.HOST + Updater.QUERY + this.id); - } catch (final MalformedURLException e) { - this.plugin.getLogger().log(Level.SEVERE, - "The project ID provided for updating, " + this.id + " is invalid.", e); - this.result = UpdateResult.FAIL_BADID; - } - - if (this.result != UpdateResult.FAIL_BADID) { - this.thread = new Thread(new UpdateRunnable()); - this.thread.start(); - } else { - runUpdater(); - } - } - - /** - * Get the result of the update process. - * - * @return result of the update process. - * @see UpdateResult - */ - public Updater.UpdateResult getResult() { - this.waitForThread(); - return this.result; - } - - /** - * Get the latest version's release type. - * - * @return latest version's release type. - * @see ReleaseType - */ - public ReleaseType getLatestType() { - this.waitForThread(); - if (this.versionType != null) { - for (ReleaseType type : ReleaseType.values()) { - if (this.versionType.equalsIgnoreCase(type.name())) { - return type; - } - } - } - return null; - } - - /** - * Get the latest version's game version (such as "CB 1.2.5-R1.0"). - * - * @return latest version's game version. - */ - public String getLatestGameVersion() { - this.waitForThread(); - return this.versionGameVersion; - } - - /** - * Get the latest version's name (such as "Project v1.0"). - * - * @return latest version's name. - */ - public String getLatestName() { - this.waitForThread(); - return this.versionName; - } - - /** - * Get the latest version's direct file link. - * - * @return latest version's file link. - */ - public String getLatestFileLink() { - this.waitForThread(); - return this.versionLink; - } - - /** - * As the result of Updater output depends on the thread's completion, it is necessary to wait for the thread to finish - * before allowing anyone to check the result. - */ - private void waitForThread() { - if ((this.thread != null) && this.thread.isAlive()) { - try { - this.thread.join(); - } catch (final InterruptedException e) { - this.plugin.getLogger().log(Level.SEVERE, null, e); - } - } - } - - /** - * Save an update from dev.bukkit.org into the server's update folder. - * - * @param file the name of the file to save it as. - */ - private void saveFile(String file) { - final File folder = this.updateFolder; - - deleteOldFiles(); - if (!folder.exists()) { - this.fileIOOrError(folder, folder.mkdir(), true); - } - downloadFile(); - - // Check to see if it's a zip file, if it is, unzip it. - final File dFile = new File(folder.getAbsolutePath(), file); - if (dFile.getName().endsWith(".zip")) { - // Unzip - this.unzip(dFile.getAbsolutePath()); - } - if (this.announce) { - this.plugin.getLogger().info("Finished updating."); - } - } - - /** - * Download a file and save it to the specified folder. - */ - private void downloadFile() { - BufferedInputStream in = null; - FileOutputStream fout = null; - try { - URL fileUrl = followRedirects(this.versionLink); - final int fileLength = fileUrl.openConnection().getContentLength(); - in = new BufferedInputStream(fileUrl.openStream()); - fout = new FileOutputStream(new File(this.updateFolder, file.getName())); - - final byte[] data = new byte[Updater.BYTE_SIZE]; - int count; - if (this.announce) { - this.plugin.getLogger().info("About to download a new update: " + this.versionName); - } - long downloaded = 0; - while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) { - downloaded += count; - fout.write(data, 0, count); - final int percent = (int) ((downloaded * 100) / fileLength); - if (this.announce && ((percent % 10) == 0)) { - this.plugin.getLogger() - .info("Downloading update: " + percent + "% of " + fileLength + " bytes."); - } - } - } catch (Exception ex) { - this.plugin.getLogger().log(Level.WARNING, - "The auto-updater tried to download a new update, but was unsuccessful.", ex); - this.result = Updater.UpdateResult.FAIL_DOWNLOAD; - } finally { - try { - if (in != null) { - in.close(); - } - } catch (final IOException ex) { - this.plugin.getLogger().log(Level.SEVERE, null, ex); - } - try { - if (fout != null) { - fout.close(); - } - } catch (final IOException ex) { - this.plugin.getLogger().log(Level.SEVERE, null, ex); - } - } - } - - private URL followRedirects(String location) throws IOException { - URL resourceUrl, base, next; - HttpURLConnection conn; - String redLoc; - while (true) { - resourceUrl = new URL(location); - conn = (HttpURLConnection) resourceUrl.openConnection(); - - conn.setConnectTimeout(15000); - conn.setReadTimeout(15000); - conn.setInstanceFollowRedirects(false); - conn.setRequestProperty("User-Agent", "Mozilla/5.0..."); - - switch (conn.getResponseCode()) { - case HttpURLConnection.HTTP_MOVED_PERM: - case HttpURLConnection.HTTP_MOVED_TEMP: - redLoc = conn.getHeaderField("Location"); - base = new URL(location); - next = new URL(base, redLoc); // Deal with relative URLs - location = next.toExternalForm(); - continue; - } - break; - } - return conn.getURL(); - } - - /** - * Remove possibly leftover files from the update folder. - */ - private void deleteOldFiles() { - //Just a quick check to make sure we didn't leave any files from last time... - File[] list = listFilesOrError(this.updateFolder); - for (final File xFile : list) { - if (xFile.getName().endsWith(".zip")) { - this.fileIOOrError(xFile, xFile.mkdir(), true); - } - } - } - - /** - * Part of Zip-File-Extractor, modified by Gravity for use with Updater. - * - * @param file the location of the file to extract. - */ - private void unzip(String file) { - final File fSourceZip = new File(file); - try { - final String zipPath = file.substring(0, file.length() - 4); - ZipFile zipFile = new ZipFile(fSourceZip); - Enumeration e = zipFile.entries(); - while (e.hasMoreElements()) { - ZipEntry entry = e.nextElement(); - File destinationFilePath = new File(zipPath, entry.getName()); - this.fileIOOrError(destinationFilePath.getParentFile(), - destinationFilePath.getParentFile().mkdirs(), true); - if (!entry.isDirectory()) { - final BufferedInputStream bis = - new BufferedInputStream(zipFile.getInputStream(entry)); - int b; - final byte[] buffer = new byte[Updater.BYTE_SIZE]; - final FileOutputStream fos = new FileOutputStream(destinationFilePath); - final BufferedOutputStream bos = - new BufferedOutputStream(fos, Updater.BYTE_SIZE); - while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) { - bos.write(buffer, 0, b); - } - bos.flush(); - bos.close(); - bis.close(); - final String name = destinationFilePath.getName(); - if (name.endsWith(".jar") && this.pluginExists(name)) { - File output = new File(this.updateFolder, name); - this.fileIOOrError(output, destinationFilePath.renameTo(output), true); - } - } - } - zipFile.close(); - - // Move any plugin data folders that were included to the right place, Bukkit won't do this for us. - moveNewZipFiles(zipPath); - - } catch (final IOException e) { - this.plugin.getLogger().log(Level.SEVERE, - "The auto-updater tried to unzip a new update file, but was unsuccessful.", e); - this.result = Updater.UpdateResult.FAIL_DOWNLOAD; - } finally { - this.fileIOOrError(fSourceZip, fSourceZip.delete(), false); - } - } - - /** - * Find any new files extracted from an update into the plugin's data directory. - * - * @param zipPath path of extracted files. - */ - private void moveNewZipFiles(String zipPath) { - File[] list = listFilesOrError(new File(zipPath)); - for (final File dFile : list) { - if (dFile.isDirectory() && this.pluginExists(dFile.getName())) { - // Current dir - final File oFile = - new File(this.plugin.getDataFolder().getParent(), dFile.getName()); - // List of existing files in the new dir - final File[] dList = listFilesOrError(dFile); - // List of existing files in the current dir - final File[] oList = listFilesOrError(oFile); - for (File cFile : dList) { - // Loop through all the files in the new dir - boolean found = false; - for (final File xFile : oList) { - // Loop through all the contents in the current dir to see if it exists - if (xFile.getName().equals(cFile.getName())) { - found = true; - break; - } - } - if (!found) { - // Move the new file into the current dir - File output = new File(oFile, cFile.getName()); - this.fileIOOrError(output, cFile.renameTo(output), true); - } else { - // This file already exists, so we don't need it anymore. - this.fileIOOrError(cFile, cFile.delete(), false); - } - } - } - this.fileIOOrError(dFile, dFile.delete(), false); - } - File zip = new File(zipPath); - this.fileIOOrError(zip, zip.delete(), false); - } - - /** - * Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip. - * - * @param name a name to check for inside the plugins folder. - * @return true if a file inside the plugins folder is named this. - */ - private boolean pluginExists(String name) { - File[] plugins = listFilesOrError(new File("plugins")); - for (final File file : plugins) { - if (file.getName().equals(name)) { - return true; - } - } - return false; - } - - /** - * Check to see if the program should continue by evaluating whether the plugin is already updated, or shouldn't be updated. - * - * @return true if the version was located and is not the same as the remote's newest. - */ - private boolean versionCheck() { - final String title = this.versionName; - if (this.type != UpdateType.NO_VERSION_CHECK) { - final String localVersion = this.plugin.getDescription().getVersion(); - if (title.split(DELIMETER).length >= 2) { - // Get the newest file's version number - final String remoteVersion = - title.split(DELIMETER)[title.split(DELIMETER).length - 1].split(" ")[0]; - - if (this.hasTag(localVersion) || !this.shouldUpdate(localVersion, remoteVersion)) { - // We already have the latest version, or this build is tagged for no-update - this.result = Updater.UpdateResult.NO_UPDATE; - return false; - } - } else { - // The file's name did not contain the string 'vVersion' - final String authorInfo = this.plugin.getDescription().getAuthors().isEmpty() ? - "" : - " (" + this.plugin.getDescription().getAuthors().get(0) + ")"; - this.plugin.getLogger().warning("The author of this plugin" + authorInfo - + " has misconfigured their Auto Update system"); - this.plugin.getLogger() - .warning("File versions should follow the format 'PluginName vVERSION'"); - this.plugin.getLogger().warning("Please notify the author of this error."); - this.result = Updater.UpdateResult.FAIL_NOVERSION; - return false; - } - } - return true; - } - - /** - * If you wish to run mathematical versioning checks, edit this method. - *

- * With default behavior, Updater will NOT verify that a remote version available on BukkitDev - * which is not this version is indeed an "update". - * If a version is present on BukkitDev that is not the version that is currently running, - * Updater will assume that it is a newer version. - * This is because there is no standard versioning scheme, and creating a calculation that can - * determine whether a new update is actually an update is sometimes extremely complicated. - *

- *

- * Updater will call this method from {@link #versionCheck()} before deciding whether - * the remote version is actually an update. - * If you have a specific versioning scheme with which a mathematical determination can - * be reliably made to decide whether one version is higher than another, you may - * revise this method, using the local and remote version parameters, to execute the - * appropriate check. - *

- *

- * Returning a value of false will tell the update process that this is NOT a new version. - * Without revision, this method will always consider a remote version at all different from - * that of the local version a new update. - *

- * - * @param localVersion the current version - * @param remoteVersion the remote version - * @return true if Updater should consider the remote version an update, false if not. - */ - public boolean shouldUpdate(String localVersion, String remoteVersion) { - return !localVersion.equalsIgnoreCase(remoteVersion); - } - - /** - * Evaluate whether the version number is marked showing that it should not be updated by this program. - * - * @param version a version number to check for tags in. - * @return true if updating should be disabled. - */ - private boolean hasTag(String version) { - for (final String string : Updater.NO_UPDATE_TAG) { - if (version.contains(string)) { - return true; - } - } - return false; - } - - /** - * Make a connection to the BukkitDev API and request the newest file's details. - * - * @return true if successful. - */ - private boolean read() { - try { - final URLConnection conn = this.url.openConnection(); - conn.setConnectTimeout(5000); - - if (this.apiKey != null) { - conn.addRequestProperty("X-API-Key", this.apiKey); - } - conn.addRequestProperty("User-Agent", Updater.USER_AGENT); - - conn.setDoOutput(true); - - final BufferedReader reader = - new BufferedReader(new InputStreamReader(conn.getInputStream())); - final String response = reader.readLine(); - - final JSONArray array = (JSONArray) JSONValue.parse(response); - - if (array.isEmpty()) { - this.plugin.getLogger() - .warning("The updater could not find any files for the project id " + this.id); - this.result = UpdateResult.FAIL_BADID; - return false; - } - - JSONObject latestUpdate = (JSONObject) array.get(array.size() - 1); - this.versionName = (String) latestUpdate.get(Updater.TITLE_VALUE); - this.versionLink = (String) latestUpdate.get(Updater.LINK_VALUE); - this.versionType = (String) latestUpdate.get(Updater.TYPE_VALUE); - this.versionGameVersion = (String) latestUpdate.get(Updater.VERSION_VALUE); - - return true; - } catch (final IOException e) { - if (e.getMessage().contains("HTTP response code: 403")) { - this.plugin.getLogger().severe( - "dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml"); - this.plugin.getLogger() - .severe("Please double-check your configuration to ensure it is correct."); - this.result = UpdateResult.FAIL_APIKEY; - } else { - this.plugin.getLogger() - .severe("The updater could not contact dev.bukkit.org for updating."); - this.plugin.getLogger().severe( - "If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime."); - this.result = UpdateResult.FAIL_DBO; - } - this.plugin.getLogger().log(Level.SEVERE, null, e); - return false; - } - } - - /** - * Perform a file operation and log any errors if it fails. - * - * @param file file operation is performed on. - * @param result result of file operation. - * @param create true if a file is being created, false if deleted. - */ - private void fileIOOrError(File file, boolean result, boolean create) { - if (!result) { - this.plugin.getLogger().severe( - "The updater could not " + (create ? "create" : "delete") + " file at: " + file - .getAbsolutePath()); - } - } - - private File[] listFilesOrError(File folder) { - File[] contents = folder.listFiles(); - if (contents == null) { - this.plugin.getLogger().severe( - "The updater could not access files at: " + this.updateFolder.getAbsolutePath()); - return new File[0]; - } else { - return contents; - } - } - - /** - * Called on main thread when the Updater has finished working, regardless - * of result. - */ - public interface UpdateCallback { - /** - * Called when the updater has finished working. - * - * @param updater The updater instance - */ - void onFinish(Updater updater); - } - - - private class UpdateRunnable implements Runnable { - @Override public void run() { - runUpdater(); - } - } - - private void runUpdater() { - if (this.url != null && (this.read() && this.versionCheck())) { - // Obtain the results of the project's file feed - if ((this.versionLink != null) && (this.type != UpdateType.NO_DOWNLOAD)) { - String name = this.file.getName(); - // If it's a zip file, it shouldn't be downloaded as the plugin's name - if (this.versionLink.endsWith(".zip")) { - name = this.versionLink.substring(this.versionLink.lastIndexOf("/") + 1); - } - this.saveFile(name); - } else { - this.result = UpdateResult.UPDATE_AVAILABLE; - } - } - - if (this.callback != null) { - new BukkitRunnable() { - @Override public void run() { - runCallback(); - } - }.runTask(this.plugin); - } - } - - private void runCallback() { - this.callback.onFinish(this); - } +// /* Constants */ +// +// // Remote file's title +// private static final String TITLE_VALUE = "name"; +// // Remote file's download link +// private static final String LINK_VALUE = "downloadUrl"; +// // Remote file's release type +// private static final String TYPE_VALUE = "releaseType"; +// // Remote file's build version +// private static final String VERSION_VALUE = "gameVersion"; +// // Path to GET +// private static final String QUERY = "/servermods/files?projectIds="; +// // Slugs will be appended to this to get to the project's RSS feed +// private static final String HOST = "https://api.curseforge.com"; +// // User-agent when querying Curse +// private static final String USER_AGENT = "Updater (by Gravity)"; +// // Used for locating version numbers in file names +// private static final String DELIMETER = "^v|[\\s_-]v"; +// // If the version number contains one of these, don't update. +// private static final String[] NO_UPDATE_TAG = {"-DEV", "-PRE", "SNAPSHOT"}; +// // Used for downloading files +// private static final int BYTE_SIZE = 1024; +// // Config key for api key +// private static final String API_KEY_CONFIG_KEY = "api-key"; +// // Config key for disabling Updater +// private static final String DISABLE_CONFIG_KEY = "disable"; +// // Default api key value in config +// private static final String API_KEY_DEFAULT = "PUT_API_KEY_HERE"; +// // Default disable value in config +// private static final boolean DISABLE_DEFAULT = false; +// +// /* User-provided variables */ +// +// // Plugin running Updater +// private final Plugin plugin; +// // Type of update check to run +// private final UpdateType type; +// // Whether to announce file downloads +// private final boolean announce; +// // The plugin file (jar) +// private final File file; +// // The folder that downloads will be placed in +// private final File updateFolder; +// // The provided callback (if any) +// private final UpdateCallback callback; +// // Project's Curse ID +// private int id = -1; +// // BukkitDev ServerMods API key +// private String apiKey = null; +// +// /* Collected from Curse API */ +// +// private String versionName; +// private String versionLink; +// private String versionType; +// private String versionGameVersion; +// +// /* Update process variables */ +// +// // Connection to RSS +// private URL url; +// // Updater thread +// private Thread thread; +// // Used for determining the outcome of the update process +// private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; +// +// +// /** +// * Gives the developer the result of the update process. Can be obtained by called {@link #getResult()} +// */ +// public enum UpdateResult { +// /** +// * The updater found an update, and has readied it to be loaded the next time the server restarts/reloads. +// */ +// SUCCESS, /** +// * The updater did not find an update, and nothing was downloaded. +// */ +// NO_UPDATE, /** +// * The server administrator has disabled the updating system. +// */ +// DISABLED, /** +// * The updater found an update, but was unable to download it. +// */ +// FAIL_DOWNLOAD, /** +// * For some reason, the updater was unable to contact dev.bukkit.org to download the file. +// */ +// FAIL_DBO, /** +// * When running the version check, the file on DBO did not contain a recognizable version. +// */ +// FAIL_NOVERSION, /** +// * The id provided by the plugin running the updater was invalid and doesn't exist on DBO. +// */ +// FAIL_BADID, /** +// * The server administrator has improperly configured their API key in the configuration. +// */ +// FAIL_APIKEY, /** +// * The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded. +// */ +// UPDATE_AVAILABLE +// } +// +// +// /** +// * Allows the developer to specify the type of update that will be run. +// */ +// public enum UpdateType { +// /** +// * Run a version check, and then if the file is out of date, download the newest version. +// */ +// DEFAULT, /** +// * Don't run a version check, just find the latest update and download it. +// */ +// NO_VERSION_CHECK, /** +// * Get information about the version and the download size, but don't actually download anything. +// */ +// NO_DOWNLOAD +// } +// +// +// /** +// * Represents the various release types of a file on BukkitDev. +// */ +// public enum ReleaseType { +// /** +// * An "alpha" file. +// */ +// ALPHA, /** +// * A "beta" file. +// */ +// BETA, /** +// * A "release" file. +// */ +// RELEASE +// } +// +// /** +// * Initialize the updater. +// * +// * @param plugin The plugin that is checking for an update. +// * @param id The dev.bukkit.org id of the project. +// * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. +// * @param type Specify the type of update this will be. See {@link UpdateType} +// * @param announce True if the program should announce the progress of new updates in console. +// */ +// public Updater(Plugin plugin, int id, File file, UpdateType type, boolean announce) { +// this(plugin, id, file, type, null, announce); +// } +// +// /** +// * Initialize the updater with the provided callback. +// * +// * @param plugin The plugin that is checking for an update. +// * @param id The dev.bukkit.org id of the project. +// * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. +// * @param type Specify the type of update this will be. See {@link UpdateType} +// * @param callback The callback instance to notify when the Updater has finished +// */ +// public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback) { +// this(plugin, id, file, type, callback, false); +// } +// +// /** +// * Initialize the updater with the provided callback. +// * +// * @param plugin The plugin that is checking for an update. +// * @param id The dev.bukkit.org id of the project. +// * @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class. +// * @param type Specify the type of update this will be. See {@link UpdateType} +// * @param callback The callback instance to notify when the Updater has finished +// * @param announce True if the program should announce the progress of new updates in console. +// */ +// public Updater(Plugin plugin, int id, File file, UpdateType type, UpdateCallback callback, +// boolean announce) { +// this.plugin = plugin; +// this.type = type; +// this.announce = announce; +// this.file = file; +// this.id = id; +// this.updateFolder = this.plugin.getServer().getUpdateFolderFile(); +// this.callback = callback; +// +// final File pluginFile = this.plugin.getDataFolder().getParentFile(); +// final File updaterFile = new File(pluginFile, "Updater"); +// final File updaterConfigFile = new File(updaterFile, "config.yml"); +// +// YamlConfiguration config = new YamlConfiguration(); +// config.options().header( +// "This configuration file affects all plugins using the Updater system (version 2+ - http://forums.bukkit.org/threads/96681/ )" +// + '\n' +// + "If you wish to use your API key, read http://wiki.bukkit.org/ServerMods_API and place it below." +// + '\n' +// + "Some updating systems will not adhere to the disabled value, but these may be turned off in their plugin's configuration."); +// config.addDefault(API_KEY_CONFIG_KEY, API_KEY_DEFAULT); +// config.addDefault(DISABLE_CONFIG_KEY, DISABLE_DEFAULT); +// +// if (!updaterFile.exists()) { +// this.fileIOOrError(updaterFile, updaterFile.mkdir(), true); +// } +// +// boolean createFile = !updaterConfigFile.exists(); +// try { +// if (createFile) { +// this.fileIOOrError(updaterConfigFile, updaterConfigFile.createNewFile(), true); +// config.options().copyDefaults(true); +// config.save(updaterConfigFile); +// } else { +// config.load(updaterConfigFile); +// } +// } catch (final Exception e) { +// final String message; +// if (createFile) { +// message = "The updater could not create configuration at " + updaterFile +// .getAbsolutePath(); +// } else { +// message = +// "The updater could not load configuration at " + updaterFile.getAbsolutePath(); +// } +// this.plugin.getLogger().log(Level.SEVERE, message, e); +// } +// +// if (config.getBoolean(DISABLE_CONFIG_KEY)) { +// this.result = UpdateResult.DISABLED; +// return; +// } +// +// String key = config.getString(API_KEY_CONFIG_KEY); +// if (API_KEY_DEFAULT.equalsIgnoreCase(key) || "".equals(key)) { +// key = null; +// } +// +// this.apiKey = key; +// +// try { +// this.url = new URL(Updater.HOST + Updater.QUERY + this.id); +// } catch (final MalformedURLException e) { +// this.plugin.getLogger().log(Level.SEVERE, +// "The project ID provided for updating, " + this.id + " is invalid.", e); +// this.result = UpdateResult.FAIL_BADID; +// } +// +// if (this.result != UpdateResult.FAIL_BADID) { +// this.thread = new Thread(new UpdateRunnable()); +// this.thread.start(); +// } else { +// runUpdater(); +// } +// } +// +// /** +// * Get the result of the update process. +// * +// * @return result of the update process. +// * @see UpdateResult +// */ +// public Updater.UpdateResult getResult() { +// this.waitForThread(); +// return this.result; +// } +// +// /** +// * Get the latest version's release type. +// * +// * @return latest version's release type. +// * @see ReleaseType +// */ +// public ReleaseType getLatestType() { +// this.waitForThread(); +// if (this.versionType != null) { +// for (ReleaseType type : ReleaseType.values()) { +// if (this.versionType.equalsIgnoreCase(type.name())) { +// return type; +// } +// } +// } +// return null; +// } +// +// /** +// * Get the latest version's game version (such as "CB 1.2.5-R1.0"). +// * +// * @return latest version's game version. +// */ +// public String getLatestGameVersion() { +// this.waitForThread(); +// return this.versionGameVersion; +// } +// +// /** +// * Get the latest version's name (such as "Project v1.0"). +// * +// * @return latest version's name. +// */ +// public String getLatestName() { +// this.waitForThread(); +// return this.versionName; +// } +// +// /** +// * Get the latest version's direct file link. +// * +// * @return latest version's file link. +// */ +// public String getLatestFileLink() { +// this.waitForThread(); +// return this.versionLink; +// } +// +// /** +// * As the result of Updater output depends on the thread's completion, it is necessary to wait for the thread to finish +// * before allowing anyone to check the result. +// */ +// private void waitForThread() { +// if ((this.thread != null) && this.thread.isAlive()) { +// try { +// this.thread.join(); +// } catch (final InterruptedException e) { +// this.plugin.getLogger().log(Level.SEVERE, null, e); +// } +// } +// } +// +// /** +// * Save an update from dev.bukkit.org into the server's update folder. +// * +// * @param file the name of the file to save it as. +// */ +// private void saveFile(String file) { +// final File folder = this.updateFolder; +// +// deleteOldFiles(); +// if (!folder.exists()) { +// this.fileIOOrError(folder, folder.mkdir(), true); +// } +// downloadFile(); +// +// // Check to see if it's a zip file, if it is, unzip it. +// final File dFile = new File(folder.getAbsolutePath(), file); +// if (dFile.getName().endsWith(".zip")) { +// // Unzip +// this.unzip(dFile.getAbsolutePath()); +// } +// if (this.announce) { +// this.plugin.getLogger().info("Finished updating."); +// } +// } +// +// /** +// * Download a file and save it to the specified folder. +// */ +// private void downloadFile() { +// BufferedInputStream in = null; +// FileOutputStream fout = null; +// try { +// URL fileUrl = followRedirects(this.versionLink); +// final int fileLength = fileUrl.openConnection().getContentLength(); +// in = new BufferedInputStream(fileUrl.openStream()); +// fout = new FileOutputStream(new File(this.updateFolder, file.getName())); +// +// final byte[] data = new byte[Updater.BYTE_SIZE]; +// int count; +// if (this.announce) { +// this.plugin.getLogger().info("About to download a new update: " + this.versionName); +// } +// long downloaded = 0; +// while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) { +// downloaded += count; +// fout.write(data, 0, count); +// final int percent = (int) ((downloaded * 100) / fileLength); +// if (this.announce && ((percent % 10) == 0)) { +// this.plugin.getLogger() +// .info("Downloading update: " + percent + "% of " + fileLength + " bytes."); +// } +// } +// } catch (Exception ex) { +// this.plugin.getLogger().log(Level.WARNING, +// "The auto-updater tried to download a new update, but was unsuccessful.", ex); +// this.result = Updater.UpdateResult.FAIL_DOWNLOAD; +// } finally { +// try { +// if (in != null) { +// in.close(); +// } +// } catch (final IOException ex) { +// this.plugin.getLogger().log(Level.SEVERE, null, ex); +// } +// try { +// if (fout != null) { +// fout.close(); +// } +// } catch (final IOException ex) { +// this.plugin.getLogger().log(Level.SEVERE, null, ex); +// } +// } +// } +// +// private URL followRedirects(String location) throws IOException { +// URL resourceUrl, base, next; +// HttpURLConnection conn; +// String redLoc; +// while (true) { +// resourceUrl = new URL(location); +// conn = (HttpURLConnection) resourceUrl.openConnection(); +// +// conn.setConnectTimeout(15000); +// conn.setReadTimeout(15000); +// conn.setInstanceFollowRedirects(false); +// conn.setRequestProperty("User-Agent", "Mozilla/5.0..."); +// +// switch (conn.getResponseCode()) { +// case HttpURLConnection.HTTP_MOVED_PERM: +// case HttpURLConnection.HTTP_MOVED_TEMP: +// redLoc = conn.getHeaderField("Location"); +// base = new URL(location); +// next = new URL(base, redLoc); // Deal with relative URLs +// location = next.toExternalForm(); +// continue; +// } +// break; +// } +// return conn.getURL(); +// } +// +// /** +// * Remove possibly leftover files from the update folder. +// */ +// private void deleteOldFiles() { +// //Just a quick check to make sure we didn't leave any files from last time... +// File[] list = listFilesOrError(this.updateFolder); +// for (final File xFile : list) { +// if (xFile.getName().endsWith(".zip")) { +// this.fileIOOrError(xFile, xFile.mkdir(), true); +// } +// } +// } +// +// /** +// * Part of Zip-File-Extractor, modified by Gravity for use with Updater. +// * +// * @param file the location of the file to extract. +// */ +// private void unzip(String file) { +// final File fSourceZip = new File(file); +// try { +// final String zipPath = file.substring(0, file.length() - 4); +// ZipFile zipFile = new ZipFile(fSourceZip); +// Enumeration e = zipFile.entries(); +// while (e.hasMoreElements()) { +// ZipEntry entry = e.nextElement(); +// File destinationFilePath = new File(zipPath, entry.getName());. + // The following "if" has been provided by Jonathan Leitschuh and + // addresses a zip-slip-vulnerability exploit with zip files. This exploit can take over + // a server. +// if (!destinationFilePath.toPath().normalize().startsWith(zipPath)) { +// throw new RuntimeException("Bad zip entry"); +// } +// this.fileIOOrError(destinationFilePath.getParentFile(), +// destinationFilePath.getParentFile().mkdirs(), true); +// if (!entry.isDirectory()) { +// final BufferedInputStream bis = +// new BufferedInputStream(zipFile.getInputStream(entry)); +// int b; +// final byte[] buffer = new byte[Updater.BYTE_SIZE]; +// final FileOutputStream fos = new FileOutputStream(destinationFilePath); +// final BufferedOutputStream bos = +// new BufferedOutputStream(fos, Updater.BYTE_SIZE); +// while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) { +// bos.write(buffer, 0, b); +// } +// bos.flush(); +// bos.close(); +// bis.close(); +// final String name = destinationFilePath.getName(); +// if (name.endsWith(".jar") && this.pluginExists(name)) { +// File output = new File(this.updateFolder, name); +// this.fileIOOrError(output, destinationFilePath.renameTo(output), true); +// } +// } +// } +// zipFile.close(); +// +// // Move any plugin data folders that were included to the right place, Bukkit won't do this for us. +// moveNewZipFiles(zipPath); +// +// } catch (final IOException e) { +// this.plugin.getLogger().log(Level.SEVERE, +// "The auto-updater tried to unzip a new update file, but was unsuccessful.", e); +// this.result = Updater.UpdateResult.FAIL_DOWNLOAD; +// } finally { +// this.fileIOOrError(fSourceZip, fSourceZip.delete(), false); +// } +// } +// +// /** +// * Find any new files extracted from an update into the plugin's data directory. +// * +// * @param zipPath path of extracted files. +// */ +// private void moveNewZipFiles(String zipPath) { +// File[] list = listFilesOrError(new File(zipPath)); +// for (final File dFile : list) { +// if (dFile.isDirectory() && this.pluginExists(dFile.getName())) { +// // Current dir +// final File oFile = +// new File(this.plugin.getDataFolder().getParent(), dFile.getName()); +// // List of existing files in the new dir +// final File[] dList = listFilesOrError(dFile); +// // List of existing files in the current dir +// final File[] oList = listFilesOrError(oFile); +// for (File cFile : dList) { +// // Loop through all the files in the new dir +// boolean found = false; +// for (final File xFile : oList) { +// // Loop through all the contents in the current dir to see if it exists +// if (xFile.getName().equals(cFile.getName())) { +// found = true; +// break; +// } +// } +// if (!found) { +// // Move the new file into the current dir +// File output = new File(oFile, cFile.getName()); +// this.fileIOOrError(output, cFile.renameTo(output), true); +// } else { +// // This file already exists, so we don't need it anymore. +// this.fileIOOrError(cFile, cFile.delete(), false); +// } +// } +// } +// this.fileIOOrError(dFile, dFile.delete(), false); +// } +// File zip = new File(zipPath); +// this.fileIOOrError(zip, zip.delete(), false); +// } +// +// /** +// * Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip. +// * +// * @param name a name to check for inside the plugins folder. +// * @return true if a file inside the plugins folder is named this. +// */ +// private boolean pluginExists(String name) { +// File[] plugins = listFilesOrError(new File("plugins")); +// for (final File file : plugins) { +// if (file.getName().equals(name)) { +// return true; +// } +// } +// return false; +// } +// +// /** +// * Check to see if the program should continue by evaluating whether the plugin is already updated, or shouldn't be updated. +// * +// * @return true if the version was located and is not the same as the remote's newest. +// */ +// private boolean versionCheck() { +// final String title = this.versionName; +// if (this.type != UpdateType.NO_VERSION_CHECK) { +// final String localVersion = this.plugin.getDescription().getVersion(); +// if (title.split(DELIMETER).length >= 2) { +// // Get the newest file's version number +// final String remoteVersion = +// title.split(DELIMETER)[title.split(DELIMETER).length - 1].split(" ")[0]; +// +// if (this.hasTag(localVersion) || !this.shouldUpdate(localVersion, remoteVersion)) { +// // We already have the latest version, or this build is tagged for no-update +// this.result = Updater.UpdateResult.NO_UPDATE; +// return false; +// } +// } else { +// // The file's name did not contain the string 'vVersion' +// final String authorInfo = this.plugin.getDescription().getAuthors().isEmpty() ? +// "" : +// " (" + this.plugin.getDescription().getAuthors().get(0) + ")"; +// this.plugin.getLogger().warning("The author of this plugin" + authorInfo +// + " has misconfigured their Auto Update system"); +// this.plugin.getLogger() +// .warning("File versions should follow the format 'PluginName vVERSION'"); +// this.plugin.getLogger().warning("Please notify the author of this error."); +// this.result = Updater.UpdateResult.FAIL_NOVERSION; +// return false; +// } +// } +// return true; +// } +// +// /** +// * If you wish to run mathematical versioning checks, edit this method. +// *

+// * With default behavior, Updater will NOT verify that a remote version available on BukkitDev +// * which is not this version is indeed an "update". +// * If a version is present on BukkitDev that is not the version that is currently running, +// * Updater will assume that it is a newer version. +// * This is because there is no standard versioning scheme, and creating a calculation that can +// * determine whether a new update is actually an update is sometimes extremely complicated. +// *

+// *

+// * Updater will call this method from {@link #versionCheck()} before deciding whether +// * the remote version is actually an update. +// * If you have a specific versioning scheme with which a mathematical determination can +// * be reliably made to decide whether one version is higher than another, you may +// * revise this method, using the local and remote version parameters, to execute the +// * appropriate check. +// *

+// *

+// * Returning a value of false will tell the update process that this is NOT a new version. +// * Without revision, this method will always consider a remote version at all different from +// * that of the local version a new update. +// *

+// * +// * @param localVersion the current version +// * @param remoteVersion the remote version +// * @return true if Updater should consider the remote version an update, false if not. +// */ +// public boolean shouldUpdate(String localVersion, String remoteVersion) { +// return !localVersion.equalsIgnoreCase(remoteVersion); +// } +// +// /** +// * Evaluate whether the version number is marked showing that it should not be updated by this program. +// * +// * @param version a version number to check for tags in. +// * @return true if updating should be disabled. +// */ +// private boolean hasTag(String version) { +// for (final String string : Updater.NO_UPDATE_TAG) { +// if (version.contains(string)) { +// return true; +// } +// } +// return false; +// } +// +// /** +// * Make a connection to the BukkitDev API and request the newest file's details. +// * +// * @return true if successful. +// */ +// private boolean read() { +// try { +// final URLConnection conn = this.url.openConnection(); +// conn.setConnectTimeout(5000); +// +// if (this.apiKey != null) { +// conn.addRequestProperty("X-API-Key", this.apiKey); +// } +// conn.addRequestProperty("User-Agent", Updater.USER_AGENT); +// +// conn.setDoOutput(true); +// +// final BufferedReader reader = +// new BufferedReader(new InputStreamReader(conn.getInputStream())); +// final String response = reader.readLine(); +// +// final JSONArray array = (JSONArray) JSONValue.parse(response); +// +// if (array.isEmpty()) { +// this.plugin.getLogger() +// .warning("The updater could not find any files for the project id " + this.id); +// this.result = UpdateResult.FAIL_BADID; +// return false; +// } +// +// JSONObject latestUpdate = (JSONObject) array.get(array.size() - 1); +// this.versionName = (String) latestUpdate.get(Updater.TITLE_VALUE); +// this.versionLink = (String) latestUpdate.get(Updater.LINK_VALUE); +// this.versionType = (String) latestUpdate.get(Updater.TYPE_VALUE); +// this.versionGameVersion = (String) latestUpdate.get(Updater.VERSION_VALUE); +// +// return true; +// } catch (final IOException e) { +// if (e.getMessage().contains("HTTP response code: 403")) { +// this.plugin.getLogger().severe( +// "dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml"); +// this.plugin.getLogger() +// .severe("Please double-check your configuration to ensure it is correct."); +// this.result = UpdateResult.FAIL_APIKEY; +// } else { +// this.plugin.getLogger() +// .severe("The updater could not contact dev.bukkit.org for updating."); +// this.plugin.getLogger().severe( +// "If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime."); +// this.result = UpdateResult.FAIL_DBO; +// } +// this.plugin.getLogger().log(Level.SEVERE, null, e); +// return false; +// } +// } +// +// /** +// * Perform a file operation and log any errors if it fails. +// * +// * @param file file operation is performed on. +// * @param result result of file operation. +// * @param create true if a file is being created, false if deleted. +// */ +// private void fileIOOrError(File file, boolean result, boolean create) { +// if (!result) { +// this.plugin.getLogger().severe( +// "The updater could not " + (create ? "create" : "delete") + " file at: " + file +// .getAbsolutePath()); +// } +// } +// +// private File[] listFilesOrError(File folder) { +// File[] contents = folder.listFiles(); +// if (contents == null) { +// this.plugin.getLogger().severe( +// "The updater could not access files at: " + this.updateFolder.getAbsolutePath()); +// return new File[0]; +// } else { +// return contents; +// } +// } +// +// /** +// * Called on main thread when the Updater has finished working, regardless +// * of result. +// */ +// public interface UpdateCallback { +// /** +// * Called when the updater has finished working. +// * +// * @param updater The updater instance +// */ +// void onFinish(Updater updater); +// } +// +// +// private class UpdateRunnable implements Runnable { +// @Override public void run() { +// runUpdater(); +// } +// } +// +// private void runUpdater() { +// if (this.url != null && (this.read() && this.versionCheck())) { +// // Obtain the results of the project's file feed +// if ((this.versionLink != null) && (this.type != UpdateType.NO_DOWNLOAD)) { +// String name = this.file.getName(); +// // If it's a zip file, it shouldn't be downloaded as the plugin's name +// if (this.versionLink.endsWith(".zip")) { +// name = this.versionLink.substring(this.versionLink.lastIndexOf("/") + 1); +// } +// this.saveFile(name); +// } else { +// this.result = UpdateResult.UPDATE_AVAILABLE; +// } +// } +// +// if (this.callback != null) { +// new BukkitRunnable() { +// @Override public void run() { +// runCallback(); +// } +// }.runTask(this.plugin); +// } +// } +// +// private void runCallback() { +// this.callback.onFinish(this); +// } } \ No newline at end of file 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 index 702545522..1d0ca15ef 100644 --- 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 @@ -544,20 +544,25 @@ protected int autoPickup( PrisonMinesBlockBreakEvent pmEvent, long nanoTime = 0L; + boolean isSellallEnabled = SpigotPrison.getInstance().isSellAllEnabled(); + + // This is true if the player cannot toggle the autosell, and it's + // true if they can, and the have it enabled: + boolean isPlayerAutosellEnabled = isSellallEnabled && + SellAllUtil.get() != null && + SellAllUtil.get().checkIfPlayerAutosellIsActive( + pmEvent.getSpigotPlayer().getWrapper() ) + ; + + for ( SpigotItemStack itemStack : drops ) { count += itemStack.getAmount(); - - // This is true if the player cannot toggle the autosell, and it's - // true if they can, and the have it enabled: - boolean isPlayerAutosellEnabled = SellAllUtil.get() != null && - SellAllUtil.get().checkIfPlayerAutosellIsActive( - pmEvent.getSpigotPlayer().getWrapper() ) - ; // Try to autosell if enabled: - if ( Prison.get().getPlatform().getConfigBooleanFalse( "sellall" ) && + if ( isSellallEnabled && + (isBoolean(AutoFeatures.isAutoSellPerBlockBreakEnabled) && isPlayerAutosellEnabled || pmEvent.isForceAutoSell() || @@ -597,7 +602,7 @@ protected int autoPickup( PrisonMinesBlockBreakEvent pmEvent, // it will have an amount of more than 0. if ( itemStack.getAmount() > 0 ) { - if ( Output.get().isDebug() && Prison.get().getPlatform().getConfigBooleanFalse( "sellall" ) ) { + if ( Output.get().isDebug() && isSellallEnabled ) { // Just get the calculated value for the drops... do not sell: double amount = SellAllUtil.get().getSellMoney( player, itemStack ); @@ -996,7 +1001,8 @@ protected void dropExtra( HashMap extra, Player player player.getInventory().firstEmpty() == -1 )) { - if ( Prison.get().getPlatform().getConfigBooleanFalse( "sellall" ) ) { +// if ( SpigotPrison.getInstance().isSellAllEnabled() ) + { SellAllUtil sellAllUtil = SellAllUtil.get(); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventCore.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventCore.java index 029a3a130..5f8dbed56 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventCore.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/OnBlockBreakEventCore.java @@ -677,7 +677,7 @@ else if ( targetExplodedBlock.isMined() ) { // AutoSell on full inventory when using BLOCKEVENTS: if ( isBoolean( AutoFeatures.isAutoSellIfInventoryIsFullForBLOCKEVENTSPriority ) && - Prison.get().getPlatform().getConfigBooleanFalse( "sellall" ) && + SpigotPrison.getInstance().isSellAllEnabled() && isPlayerAutosellEnabled && pmEvent.getSpigotPlayer().isInventoryFull() ) { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotItemStack.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotItemStack.java index e61a1af9f..3f7ca702b 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotItemStack.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/block/SpigotItemStack.java @@ -40,14 +40,17 @@ public SpigotItemStack( org.bukkit.inventory.ItemStack bukkitStack ) private void setupBukkitStack( org.bukkit.inventory.ItemStack bukkitStack ) { XMaterial xMat = null; - try { - xMat = XMaterial.matchXMaterial( bukkitStack ); - } catch (Exception e) { + if ( bukkitStack != null ) { - String message = String.format( - "Unsupported ItemStack type: %s", - e.getMessage() ); - throw new PrisonItemStackNotSupportedRuntimeException( message ); + try { + xMat = XMaterial.matchXMaterial( bukkitStack ); + } catch (Exception e) { + + String message = String.format( + "Unsupported ItemStack type: %s", + e.getMessage() ); + throw new PrisonItemStackNotSupportedRuntimeException( message ); + } } // if ( xMat != XMaterial.AIR ) { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/bstats/PrisonBStats.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/bstats/PrisonBStats.java new file mode 100644 index 000000000..b92915184 --- /dev/null +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/bstats/PrisonBStats.java @@ -0,0 +1,653 @@ +package tech.mcprison.prison.spigot.bstats; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.bstats.bukkit.Metrics; +import org.bstats.charts.DrilldownPie; +import org.bstats.charts.SimpleBarChart; +import org.bstats.charts.SimplePie; + +import tech.mcprison.prison.Prison; +import tech.mcprison.prison.PrisonAPI; +import tech.mcprison.prison.PrisonCommand.RegisteredPluginsData; +import tech.mcprison.prison.autofeatures.AutoFeaturesFileConfig; +import tech.mcprison.prison.autofeatures.AutoFeaturesWrapper; +import tech.mcprison.prison.integration.Integration; +import tech.mcprison.prison.integration.IntegrationManager; +import tech.mcprison.prison.integration.IntegrationType; +import tech.mcprison.prison.mines.PrisonMines; +import tech.mcprison.prison.modules.Module; +import tech.mcprison.prison.ranks.PrisonRanks; +import tech.mcprison.prison.ranks.data.TopNPlayers; +import tech.mcprison.prison.spigot.SpigotPrison; + +public class PrisonBStats { + + private SpigotPrison spigotPrison; + + private Metrics bStatsMetrics = null; + + private List reportPrisonCore; + private List reportPermissions; + private List reportEconomy; + private List reportPlaceholders; +// private List reportVault; + private List reportEnchantments; + private List reportAdminTools; + + + private TreeSet pluginsUsed; + + + public PrisonBStats( SpigotPrison spigotPrison ) { + super(); + + this.spigotPrison = spigotPrison; + + + this.reportPrisonCore = new ArrayList<>(); + this.reportPermissions = new ArrayList<>(); + + this.reportEconomy = new ArrayList<>(); + this.reportPlaceholders = new ArrayList<>(); +// this.reportVault = new ArrayList<>(); + this.reportEnchantments = new ArrayList<>(); + this.reportAdminTools = new ArrayList<>(); + + this.pluginsUsed = new TreeSet<>(); + + + setupPluginReports(); + } + + + + /** + *

bStats reporting

+ * + * https://github.com/Bastian/bStats-Metrics/tree/master/base/src/main/java/org/bstats/charts + * + */ + public void initMetricsOnLoad() { + if (!spigotPrison.getConfig().getBoolean("send-metrics", true)) { + return; // Don't check if they don't want it + } + + int pluginId = 657; + setbStatsMetrics( new Metrics( spigotPrison, pluginId ) );; +// bStatsMetrics = new PrisonMetrics( this, pluginId ); + +// Metrics metrics = new Metrics( this, pluginId ); + } + + public void initMetricsOnEnable() { + if (!spigotPrison.getConfig().getBoolean("send-metrics", true)) { + return; // Don't check if they don't want it + } + + + if ( getbStatsMetrics() == null ) { + int pluginId = 657; + + setbStatsMetrics( new Metrics( spigotPrison, pluginId ) ); +// bStatsMetrics = new PrisonMetrics( this, pluginId ); + } + + // Report the modules being used + SimpleBarChart sbcModulesUsed = new SimpleBarChart("modules_used", () -> { + Map valueMap = new HashMap<>(); + for (Module m : PrisonAPI.getModuleManager().getModules()) { + valueMap.put(m.getName(), 1); + } + return valueMap; + }); + getbStatsMetrics().addCustomChart( sbcModulesUsed ); + + // Report the API level + SimplePie spApiLevel = + new SimplePie("api_level", () -> + "API Level " + Prison.API_LEVEL + "." + Prison.API_LEVEL_MINOR ); + getbStatsMetrics().addCustomChart( spApiLevel ); + + + Optional prisonMinesOpt = Prison.get().getModuleManager().getModule( PrisonMines.MODULE_NAME ); + Optional prisonRanksOpt = Prison.get().getModuleManager().getModule( PrisonRanks.MODULE_NAME ); + + + // delete: "prison_ranks" + // delete: "mines_ranks_and_ladders" +// "prison_default_rank_counts" +// "prison_prestiges_rank_counts" +// "prison_other_rank_counts" +// "prison_total_rank_counts" + +// "prison_total_ladder_counts" +// "prison_total_mine_counts" + +// "prison_total_active_player_counts" +// "prison_total_archived_player_counts" +// "prison_total_player_counts" + +// "prison_languages" + + // Note that the functions that provide the values to these arrow functions must get the values from within them + // or they will never update their values when bstats runs them during their intervals!! + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_default_rank_counts", () -> { + int defaultRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getDefaultLadderRankCount()).orElse(0); + return Integer.toString( defaultRankCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_prestiges_rank_counts", () -> { + int prestigesRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPrestigesLadderRankCount()).orElse(0); + return Integer.toString( prestigesRankCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_other_rank_counts", () -> { + int rankCountTotal = prisonRanksOpt.map(module -> ((PrisonRanks) module).getRankCount()).orElse(0); + int defaultRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getDefaultLadderRankCount()).orElse(0); + int prestigesRankCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPrestigesLadderRankCount()).orElse(0); + + int otherRankCount = rankCountTotal - defaultRankCount - prestigesRankCount; + return Integer.toString( otherRankCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_rank_counts", () -> { + int rankCountTotal = prisonRanksOpt.map(module -> ((PrisonRanks) module).getRankCount()).orElse(0); + return Integer.toString( rankCountTotal ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_ladder_counts", () -> { + int ladderCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getladderCount()).orElse(0); + return Integer.toString( ladderCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_mine_counts", () -> { + + int mineCount = prisonMinesOpt.map(module -> ((PrisonMines) module).getMineManager().getMines().size()).orElse(0); + return Integer.toString( mineCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_player_counts", () -> { + + int playerCount = prisonRanksOpt.map(module -> ((PrisonRanks) module).getPlayersCount()).orElse(0); + return Integer.toString( playerCount ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_active_player_counts", () -> { + + int playerCountActive = TopNPlayers.getInstance().getTopNSize(); + return Integer.toString( playerCountActive ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_total_archived_player_counts", () -> { + + int playerCountArchived = TopNPlayers.getInstance().getArchivedSize(); + return Integer.toString( playerCountArchived ); + }) ); + + getbStatsMetrics().addCustomChart( new SimplePie( "prison_languages", () -> { + return Prison.get().getPlatform().getConfigString( "default-language", "en_US" ); + }) ); + + + + +// DrilldownPie mlcPrisonRanksAndLadders = new DrilldownPie("mines_ranks_and_ladders", () -> { +// Map> map = new HashMap<>(); +// +// Map ranks = new HashMap<>(); +// ranks.put( Integer.toString( mineCount ), 1 ); +// map.put( "mines", ranks ); +// +// Map defRanks = new HashMap<>(); +// defRanks.put( Integer.toString( rankCount ), 1 ); +// map.put( "ranks", defRanks ); +// +// Map prestigesRanks = new HashMap<>(); +// prestigesRanks.put( Integer.toString( ladderCount ), 1 ); +// map.put( "ladders", prestigesRanks ); +// +// Map otherRanks = new HashMap<>(); +// otherRanks.put( Integer.toString( playerCount ), 1 ); +// map.put( "players", otherRanks ); +// +// return map; +// }); +// getbStatsMetrics().addCustomChart( mlcPrisonRanksAndLadders ); + +// MultiLineChart mlcMinesRanksAndLadders = +// new 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); +// valueMap.put("players", playerCount); +// return valueMap; +// } +// }); +// bStatsMetrics.addCustomChart( mlcMinesRanksAndLadders ); + + + +// DrilldownPie mlcPrisonPrisonRanks = new DrilldownPie("prison_ranks", () -> { +// Map> map = new HashMap<>(); +// +// Map ranks = new HashMap<>(); +// ranks.put( Integer.toString( rankCount ), 1 ); +// map.put( "ranks", ranks ); +// +// Map defRanks = new HashMap<>(); +// defRanks.put( Integer.toString( defaultRankCount ), 1 ); +// map.put( "defaultRanks", defRanks ); +// +// Map prestigesRanks = new HashMap<>(); +// prestigesRanks.put( Integer.toString( prestigesRankCount ), 1 ); +// map.put( "prestigesRanks", prestigesRanks ); +// +// Map otherRanks = new HashMap<>(); +// otherRanks.put( Integer.toString( otherRankCount ), 1 ); +// map.put( "otherRanks", otherRanks ); +// +// return map; +// }); +// getbStatsMetrics().addCustomChart( mlcPrisonPrisonRanks ); + +// MultiLineChart mlcPrisonRanks = new MultiLineChart("prison_ranks", new Callable>() { +// @Override +// public Map call() throws Exception { +// Map valueMap = new HashMap<>(); +// valueMap.put("ranks", rankCount); +// valueMap.put("defaultRanks", defaultRankCount); +// valueMap.put("prestigesRanks", prestigesRankCount); +// valueMap.put("otherRanks", otherRankCount); +// return valueMap; +// } +// }); +// bStatsMetrics.addCustomChart( mlcPrisonRanks ); + + + // remove "prison_ladders" since the many are duplicated and we should not have personal ladder names: +// DrilldownPie mlcPrisonPrisonLadders = new DrilldownPie("prison_ladders", () -> { +// Map> map = new HashMap<>(); +// +// +// PrisonRanks pRanks = (PrisonRanks) prisonRanksOpt.orElseGet( null ); +// for ( RankLadder ladder : pRanks.getLadderManager().getLadders() ) { +// +// Map entry = new HashMap<>(); +// entry.put( Integer.toString( ladder.getRanks().size() ), 1 ); +// +// map.put( ladder.getName(), entry ); +// } +// +// return map; +// }); +// getbStatsMetrics().addCustomChart( mlcPrisonPrisonLadders ); + + +// MultiLineChart mlcPrisonladders = new MultiLineChart("prison_ladders", new Callable>() { +// @Override +// public Map call() throws Exception { +// Map valueMap = new HashMap<>(); +// +// PrisonRanks pRanks = (PrisonRanks) prisonRanksOpt.orElseGet( null ); +// for ( RankLadder ladder : pRanks.getLadderManager().getLadders() ) { +// +// valueMap.put( ladder.getName(), ladder.getRanks().size() ); +// } +// +// return valueMap; +// } +// }); +// bStatsMetrics.addCustomChart( mlcPrisonladders ); + + TreeMap plugins = Prison.get().getPrisonCommands().getRegisteredPluginData(); + + + createNewBstatReport( "core_prison_plugins", reportPrisonCore, plugins, pluginsUsed ); + createNewBstatReport( "permission_plugins", reportPermissions, plugins, pluginsUsed ); + createNewBstatReport( "economy_plugins", reportEconomy, plugins, pluginsUsed ); + + createNewBstatReport( "placeholder_plugins", reportPlaceholders, plugins, pluginsUsed ); + createNewBstatReport( "enchantment_plugins", reportEnchantments, plugins, pluginsUsed ); + createNewBstatReport( "admin_tools_plugins", reportAdminTools, plugins, pluginsUsed ); + + + + //this.reportVault = new ArrayList<>(); "prison_integrated_vault_plugins" + + + TreeMap pluginsAtoE = getSubsetOfPlugins(plugins, 'a', 'f', false, pluginsUsed ); + TreeMap pluginsFtoM = getSubsetOfPlugins(plugins, 'f', 'n', false, pluginsUsed ); + TreeMap pluginsNtoS = getSubsetOfPlugins(plugins, 'n', 't', false, pluginsUsed ); + TreeMap pluginsTto9 = getSubsetOfPlugins(plugins, 't', 'z', true, pluginsUsed ); + + // Remove "plugins" - Too much info on one report +// DrilldownPie mlcPrisonPlugins = new DrilldownPie("plugins", () -> { +// Map> map = new HashMap<>(); +// +// for (String pluginName : plugins.keySet() ) { +// RegisteredPluginsData pluginData = plugins.get( pluginName ); +// +// Map entry = new HashMap<>(); +// entry.put( pluginData.getPluginVersion(), 1 ); +// +// map.put( pluginData.getPluginName(), entry ); +// } +// +// return map; +// }); +// getbStatsMetrics().addCustomChart( mlcPrisonPlugins ); + + + + DrilldownPie mlcPrisonVaultPlugins = new DrilldownPie("prison_vault_plugins", () -> { + Map> map = new HashMap<>(); + + + IntegrationManager im = Prison.get().getIntegrationManager(); + + Set inTypeKeys = im.getIntegrations().keySet(); + for (IntegrationType inTypeKey : inTypeKeys ) { + List integrations = im.getIntegrations().get( inTypeKey ); + + for (Integration integration : integrations) { + + if ( integration.getDisplayName().toLowerCase().contains( "(vault)") ) { + + Map entry = new HashMap<>(); + entry.put( integration.getDisplayName(), 1 ); + + map.put( integration.getType().name(), entry ); + } + + } + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonVaultPlugins ); + + + DrilldownPie mlcPrisonPluginsAtoE = new DrilldownPie("plugins_a_to_e", () -> { + Map> map = new HashMap<>(); + + for (String pluginName : pluginsAtoE.keySet() ) { + RegisteredPluginsData pluginData = pluginsAtoE.get( pluginName ); + + Map entry = new HashMap<>(); + entry.put( pluginData.getPluginVersion(), 1 ); + + map.put( pluginData.getPluginName(), entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonPluginsAtoE ); + + + DrilldownPie mlcPrisonPluginsFtoM = new DrilldownPie("plugins_f_to_m", () -> { + Map> map = new HashMap<>(); + + for (String pluginName : pluginsFtoM.keySet() ) { + RegisteredPluginsData pluginData = pluginsFtoM.get( pluginName ); + + Map entry = new HashMap<>(); + entry.put( pluginData.getPluginVersion(), 1 ); + + map.put( pluginData.getPluginName(), entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonPluginsFtoM ); + + + DrilldownPie mlcPrisonPluginsNtoS = new DrilldownPie("plugins_n_to_s", () -> { + Map> map = new HashMap<>(); + + for (String pluginName : pluginsNtoS.keySet() ) { + RegisteredPluginsData pluginData = pluginsNtoS.get( pluginName ); + + Map entry = new HashMap<>(); + entry.put( pluginData.getPluginVersion(), 1 ); + + map.put( pluginData.getPluginName(), entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonPluginsNtoS ); + + + DrilldownPie mlcPrisonPluginsTto9 = new DrilldownPie("plugins_t_to_z_plus_others", () -> { + Map> map = new HashMap<>(); + + for (String pluginName : pluginsTto9.keySet() ) { + RegisteredPluginsData pluginData = pluginsTto9.get( pluginName ); + + Map entry = new HashMap<>(); + entry.put( pluginData.getPluginVersion(), 1 ); + + map.put( pluginData.getPluginName(), entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonPluginsTto9 ); + + + + AutoFeaturesFileConfig afConfig = AutoFeaturesWrapper.getInstance().getAutoFeaturesConfig(); + TreeMap autofeaturesBstats = afConfig.getBstatsDetails(); + + DrilldownPie mlcPrisonAutofeatures = new DrilldownPie("autofeatures", () -> { + Map> map = new HashMap<>(); + + for (String feature : autofeaturesBstats.keySet() ) { + String detail = autofeaturesBstats.get( feature ); + + Map entry = new HashMap<>(); + entry.put( detail, 1 ); + + map.put( feature, entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( mlcPrisonAutofeatures ); + + + + } + + /** + *

This function will split up a list of active plugins in to sub-groups. + * This is controlled by the rangeLow through rangeHigh parameters. + * The parameter includeNonAlpha will include all other plugins where their + * names do not begin with an alpha character; this is a catch-all to prevent plugins + * from being omitted. + *

+ * + *

The parameter pluginsUsed is a set plugins that have already been + * included in other reports so therefore should be omitted from these reports. + *

+ * + * @param plugins + * @param rangeLow + * @param rangeHigh + * @param includeNonAlpha + * @param pluginsUsed + * @return + */ + private TreeMap getSubsetOfPlugins( + TreeMap plugins, + char rangeLow, char rangeHigh, + boolean includeNonAlpha, + TreeSet pluginsUsed ) { + + TreeMap results = new TreeMap<>(); + + Set keys = plugins.keySet(); + for (String key : keys) { + + if ( !pluginsUsed.contains( key ) ) { + + char keyFirstChar = key.toLowerCase().charAt(0); + + if ( Character.isAlphabetic(keyFirstChar) ) { + + if ( Character.compare(keyFirstChar, rangeLow) >= 0 && Character.compare( keyFirstChar, rangeHigh) < 0 ) { + + results.put( key, plugins.get(key) ); + } + } + else { + + // Add all non-alpha plugins to this result: + results.put( key, plugins.get(key) ); + } + } + + } + + return results; + } + + + /** + *

Most of the new reports falls in to specific formats for report plugins. + * This function uses the pattern to generate the report of interest. + *

+ * + * @param reportName + * @param pluginsInThisReport + * @param plugins + * @param pluginsUsed + */ + private void createNewBstatReport( String reportName, List pluginsInThisReport, + TreeMap plugins, TreeSet pluginsUsed ) { + + + DrilldownPie report = new DrilldownPie( reportName, () -> { + Map> map = new HashMap<>(); + + for (String pluginName : pluginsInThisReport ) { + + pluginsUsed.add( pluginName ); + + RegisteredPluginsData pluginData = plugins.get( pluginName ); + Map entry = new HashMap<>(); + + String detailNameVersion = null; + + if ( pluginData != null ) { + + detailNameVersion = pluginName + ": " + pluginData.getPluginVersion(); + } + else { + detailNameVersion = pluginName + ": Not Installed"; + } + + entry.put( detailNameVersion, 1 ); + + map.put( pluginName, entry ); + } + + return map; + }); + getbStatsMetrics().addCustomChart( report ); + + + } + + private void setupPluginReports() { + + reportPrisonCore.add( "Prison" ); + reportPrisonCore.add( "Essentials" ); + reportPrisonCore.add( "Vault" ); + reportPrisonCore.add( "ProtocolLib" ); + reportPrisonCore.add( "Minepacks" ); + reportPrisonCore.add( "CustomItems" ); + + reportPermissions.add( "GroupManager" ); + reportPermissions.add( "GroupManagerX" ); + reportPermissions.add( "LuckPerms" ); + reportPermissions.add( "LPC" ); + reportPermissions.add( "PermissionsEx" ); + + reportEconomy.add( "Essentials" ); + reportEconomy.add( "Economy_CMI" ); + reportEconomy.add( "GemsEconomy" ); + reportEconomy.add( "SDFEconomy" ); + reportEconomy.add( "SaneEconomy" ); + reportEconomy.add( "Tokens" ); + reportEconomy.add( "Ultimate_Economy" ); + reportEconomy.add( "XConomy" ); + + + reportPlaceholders.add( "PlaceholderAPI" ); + reportPlaceholders.add( "MVdWPlaceholderAPI" ); + + reportPlaceholders.add( "AnimatedScoreboard" ); + reportPlaceholders.add( "DecentHolograms" ); + reportPlaceholders.add( "DeluxeMenus" ); + reportPlaceholders.add( "EssentialsChat" ); + reportPlaceholders.add( "HolographicDisplays" ); + reportPlaceholders.add( "HolographicExtension" ); + reportPlaceholders.add( "RealScoreboard" ); + reportPlaceholders.add( "Scoreboard-revision" ); + reportPlaceholders.add( "RealScoreboard" ); + reportPlaceholders.add( "TAB" ); + reportPlaceholders.add( "TabList" ); + + + //reportVault.add( "" ); + + + reportEnchantments.add( "AdvancedEnchantmens" ); + reportEnchantments.add( "CrazyEnchantments" ); + reportEnchantments.add( "TokenEnchant" ); + reportEnchantments.add( "TimTheEnchanter" ); + reportEnchantments.add( "Zenchantments" ); + reportEnchantments.add( "PrisonEnchants" ); + reportEnchantments.add( "RevEnchants" ); + + + reportAdminTools.add( "WorldEdit" ); + reportAdminTools.add( "WorldGuard" ); + reportAdminTools.add( "Multiverse-Core" ); + reportAdminTools.add( "Multiverse" ); + reportAdminTools.add( "Multiworld" ); + reportAdminTools.add( "MyCommand" ); + reportAdminTools.add( "CMI" ); + reportAdminTools.add( "CMIEInjector" ); + reportAdminTools.add( "FastAsyncWorldEdit" ); + reportAdminTools.add( "VoidGen" ); + reportAdminTools.add( "Skript" ); + reportAdminTools.add( "PlugMan" ); // Just to get an idea of how many may be causing issues + reportAdminTools.add( "ViaBackwards" ); + reportAdminTools.add( "ViaRewind" ); + reportAdminTools.add( "ViaVersion" ); + reportAdminTools.add( "Citizens" ); + reportAdminTools.add( "NBTAPI" ); + + } + public Metrics getbStatsMetrics() { + return bStatsMetrics; + } + + public void setbStatsMetrics(Metrics bStatsMetrics) { + this.bStatsMetrics = bStatsMetrics; + } +} diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotSellAllCommands.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotSellAllCommands.java index 07f13d03b..4c376d10b 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotSellAllCommands.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/commands/PrisonSpigotSellAllCommands.java @@ -2,7 +2,6 @@ import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.HashMap; import java.util.Set; import java.util.TreeMap; @@ -44,7 +43,8 @@ public class PrisonSpigotSellAllCommands extends PrisonSpigotBaseCommands { * Check if SellAll's enabled. * */ public static boolean isEnabled(){ - return SpigotPrison.getInstance().getConfig().getString("sellall").equalsIgnoreCase("true"); + + return SpigotPrison.getInstance().isSellAllEnabled(); } /** 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 ca416693a..2740820fd 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 @@ -49,6 +49,8 @@ public interface Compatibility public SpigotItemStack getPrisonItemInMainHand(PlayerInteractEvent e); public SpigotItemStack getPrisonItemInMainHand(Player player); + + public SpigotItemStack getPrisonItemInOffHand(Player player); public ItemStack getItemInOffHand(PlayerInteractEvent 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 index 50b260f85..e0d296255 100644 --- 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 @@ -49,6 +49,11 @@ public SpigotItemStack getPrisonItemInMainHand(PlayerInteractEvent e) { public SpigotItemStack getPrisonItemInMainHand(Player player) { return SpigotUtil.bukkitItemStackToPrison( getItemInMainHand( player ) ); } + + @Override + public SpigotItemStack getPrisonItemInOffHand(Player player) { + return SpigotUtil.bukkitItemStackToPrison( getItemInOffHand( player ) ); + } @Override public ItemStack getItemInOffHand(PlayerInteractEvent e) { @@ -80,8 +85,10 @@ public void setItemInMainHand(Player p, ItemStack itemStack){ @Override public void setItemStackInOffHand( SpigotPlayerInventory inventory, SpigotItemStack itemStack ) { + ItemStack iStack = itemStack == null ? null : itemStack.getBukkitStack(); + ((org.bukkit.inventory.PlayerInventory) inventory.getWrapper()) - .setItemInOffHand( itemStack.getBukkitStack() ); + .setItemInOffHand( iStack ); } @Override 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 c22439a8b..2c0f89864 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 @@ -68,6 +68,10 @@ public SpigotItemStack getPrisonItemInMainHand(PlayerInteractEvent e) { public SpigotItemStack getPrisonItemInMainHand(Player player) { return SpigotUtil.bukkitItemStackToPrison( getItemInMainHand( player ) ); } + + public SpigotItemStack getPrisonItemInOffHand(Player player) { + return SpigotUtil.bukkitItemStackToPrison( getItemInMainHand( player ) ); + } @Override public ItemStack getItemInOffHand(PlayerInteractEvent e) { 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 018c72b46..5b3a2e2d5 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 @@ -72,6 +72,11 @@ public SpigotItemStack getPrisonItemInMainHand(PlayerInteractEvent e) { public SpigotItemStack getPrisonItemInMainHand(Player player) { return SpigotUtil.bukkitItemStackToPrison( getItemInMainHand( player ) ); } + + @Override + public SpigotItemStack getPrisonItemInOffHand(Player player) { + return SpigotUtil.bukkitItemStackToPrison( getItemInOffHand( player ) ); + } @Override public ItemStack getItemInOffHand(PlayerInteractEvent e) { @@ -102,9 +107,11 @@ public void setItemInMainHand(Player p, ItemStack itemStack) { @Override public void setItemStackInOffHand( SpigotPlayerInventory inventory, SpigotItemStack itemStack ) { + + ItemStack iStack = itemStack == null ? null : itemStack.getBukkitStack(); ((org.bukkit.inventory.PlayerInventory) inventory.getWrapper()) - .setItemInOffHand( itemStack.getBukkitStack() ); + .setItemInOffHand( iStack ); } @Override public void playIronDoorSound(Location loc) { 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 index 29d5d1a6d..ac9f715c6 100644 --- 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 @@ -12,6 +12,7 @@ import tech.mcprison.prison.cache.PlayerCache; import tech.mcprison.prison.cache.PlayerCachePlayerData; +import tech.mcprison.prison.file.JsonFileIO; import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.OfflineMcPlayer; import tech.mcprison.prison.internal.inventory.Inventory; @@ -33,6 +34,26 @@ public SpigotOfflinePlayer(OfflinePlayer offlinePlayer) { public String getName() { return offlinePlayer.getName(); } + + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public String getPlayerFileName() { + + return JsonFileIO.getPlayerFileName( this ); + } @Override public String toString() { diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotPlayer.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotPlayer.java index 929741a8a..0c13c3c9f 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotPlayer.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/game/SpigotPlayer.java @@ -32,6 +32,7 @@ import tech.mcprison.prison.autofeatures.PlayerMessaging.MessageType; import tech.mcprison.prison.cache.PlayerCache; import tech.mcprison.prison.cache.PlayerCachePlayerData; +import tech.mcprison.prison.file.JsonFileIO; import tech.mcprison.prison.internal.ItemStack; import tech.mcprison.prison.internal.Player; import tech.mcprison.prison.internal.inventory.Inventory; @@ -64,6 +65,26 @@ public SpigotPlayer(org.bukkit.entity.Player bukkitPlayer) { this.bukkitPlayer = bukkitPlayer; } + /** + *

This constructs a player file named based upon the UUID followed + * by the player's name. This format is used so it's easier to identify + * the correct player. + *

+ * + *

The format should be UUID-PlayerName.json. The UUID is a shortened + * format, which should still produce a unique id. The name, when read, + * is based upon the UUID and not the player's name, which may change. + * This format includes the player's name to make it easier to identify + * who's record is whom's. + *

+ * + * @return + */ + public String getPlayerFileName() { + + return JsonFileIO.getPlayerFileName( this ); + } + @Override public UUID getUUID() { return bukkitPlayer.getUniqueId(); 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 62352df0f..dd11d45d9 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 @@ -86,57 +86,6 @@ public Block getBlockAt( Location location, boolean containsCustomBlocks ) { } return getBlockAtLocation.getBlockAt(location, containsCustomBlocks, this); - -// SpigotBlock sBlock = null; -// -// if ( location != null ) { -// -// org.bukkit.Location bLocation = getBukkitLocation( location ); -// org.bukkit.block.Block bBlock = bukkitWorld.getBlockAt( bLocation ); -// -// -// sBlock = SpigotCompatibility.getInstance().getSpigotBlock( bBlock ); -// -// if ( sBlock == null ) { -// -// sBlock = new SpigotBlock( bBlock, PrisonBlock.AIR.clone() ); -// } -// -// -// if ( containsCustomBlocks ) { -// -// List cbIntegrations = -// PrisonAPI.getIntegrationManager().getCustomBlockIntegrations(); -// -// for ( CustomBlockIntegration customBlock : cbIntegrations ) -// { -// PrisonBlock pBlock = customBlock.getCustomBlock( sBlock ); -// -// if ( pBlock != null ) { -// -// //if ( Output.get().isDebug() ) -//// { -//// -//// String message = String.format( -//// "SpigotWorld.getBlockAt: customBlock: %s " + -//// "spigot: %s bukkit: %s", -//// pBlock.getBlockName(), sBlock.getBlockName(), -//// bBlock.getType().name() ); -//// -//// Output.get().logInfo( message ); -//// } -// -// sBlock.setBlockName( pBlock.getBlockName() ); -// sBlock.setBlockType( customBlock.getBlockType() ); -// break; -// } -// } -// } -// -// -// } -// -// return sBlock; } public Block getBlockAt( Location location ) { @@ -210,73 +159,6 @@ public void setBlocksSynchronously( List tBlocks, MineRes } setBlockSync.setBlocksSynchronously(tBlocks, resetType, nanos, this ); -// List tBlocksCloned = new ArrayList<>(); -// for ( MineTargetPrisonBlock mtpb : tBlocks ) { -// -// tBlocksCloned.add( mtpb ); -// } -// -// new BukkitRunnable() { -// @Override -// public void run() { -// -// long start = System.nanoTime(); -// -// MineTargetPrisonBlock current = null; -// try -// { -// for ( MineTargetPrisonBlock tBlock : tBlocksCloned ) -// { -// current = tBlock; -// -// if ( tBlock != null && tBlock.getLocation() != null ) { -// -// final PrisonBlock pBlock = tBlock.getPrisonBlock( resetType ); -// -// if ( pBlock != null ) { -// -// Location location = tBlock.getLocation(); -// -// SpigotBlock sBlock = (SpigotBlock) getBlockAt( location ); -//// SpigotBlock sBlock = (SpigotBlock) location.getBlockAt(); -// -// sBlock.setPrisonBlock( pBlock ); -// } -// } -// -// } -// } -// catch ( Exception e ) { -// -// if ( current != null ) { -// -// String blkName = current.getPrisonBlock().getBlockName(); -// PrisonBlock pBlock = current.getPrisonBlock( resetType ); -// String resetTypeBlockName = pBlock == null ? "null" : pBlock.getBlockName(); -// -// Output.get().logError( -// String.format( "SpigotWorld.setBlocksSynchronously Exception: %s resetType: %s %s :: %s", -// blkName, resetType.name(), resetTypeBlockName, e.getMessage() ), e ); -// } -// else { -// -// Output.get().logError( -// String.format( "SpigotWorld.setBlocksSynchronously Exception: --noBlock-- resetType: %s " + -// "[unable to set 'current'] :: %s", -// resetType.name(), e.getMessage() ), e ); -// } -// } -// -// long elapsedNanos = System.nanoTime() - start; -// -// -// if ( nanos != null ) { -// nanos.addNanos( elapsedNanos ); -// } -// -// } -// }.runTaskLater( SpigotPrison.getInstance(), 0 ); - } public org.bukkit.World getWrapper() { 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 index 73dfc830f..1bc40011e 100644 --- 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 @@ -1832,8 +1832,8 @@ private void prestigeConfirmationGUI(InventoryClickEvent e, Player p, String but 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]; + String buttonName = ( parts.length >= 1 ? parts[0] : ""); + String rankName = (parts.length >= 2 ? parts[1] : "-rankHasNoName-"); // Get the rank. Rank rank = PrisonRanks.getInstance().getRankManager().getRank(rankName); @@ -1871,7 +1871,9 @@ private void rankManagerGUI(InventoryClickEvent e, Player p, String[] parts) { PlayerRank pRank = rankPlayerFactory.getRank( rankPlayer, rank.getLadder() ); - SpigotRankPriceGUI gui = new SpigotRankPriceGUI(p, pRank.getRankCost().intValue(), rank.getName()); + int costInt = pRank == null || pRank.getRankCost() == null ? 0 : pRank.getRankCost().intValue(); + + SpigotRankPriceGUI gui = new SpigotRankPriceGUI(p, costInt, rank.getName()); gui.open(); } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/guiutility/SpigotGUIComponents.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/guiutility/SpigotGUIComponents.java index 275aa48f0..8e059a685 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/guiutility/SpigotGUIComponents.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/gui/guiutility/SpigotGUIComponents.java @@ -215,8 +215,10 @@ public static void updateMessages(){ * */ public static void updateSellAllConfig(){ SellAllUtil util = SpigotPrison.getInstance().getSellAllUtil(); - util.updateConfig(); - sellAllConfig = util.sellAllConfig; + if ( util != null ) { + util.updateConfig(); + sellAllConfig = util.sellAllConfig; + } } public static void updateGUIConfig(){ 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 index 60bd4f6f1..6967f8c0d 100644 --- 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 @@ -183,12 +183,14 @@ public void open() { for (String stringValue : mineLore) { - double volume = m.getBounds().getArea(); + double volume = ( m.isVirtual() ? 0 : m.getBounds().getTotalBlockCount() ); double remaining = volume * m.getPercentRemainingBlockCount() / 100.0; + String dimensions = ( m.isVirtual() ? "virtual" : m.getBounds().getDimensions() ); + stringValue = stringValue.replace( "{mineName}", mineName ); stringValue = stringValue.replace( "{mineTag}", mineTag ); - stringValue = stringValue.replace( "{mineSize}", m.getBounds().getDimensions() ); + stringValue = stringValue.replace( "{mineSize}", dimensions ); stringValue = stringValue.replace( "{mineVolume}", iFmt.format( volume )); stringValue = stringValue.replace( "{mineRemaining}", iFmt.format( remaining )); stringValue = stringValue.replace( "{mineRemainingPercent}", iFmt.format( m.getPercentRemainingBlockCount() )); 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 index 5db020331..f66041e75 100644 --- 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 @@ -52,7 +52,7 @@ public void open(){ LadderManager lm = PrisonRanks.getInstance().getLadderManager(); // If the inventory is empty - if (lm.getLadders().size() == 0){ + if (lm == null || lm.getLadders().size() == 0){ Output.get().sendWarn(new SpigotPlayer(p), messages.getString(MessagesConfig.StringID.spigot_message_gui_ladder_empty)); p.closeInventory(); return; 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 index 19a216808..ab41bf0c6 100644 --- 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 @@ -25,7 +25,6 @@ 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.RankPlayerFactory; import tech.mcprison.prison.ranks.managers.LadderManager; import tech.mcprison.prison.ranks.managers.PlayerManager; import tech.mcprison.prison.spigot.SpigotPrison; @@ -151,7 +150,7 @@ public void open() { // Get many parameters - RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); +// RankPlayerFactory rankPlayerFactory = new RankPlayerFactory(); // Rank rank = ladder.getLowestRank().get(); // PlayerRank playerRankRank = rankPlayerFactory.getRank( getRankPlayer(), guiConfig.getString("Options.Ranks.Ladder")); @@ -183,7 +182,7 @@ public void open() { DecimalFormat mFmt = new DecimalFormat("###,##0.0000"); boolean showNumber = getBoolean(guiConfig.getString("Options.Ranks.Number_of_Rank_Player_GUI")); - PlayerRank pRank = rankPlayerFactory.getRank( getRankPlayer(), ladder, true ); +// PlayerRank pRank = rankPlayerFactory.getRank( getRankPlayer(), ladder, true ); for ( Rank rank : ranksDisplay ) { @@ -197,7 +196,9 @@ public void open() { ButtonLore ranksLore = new ButtonLore(); - PlayerRank calPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, rank ); + PlayerRank calPRank = rankPlayer.calculateTargetPlayerRank( rank ); +// PlayerRank calPRank = pRank.getTargetPlayerRankForPlayer( rankPlayer, rank ); + double rankPrice = calPRank.getRankCost(); double rankMultiplier = calPRank.getRankMultiplier(); 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 index 6453f3019..214fc1a87 100644 --- 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 @@ -41,7 +41,7 @@ public SellAllAdminGUI( Player p, int page, String cmdPage, String cmdReturn ) { public void open() { - if (!SpigotPrison.getInstance().getConfig().getString("sellall").equalsIgnoreCase("true")){ + if ( !SpigotPrison.getInstance().isSellAllEnabled() ){ new SpigotVariousGuiMessages().sellallIsDisabledMsg( new SpigotCommandSender(p) ); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationUppercaseWrapper.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationUppercaseWrapper.java index 156c4479e..1672ced92 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationUppercaseWrapper.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/placeholder/PlaceHolderAPIIntegrationUppercaseWrapper.java @@ -8,6 +8,7 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.placeholders.PlaceholderManager; +import tech.mcprison.prison.util.ChatColor; public class PlaceHolderAPIIntegrationUppercaseWrapper extends PlaceholderExpansion @@ -86,6 +87,6 @@ public String onRequest(OfflinePlayer player, String identifier) { .placeholderTranslate( playerUuid, player.getName(), identifier ); - return results; + return ChatColor.translateAlternateColorCodes( '&', results); } } 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 index 897fe35ed..ff3b13354 100644 --- 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 @@ -8,6 +8,7 @@ import tech.mcprison.prison.Prison; import tech.mcprison.prison.PrisonAPI; import tech.mcprison.prison.placeholders.PlaceholderManager; +import tech.mcprison.prison.util.ChatColor; public class PlaceHolderAPIIntegrationWrapper extends PlaceholderExpansion @@ -83,7 +84,7 @@ public String onRequest(OfflinePlayer player, String identifier) { UUID playerUuid = player.getUniqueId(); String results = Prison.get().getPlatform().getPlaceholders() .placeholderTranslate( playerUuid, player.getName(), identifier ); - - return results; + + return ChatColor.translateAlternateColorCodes( '&', 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 index ac7a3c4ea..f9400dee3 100644 --- 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 @@ -470,6 +470,7 @@ public List placeholderSearch( UUID playerUuid, String playerName, Strin PlaceholderIdentifier identifier = new PlaceholderIdentifier( placeholder ); identifier.setPlayer(playerUuid, playerName); + identifier.setPlaceholderKey(placeHolderKey); String value = processPlaceholderHavePlaceholderKey( identifier ); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllUtil.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllUtil.java index 0b7acb87d..66296e8c2 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllUtil.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/sellall/SellAllUtil.java @@ -57,9 +57,6 @@ public class SellAllUtil private static SellAllUtil instance; - private static final boolean isEnabled = Prison.get().getPlatform().getConfigBooleanFalse( "sellall" ); - -// private static final boolean isEnabled = getBoolean(SpigotPrison.getInstance().getConfig().getString("sellall")); private final Compatibility compat = SpigotPrison.getInstance().getCompatibility(); private final ItemStack lapisLazuli = compat.getLapisItemStack(); @@ -122,7 +119,7 @@ public class SellAllUtil * @return SellAllUtil. * */ public static SellAllUtil get() { - if (!isEnabled){ + if ( !SpigotPrison.getInstance().isSellAllEnabled() ){ return null; } if (instance == null){ diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/tasks/SpigotPrisonDelayedStartupTask.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/tasks/SpigotPrisonDelayedStartupTask.java index 62bddf8cf..c1a8604cf 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/tasks/SpigotPrisonDelayedStartupTask.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/tasks/SpigotPrisonDelayedStartupTask.java @@ -21,6 +21,7 @@ public class SpigotPrisonDelayedStartupTask private long cooldownInTicks; private int maxAttempts; private String targetVaultEconomyName; + private boolean useAnyVaultEconomy = false; private final SpigotPrison prison; private Economy econ = null; @@ -50,7 +51,8 @@ public void submit() { if ( isVaultEconomyIntegrated() && econ != null && econ.isEnabled() && vaultEconomyName != null && - ( vaultEconomyName.equalsIgnoreCase( targetVaultEconomyName ) || + ( isUseAnyVaultEconomy() || + vaultEconomyName.equalsIgnoreCase( targetVaultEconomyName ) || isEssentialsEconomy( vaultEconomyName ) && isEssentialsEconomy( targetVaultEconomyName ) ) ) { @@ -58,8 +60,9 @@ public void submit() { // It's enabled now, so don't submit, just go ahead and startup prison: Output.get().logInfo( - String.format( "&7Prison Delayed Enablement: &3A Vault economy is available. " + - "Skipping delayed start. Starting Prison now." )); + String.format( "&7Prison Delayed Enablement: &3A Vault economy is available: %s. " + + "Skipping delayed start. Starting Prison now.", + vaultEconomyName )); prison.onEnableStartup(); @@ -182,4 +185,12 @@ public void run() { } } + + public boolean isUseAnyVaultEconomy() { + return useAnyVaultEconomy; + } + public void setUseAnyVaultEconomy(boolean useAnyVaultEconomy) { + this.useAnyVaultEconomy = useAnyVaultEconomy; + } + } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonBombListener.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonBombListener.java index cb53d2b58..ed2dc5c3e 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonBombListener.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonBombListener.java @@ -8,11 +8,14 @@ import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; import de.tr7zw.nbtapi.NBTItem; import tech.mcprison.prison.bombs.MineBombData; import tech.mcprison.prison.bombs.MineBombs; +import tech.mcprison.prison.mines.data.Mine; import tech.mcprison.prison.output.Output; +import tech.mcprison.prison.spigot.block.OnBlockBreakMines; import tech.mcprison.prison.spigot.block.SpigotBlock; import tech.mcprison.prison.spigot.game.SpigotPlayer; import tech.mcprison.prison.util.Location; @@ -34,10 +37,14 @@ public class PrisonBombListener private PrisonUtilsMineBombs prisonUtilsMineBombs; + private OnBlockBreakMines blockBreakMines; + public PrisonBombListener( PrisonUtilsMineBombs utilsMineBombs ) { super(); - prisonUtilsMineBombs = utilsMineBombs; + this.prisonUtilsMineBombs = utilsMineBombs; + + this.blockBreakMines = new OnBlockBreakMines(); } @EventHandler( priority = EventPriority.LOW ) @@ -48,6 +55,7 @@ public void onInteract( PlayerInteractEvent event ) { //Output.get().logInfo( "### PrisonBombListener: PlayerInteractEvent 01 " ); + if ( (event.getAction().equals(Action.RIGHT_CLICK_BLOCK) || event.getAction().equals(Action.RIGHT_CLICK_AIR)) && event.getItem() != null && event.getItem().getType() != Material.AIR ) { @@ -61,16 +69,17 @@ public void onInteract( PlayerInteractEvent event ) { // NOTE: Because we're just checking, do not auto update the itemstack. NBTItem nbtItem = new NBTItem( event.getItem() ); - if ( Output.get().isDebug() && nbtItem != null ) { - Output.get().logInfo( "PrisonBombListener.onInteract ntb: %s", nbtItem.toString() ); - } - if ( !nbtItem.hasKey( MineBombs.MINE_BOMBS_NBT_BOMB_KEY ) ) { return; } String bombName = nbtItem.getString( MineBombs.MINE_BOMBS_NBT_BOMB_KEY ); + if ( Output.get().isDebug() && nbtItem != null ) { + Output.get().logInfo( "PrisonBombListener.onInteract ntb: %s :: %s", + bombName, nbtItem.toString() ); + } + Player player = event.getPlayer(); // // Temp test stuff... remove when NBTs are working: @@ -101,11 +110,11 @@ public void onInteract( PlayerInteractEvent event ) { SpigotBlock sBlock = null; + SpigotPlayer sPlayer = new SpigotPlayer( player ); // If clicking AIR, then event.getClickedBlock() will be null... // so if null, then use the player's location for placing the bomb. if ( event.getClickedBlock() == null ) { - SpigotPlayer sPlayer = new SpigotPlayer( player ); Location loc = sPlayer.getLocation(); // Get the block 3 away from the player, in the direction (vector) in which @@ -117,9 +126,24 @@ public void onInteract( PlayerInteractEvent event ) { } + Mine mine = blockBreakMines.findMine(player, sBlock, null, null); + if ( mine == null ) { + // player is not in a mine, so do not allow them to trigger a mine bomb: + + event.setCancelled( true ); + return; + } + else if ( !mine.hasMiningAccess( sPlayer ) ) { + // Player does not have access to the mine, so don't allow them to trigger a mine bomb: + + event.setCancelled( true ); + return; + } + + EquipmentSlot hand = event.getHand(); // Output.get().logInfo( "### PrisonBombListener: PlayerInteractEvent 02 " ); - if ( getPrisonUtilsMineBombs().setBombInHand( player, bomb, sBlock ) ) { + if ( getPrisonUtilsMineBombs().setBombInHand( player, bomb, sBlock, hand ) ) { // The item was a bomb and it was activated. // Cancel the event so the item will not be placed or processed farther. diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMessages.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMessages.java index 5aba64a99..5469bee41 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMessages.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMessages.java @@ -8,6 +8,7 @@ import tech.mcprison.prison.internal.CommandSender; import tech.mcprison.prison.output.Output; import tech.mcprison.prison.spigot.game.SpigotPlayer; +import tech.mcprison.prison.util.Text; public class PrisonUtilsMessages extends PrisonUtils @@ -86,7 +87,9 @@ public void utilMessageBroadcast(CommandSender sender, } else { - Bukkit.getServer().broadcastMessage( msg ); + String message = Text.translateAmpColorCodes( msg ); + + Bukkit.getServer().broadcastMessage( message ); } } diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombs.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombs.java index 6f58d9379..198a7ede8 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombs.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombs.java @@ -10,6 +10,7 @@ import org.bukkit.Effect; import org.bukkit.Particle; import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import com.cryptomorin.xseries.XMaterial; @@ -36,6 +37,7 @@ import tech.mcprison.prison.spigot.compat.SpigotCompatibility; import tech.mcprison.prison.spigot.game.SpigotPlayer; import tech.mcprison.prison.spigot.game.SpigotWorld; +import tech.mcprison.prison.spigot.inventory.SpigotPlayerInventory; import tech.mcprison.prison.spigot.spiget.BluesSpigetSemVerComparator; import tech.mcprison.prison.util.Location; import tech.mcprison.prison.util.Text; @@ -669,7 +671,7 @@ public static ItemStack getItemStackBomb( MineBombData bombData ) { nbtItem = new NBTItem( sItemStack, true ); nbtItem.setString( MineBombs.MINE_BOMBS_NBT_BOMB_KEY, bombData.getName() ); - if ( Output.get().isDebug() && nbtItem != null ) { + if ( Output.get().isDebug() && nbtItem != null && nbtItem.toString() != null ) { Output.get().logInfo( "getItemStackBombs ntb: %s", nbtItem.toString() ); } } @@ -742,7 +744,8 @@ public MineBombData getBombItem( String bombName ) { * @param bomb * @return */ - public boolean setBombInHand( Player player, MineBombData bomb, SpigotBlock sBlock ) { + public boolean setBombInHand( Player player, + MineBombData bomb, SpigotBlock sBlock, EquipmentSlot hand ) { boolean isABomb = false; // MineBombData bomb = getBombItem( player ); @@ -874,19 +877,36 @@ else if ( bomb != null ) { bomb.setActivated( true ); + SpigotItemStack itemInHand = + hand == EquipmentSlot.HAND ? + SpigotCompatibility.getInstance().getPrisonItemInMainHand( player ) : + SpigotCompatibility.getInstance().getPrisonItemInOffHand( player ) + ; - SpigotItemStack itemInHand = SpigotCompatibility.getInstance().getPrisonItemInMainHand( player ); - // Remove from inventory: int inHandBombCount = itemInHand.getAmount() - 1; if ( inHandBombCount == 0 ) { - SpigotCompatibility.getInstance() - .setItemInMainHand( player, null ); + if ( hand == EquipmentSlot.HAND ) { + + SpigotCompatibility.getInstance() + .setItemInMainHand( player, null ); + } + else { + + SpigotPlayerInventory sInventory = (SpigotPlayerInventory) sPlayer.getInventory(); + SpigotItemStack sItemStack = null; + + SpigotCompatibility.getInstance() + .setItemStackInOffHand( sInventory, sItemStack ); + } } else { itemInHand.setAmount( inHandBombCount ); } + + + // Apply updates to the player's inventory: player.updateInventory(); diff --git a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombsTasks.java b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombsTasks.java index fc96653e5..071101d6a 100644 --- a/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombsTasks.java +++ b/prison-spigot/src/main/java/tech/mcprison/prison/spigot/utils/PrisonUtilsMineBombsTasks.java @@ -552,7 +552,7 @@ private void initializeArmorStand() { if ( tagName.contains( "{name}" ) ) { tagName = tagName.replace( "{name}", bomb.getName() ); } - this.tagName = Text.convertToAmpColorCodes( tagName ); + this.tagName = Text.translateAmpColorCodes( tagName ); //updateArmorStandCustomName(); armorStand.setCustomName( this.tagName ); diff --git a/prison-spigot/src/main/resources/config.yml b/prison-spigot/src/main/resources/config.yml index e1c5f8f01..a7b6c7414 100644 --- a/prison-spigot/src/main/resources/config.yml +++ b/prison-spigot/src/main/resources/config.yml @@ -206,6 +206,13 @@ prison-mines: # any other economy plugins using vault too. But please do not use this # unless all other options have been exhusted. # +# NOTE: Due to the high degree of CMI Economies forcing the use of this delayed +# start, if delayed start is disabled and if Prison detects that CMI is enabled, +# Prison will now force a delayed start with the any vault economy being the +# target. This means as long as vault has an active economy, then prison will +# start. IF the vault economy is found right away, then no dealy startup will +# be submitted, and normal startup will continue right away. +# # To use the delayed startup: # 1. delayedPrisonStartup.enabled: true # 2. Optional: cooldown-secs and max-attemps identifies how frequently and how @@ -247,3 +254,39 @@ prisonCommandHandler: tp: - mines teleport + + + +# +# topN Player Settings: +# +# The refresh settings are for an async task that will be running to +# provide updates to the topN player stats. Only online players are +# updated. Offline players only have their stats calculated once, then +# they are ignored for refresh processing. +# +# The delay-ticks setting is the delay that prison waits before starting +# to run the topN refresh for the first time. By default, it's 3 minutes. +# +# The interval-ticks is the time between runs, with 5 minutes being the +# default value. +#' +# topNPlayers.refesh.delay-ticks: 600 (30 seconds) +# topNPlayers.refresh.interval-ticks: 6000 (5 minutes) +# +# Archive cutoff-days is the number of days that a player must be inactive +# on prison before they are moved to the archival vaults. Any player that +# has been archived will not be included in the topN calculations. If +# a player is archived, then they return, they will become active and +# their old stats will apply +# +# topNPlayers.archive.cutoff-days: 90 (90 days) + +topNPlayers: + archive: + cutoff-days: 90 + refresh: + delay-ticks: 600 + interval-ticks: 6000 + +