├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── help.md │ ├── report-a-bug.md │ └── request-a-feature.md └── workflows │ ├── call.github_release.yml │ ├── call.platform_uploads.yml │ ├── dispatch.platform_uploads.yml │ ├── dispatch.promote_release.yml │ ├── main.prerelease.yml │ ├── pr.require_label.yml │ └── pr.test.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── build.gradle ├── config ├── multiverse-banner.png └── mv_checks.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── org │ └── mvplugins │ └── multiverse │ └── portals │ ├── BstatsMetricsConfigurator.java │ ├── MVPortal.java │ ├── MultiversePortals.java │ ├── MultiversePortalsApi.java │ ├── MultiversePortalsPluginBinder.java │ ├── PortalLocation.java │ ├── PortalPlayerSession.java │ ├── WorldEditConnection.java │ ├── command │ ├── PortalsCommandCompletions.java │ └── PortalsCommandContexts.java │ ├── commands │ ├── ConfigCommand.java │ ├── CreateCommand.java │ ├── DebugCommand.java │ ├── InfoCommand.java │ ├── ListCommand.java │ ├── ModifyCommand.java │ ├── PortalsCommand.java │ ├── RemoveCommand.java │ ├── SelectCommand.java │ └── WandCommand.java │ ├── destination │ ├── PortalDestination.java │ ├── PortalDestinationInstance.java │ ├── RandomPortalDestination.java │ └── RandomPortalDestinationInstance.java │ ├── enums │ ├── MoveType.java │ ├── PortalConfigProperty.java │ ├── PortalType.java │ └── SetProperties.java │ ├── event │ └── MVPortalEvent.java │ ├── listeners │ ├── MVPBlockListener.java │ ├── MVPCoreListener.java │ ├── MVPPlayerListener.java │ ├── MVPPlayerMoveListener.java │ ├── MVPVehicleListener.java │ ├── PlayerListenerHelper.java │ └── PortalsListener.java │ └── utils │ ├── DisplayUtils.java │ ├── MultiverseRegion.java │ ├── PortalFiller.java │ └── PortalManager.java └── resources ├── defaults ├── config.yml └── portals.yml └── plugin.yml /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 ​Multiverse Discord Server 4 | url: https://discord.gg/NZtfKky 5 | about: Need help with using Multiverse-Portals or just want to chat with the devs? Join the Multiverse Discord Server! 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ❓ Help! 3 | about: Encountered a problem with Multiverse-Portals? Not sure how to fix it? 4 | title: '' 5 | labels: 'Assistance' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 37 | 38 | ### Information 39 | 40 | * **Server version:** 41 | 42 | * **Full output of `/mv version -p`:** 43 | 44 | * **Server log:** 45 | 46 | ### Help request 47 | 48 | **Problem** 49 | 50 | 51 | **What I have tried** 52 | 53 | 54 | **Screenshots** 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report-a-bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Report a Bug 3 | about: Report a Multiverse-Portals bug. Only use this if you're 100% sure it's something wrong with Multiverse-Portals - otherwise, try "Help!". 4 | title: '' 5 | labels: 'Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 37 | 38 | ### Information 39 | 40 | * **Server version:** 41 | 42 | * **Full output of `/mv version -p`:** 43 | 44 | * **Server log:** 45 | 46 | ### Details 47 | I was **``** to reproduce my issue on a freshly setup and up-to-date server with the latest version of Multiverse plugins with no other plugins and with no kinds of other server or client mods. 48 | 49 | **Description** 50 | 51 | 52 | **Steps to reproduce** 53 | 54 | 55 | **Expected behavior** 56 | 57 | 58 | **Screenshots** 59 | 60 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/request-a-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Request a Feature 3 | about: Suggest a feature you want to see in Multiverse-Portals! 4 | title: '' 5 | labels: 'Suggestion' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 47 | 48 | ### Feature request 49 | 50 | **Feature description** 51 | 52 | 53 | **How the feature is useful** 54 | -------------------------------------------------------------------------------- /.github/workflows/call.github_release.yml: -------------------------------------------------------------------------------- 1 | name: 'Call: GitHub Release' 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | release_mode: 7 | description: 'Release mode' 8 | required: true 9 | type: string 10 | version_bump: 11 | description: 'Version bump' 12 | required: false 13 | type: string 14 | promote_from: 15 | description: 'Promote from' 16 | required: false 17 | type: string 18 | outputs: 19 | release_created: 20 | description: 'Release created' 21 | value: ${{ jobs.github_release.outputs.release_created }} 22 | tag_name: 23 | description: 'Tag name' 24 | value: ${{ jobs.github_release.outputs.tag_name }} 25 | 26 | jobs: 27 | github_release: 28 | uses: Multiverse/Multiverse-Core/.github/workflows/generic.github_release.yml@main 29 | secrets: inherit 30 | with: 31 | plugin_name: multiverse-portals 32 | release_mode: ${{ inputs.release_mode }} 33 | version_bump: ${{ inputs.version_bump }} 34 | promote_from: ${{ inputs.promote_from }} 35 | -------------------------------------------------------------------------------- /.github/workflows/call.platform_uploads.yml: -------------------------------------------------------------------------------- 1 | name: 'Call: Platform Uploads' 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | target_tag: 7 | description: 'Version to upload' 8 | required: true 9 | type: string 10 | upload_modrinth: 11 | description: 'Upload to modrinth.com' 12 | required: true 13 | type: string 14 | upload_dbo: 15 | description: 'Upload to dev.bukkit.org' 16 | required: true 17 | type: string 18 | upload_hangar: 19 | description: 'Upload to hangar.papermc.io' 20 | required: true 21 | type: string 22 | secrets: 23 | MODRINTH_TOKEN: 24 | required: true 25 | DBO_UPLOAD_API_TOKEN: 26 | required: true 27 | HANGAR_UPLOAD_TOKEN: 28 | required: true 29 | 30 | jobs: 31 | platform_uploads: 32 | uses: Multiverse/Multiverse-Core/.github/workflows/generic.platform_uploads.yml@main 33 | secrets: inherit 34 | with: 35 | plugin_name: multiverse-portals 36 | 37 | modrinth_project_id: 8VMk6P0I 38 | modrinth_dependencies: > 39 | [ 40 | {"project_id": "3wmN97b8", "dependency_type": "required"} 41 | ] 42 | 43 | dbo_project_id: 30781 44 | dbo_project_relations: > 45 | [ 46 | {"slug": "multiverse-core", "type": "requiredDependency"}, 47 | {"slug": "worldedit", "type": "optionalDependency"} 48 | ] 49 | 50 | hangar_slug: Multiverse-Portals 51 | hangar_plugin_dependencies: > 52 | { "PAPER": [ 53 | { 54 | "name": "Multiverse-Core", 55 | "required": true, 56 | "platforms": ["PAPER"] 57 | } 58 | ]} 59 | 60 | target_tag: ${{ inputs.target_tag }} 61 | upload_modrinth: ${{ inputs.upload_modrinth }} 62 | upload_dbo: ${{ inputs.upload_dbo }} 63 | upload_hangar: ${{ inputs.upload_hangar }} 64 | -------------------------------------------------------------------------------- /.github/workflows/dispatch.platform_uploads.yml: -------------------------------------------------------------------------------- 1 | name: 'Dispatch: Platform Uploads' 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | target_tag: 7 | description: 'Version to upload' 8 | required: true 9 | type: string 10 | upload_modrinth: 11 | description: 'Upload to modrinth.com' 12 | required: true 13 | type: boolean 14 | upload_dbo: 15 | description: 'Upload to dev.bukkit.org' 16 | required: true 17 | type: boolean 18 | upload_hangar: 19 | description: 'Upload to hangar.papermc.io' 20 | required: true 21 | type: boolean 22 | 23 | jobs: 24 | dispatch_platform_uploads: 25 | uses: ./.github/workflows/call.platform_uploads.yml 26 | secrets: inherit 27 | with: 28 | target_tag: ${{ github.event.inputs.target_tag }} 29 | upload_modrinth: ${{ github.event.inputs.upload_modrinth }} 30 | upload_dbo: ${{ github.event.inputs.upload_dbo }} 31 | upload_hangar: ${{ github.event.inputs.upload_hangar }} 32 | -------------------------------------------------------------------------------- /.github/workflows/dispatch.promote_release.yml: -------------------------------------------------------------------------------- 1 | name: 'Dispatch: Promote Release' 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | target_tag: 7 | description: 'Version to promote' 8 | required: true 9 | 10 | jobs: 11 | check_version: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Verify input version is prerelease 15 | run: | 16 | if [[ "${{ github.event.inputs.target_tag }}" != *"pre"* ]]; then 17 | echo "Version must be a prerelease" 18 | exit 1 19 | fi 20 | 21 | github_release: 22 | needs: check_version 23 | uses: ./.github/workflows/call.github_release.yml 24 | secrets: inherit 25 | with: 26 | release_mode: promote 27 | promote_from: ${{ github.event.inputs.target_tag }} 28 | 29 | platform_uploads: 30 | needs: github_release 31 | if: needs.github_release.outputs.release_created == 'true' 32 | uses: ./.github/workflows/call.platform_uploads.yml 33 | secrets: inherit 34 | with: 35 | target_tag: ${{ needs.github_release.outputs.tag_name }} 36 | upload_modrinth: 'true' 37 | upload_dbo: 'true' 38 | upload_hangar: 'true' 39 | -------------------------------------------------------------------------------- /.github/workflows/main.prerelease.yml: -------------------------------------------------------------------------------- 1 | name: 'Main: Prerelease' 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | github_release_on_push: 9 | uses: ./.github/workflows/call.github_release.yml 10 | secrets: inherit 11 | with: 12 | release_mode: prerelease 13 | version_bump: prlabel 14 | 15 | platform_uploads_on_push: 16 | needs: github_release_on_push 17 | if: needs.github_release_on_push.outputs.release_created == 'true' 18 | uses: ./.github/workflows/call.platform_uploads.yml 19 | secrets: inherit 20 | with: 21 | target_tag: ${{ needs.github_release_on_push.outputs.tag_name }} 22 | upload_modrinth: 'true' 23 | upload_dbo: 'false' 24 | upload_hangar: 'false' 25 | -------------------------------------------------------------------------------- /.github/workflows/pr.require_label.yml: -------------------------------------------------------------------------------- 1 | name: 'PR: Require Label' 2 | 3 | on: 4 | pull_request: 5 | types: [opened, labeled, unlabeled, synchronize] 6 | branches: [main] 7 | 8 | jobs: 9 | require_label: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: mheap/github-action-required-labels@v2 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | with: 16 | mode: exactly 17 | count: 1 18 | labels: "release:major, release:minor, release:patch, no release" 19 | -------------------------------------------------------------------------------- /.github/workflows/pr.test.yml: -------------------------------------------------------------------------------- 1 | name: 'PR: Test' 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | 7 | jobs: 8 | test: 9 | uses: Multiverse/Multiverse-Core/.github/workflows/generic.test.yml@main 10 | with: 11 | plugin_name: multiverse-portals 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse stuff 2 | /.classpath 3 | /.project 4 | /.settings 5 | /.checkstyle 6 | 7 | # netbeans 8 | /nbproject 9 | 10 | # we use maven! 11 | /build.xml 12 | 13 | # maven 14 | /target 15 | dependency-reduced-pom.xml 16 | 17 | # vim 18 | .*.sw[a-p] 19 | 20 | # various other potential build files 21 | /build 22 | /bin 23 | /dist 24 | /manifest.mf 25 | 26 | /world 27 | 28 | # Mac filesystem dust 29 | *.DS_Store 30 | 31 | # intellij 32 | *.iml 33 | *.ipr 34 | *.iws 35 | .idea/ 36 | 37 | # Fern's utils 38 | uploadtoserver.sh 39 | 40 | # Testing files: 41 | debug.log 42 | 43 | # Doxygen 44 | /docs/html 45 | debug.txt 46 | 47 | # Gradle 48 | .gradle 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, The Multiverse Team All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the The Multiverse Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Multiverse Logo 3 |

4 | 5 | [![Modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/plugin/Multiverse-Portals) 6 | [![Hangar](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/hangar_vector.svg)](https://hangar.papermc.io/Multiverse/Multiverse-Portals) 7 | [![Bukkit](https://raw.githubusercontent.com/intergrav/devins-badges/refs/heads/v3/assets/cozy/available/bukkit_vector.svg)](https://dev.bukkit.org/projects/Multiverse-Portals) 8 | 9 | ![GitHub release (with filter)](https://img.shields.io/github/v/release/multiverse/multiverse-portals) 10 | [![Discord](https://img.shields.io/discord/325459248047980545?label=discord&logo=discord)](https://discord.gg/NZtfKky) 11 | [![Support us on Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Ddumptruckman%26type%3Dpatrons&style=flat)](https://patreon.com/dumptruckman) 12 | 13 | 14 | ## About 15 | 16 | [Multiverse Portals](https://dev.bukkit.org/projects/Multiverse-Portals) is an add-on Plugin for [Multiverse core](https://dev.bukkit.org/projects/multiverse-core) that allows you to create Portals that can teleport players to Multiverse destinations (worlds, anchors or even exact positions), With [Multiverse Command Destination](https://www.spigotmc.org/resources/multiverse-commanddestination.90232/) you can even make portals run commands 17 | 18 | Now it's time to create your very own server with Multiverse Portals, do check out our [Wiki](https://mvplugins.org) and [Usage Guide](https://mvplugins.org/portals/fundamentals/basic-usage/) to get started. Feel free to hop onto our [Discord](https://discord.gg/NZtfKky) if you have any questions or just want to have a chat with us! 19 | 20 | ## Our other amazing sub-modules: 21 | 22 | With just [Multiverse Core](https://github.com/multiverse/multiverse) and any of the below plugins, you can access all of these other related features in the Multiverse ecosystem. 23 | 24 | * [Multiverse-NetherPortals](https://github.com/Multiverse/Multiverse-NetherPortals) -> Have separate nether and end worlds for each of your overworlds! 25 | * [Multiverse-Inventories](https://github.com/Multiverse/Multiverse-Inventories) -> Have separated players stats and inventories per world or per group of worlds. 26 | * [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleporters! 27 | 28 | ## Building 29 | Simply build the source with Gradle: 30 | ``` 31 | ./gradlew build 32 | ``` 33 | 34 | ## Contributing 35 | 36 | **Want to help improve Multiverse Portals?** There are several ways you can support and contribute to the project. 37 | * Take a look at our "Bug: Unconfirmed" issues, where you can find issues that need extra testing and investigation. 38 | * Want others to love Multiverse too? You can join the [Multiverse Discord community](https://discord.gg/NZtfKky) and help others with issues and setup! 39 | * A Multiverse guru? You can update our [Wiki](https://github.com/Multiverse/multiverse-web) with your latest tips, tricks and guides! The wiki open for all to edit and improve. 40 | * Love coding? You could look at ["State: Open to PR"](https://github.com/Multiverse/Multiverse-Portals/labels/State%3A%20Open%20to%20PR) and ["Resolution: Accepted"](https://github.com/Multiverse/Multiverse-Portals/labels/Resolution%3A%20Accepted) issues. We're always happy to receive bug fixes and feature additions as [pull requests](https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/). 41 | * If you'd like to make a financial contribution to the project, do consider joining our [Patreon](https://www.patreon.com/dumptruckman) or make a one-time donation [here](https://paypal.me/dumptruckman)! 42 | 43 | Additionally, we would like to give a big thanks to everyone that has supported Multiverse over the years, as well as those in the years to come. Thank you! 44 | 45 | ## License 46 | 47 | Multiverse-Portals is licensed under BSD-3-Clause License. Please see [LICENSE.md](LICENSE.md) for more info. 48 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'checkstyle' 3 | id 'org.mvplugins.multiverse-plugin' version '1.2.0' 4 | } 5 | 6 | group = 'org.mvplugins.multiverse.portals' 7 | description = 'Multiverse-Portals' 8 | 9 | repositories { 10 | maven { 11 | name = 'enginehub' 12 | url = uri('https://maven.enginehub.org/repo/') 13 | } 14 | } 15 | 16 | configure(apiDependencies) { 17 | serverApiVersion = '1.18.2-R0.1-SNAPSHOT' 18 | mockBukkitServerApiVersion = '1.21' 19 | mockBukkitVersion = '4.31.1' 20 | } 21 | 22 | dependencies { 23 | // Multiverse 24 | externalPlugin 'org.mvplugins.multiverse.core:multiverse-core:5.0.0-SNAPSHOT' 25 | 26 | // WorldEdit 27 | externalPlugin('com.sk89q.worldedit:worldedit-bukkit:7.2.9') { 28 | exclude group: 'org.bukkit', module: 'bukkit' 29 | } 30 | 31 | // Utils 32 | shadowed('com.dumptruckman.minecraft:Logging:1.1.1') { 33 | exclude group: 'junit', module: 'junit' 34 | } 35 | 36 | // hk2 for annotation processing only 37 | compileOnly('org.glassfish.hk2:hk2-api:3.0.3') { 38 | exclude group: '*', module: '*' 39 | } 40 | annotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3' 41 | testAnnotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3' 42 | } 43 | 44 | checkstyle { 45 | toolVersion = '6.1.1' 46 | configFile file('config/mv_checks.xml') 47 | ignoreFailures = true 48 | } 49 | 50 | shadowJar { 51 | relocate 'com.dumptruckman.minecraft.util.Logging', 'org.mvplugins.multiverse.portals.util.MVPLogging' 52 | relocate 'com.dumptruckman.minecraft.util.DebugLog', 'org.mvplugins.multiverse.portals.util.DebugFileLogger' 53 | } 54 | 55 | publishing { 56 | repositories { 57 | maven { 58 | name = "GitHubPackages" 59 | url = "https://maven.pkg.github.com/Multiverse/Multiverse-Portals" 60 | credentials { 61 | username = System.getenv("GITHUB_ACTOR") 62 | password = System.getenv("GITHUB_TOKEN") 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /config/multiverse-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multiverse/Multiverse-Portals/dab0e666917d9f1eabb3ff2ef981a773a21408f2/config/multiverse-banner.png -------------------------------------------------------------------------------- /config/mv_checks.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multiverse/Multiverse-Portals/dab0e666917d9f1eabb3ff2ef981a773a21408f2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | 5 | pluginManagement { 6 | repositories { 7 | gradlePluginPortal() 8 | maven { 9 | url = uri('https://repo.onarandombox.com/multiverse-releases') 10 | } 11 | } 12 | } 13 | 14 | rootProject.name = 'multiverse-portals' 15 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/BstatsMetricsConfigurator.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals; 2 | 3 | import org.jvnet.hk2.annotations.Service; 4 | import org.mvplugins.multiverse.external.bstats.bukkit.Metrics; 5 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 6 | 7 | @Service 8 | final class BstatsMetricsConfigurator { 9 | 10 | private static final int PLUGIN_ID = 7767; 11 | private final Metrics metrics; 12 | 13 | @Inject 14 | private BstatsMetricsConfigurator(MultiversePortals plugin) { 15 | this.metrics = new Metrics(plugin, PLUGIN_ID); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsApi.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.plugin.ServicePriority; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.mvplugins.multiverse.core.inject.PluginServiceLocator; 7 | import org.mvplugins.multiverse.portals.utils.PortalFiller; 8 | import org.mvplugins.multiverse.portals.utils.PortalManager; 9 | 10 | import java.util.Objects; 11 | 12 | public class MultiversePortalsApi { 13 | 14 | private static MultiversePortalsApi instance; 15 | 16 | static void init(@NotNull MultiversePortals multiversePortals) { 17 | if (instance != null) { 18 | throw new IllegalStateException("MultiversePortalsApi has already been initialized!"); 19 | } 20 | instance = new MultiversePortalsApi(multiversePortals.getServiceLocator()); 21 | Bukkit.getServicesManager().register(MultiversePortalsApi.class, instance, multiversePortals, ServicePriority.Normal); 22 | } 23 | 24 | static void shutdown() { 25 | Bukkit.getServicesManager().unregister(instance); 26 | instance = null; 27 | } 28 | 29 | /** 30 | * Gets the MultiversePortalsApi. This will throw an exception if the Multiverse-Portals has not been initialized. 31 | * 32 | * @return The MultiversePortalsApi 33 | */ 34 | public static @NotNull MultiversePortalsApi get() { 35 | if (instance == null) { 36 | throw new IllegalStateException("MultiversePortalsApi has not been initialized!"); 37 | } 38 | return instance; 39 | } 40 | 41 | private final PluginServiceLocator serviceLocator; 42 | 43 | private MultiversePortalsApi(@NotNull PluginServiceLocator serviceProvider) { 44 | this.serviceLocator = serviceProvider; 45 | } 46 | 47 | /** 48 | * Gets the instance of the PortalFiller. 49 | * 50 | * @return The PortalFiller instance 51 | */ 52 | public @NotNull PortalFiller getPortalFiller() { 53 | return Objects.requireNonNull(serviceLocator.getService(PortalFiller.class)); 54 | } 55 | 56 | /** 57 | * Gets the instance of the PortalManager. 58 | * 59 | * @return The PortalManager instance 60 | */ 61 | public @NotNull PortalManager getPortalManager() { 62 | return Objects.requireNonNull(serviceLocator.getService(PortalManager.class)); 63 | } 64 | 65 | /** 66 | * Gets the instance of Multiverse-Portals's PluginServiceLocator. 67 | *
68 | * You can use this to hook into the hk2 dependency injection system used by Multiverse-Portals. 69 | * 70 | * @return The Multiverse-Portals's PluginServiceLocator 71 | */ 72 | public @NotNull PluginServiceLocator getServiceLocator() { 73 | return serviceLocator; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsPluginBinder.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals; 2 | 3 | import org.mvplugins.multiverse.core.module.MultiverseModuleBinder; 4 | import org.mvplugins.multiverse.external.glassfish.hk2.utilities.binding.ScopedBindingBuilder; 5 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 6 | 7 | public class MultiversePortalsPluginBinder extends MultiverseModuleBinder { 8 | 9 | protected MultiversePortalsPluginBinder(@NotNull MultiversePortals plugin) { 10 | super(plugin); 11 | } 12 | 13 | @Override 14 | protected ScopedBindingBuilder bindPluginClass( 15 | ScopedBindingBuilder bindingBuilder) { 16 | return super.bindPluginClass(bindingBuilder).to(MultiversePortals.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import com.dumptruckman.minecraft.util.Logging; 14 | import com.sk89q.worldedit.math.BlockVector3; 15 | import org.bukkit.util.Vector; 16 | 17 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 18 | import org.mvplugins.multiverse.portals.utils.MultiverseRegion; 19 | 20 | public class PortalLocation { 21 | 22 | private MultiverseRegion region; 23 | private boolean validLocation = false; 24 | 25 | public PortalLocation(Vector pos1, Vector pos2, LoadedMultiverseWorld world) { 26 | this.validLocation = this.setLocation(pos1, pos2, world); 27 | } 28 | 29 | public PortalLocation() { 30 | } 31 | 32 | /** 33 | * This constructor takes the Vectors from WorldEdit and converts them to Bukkit vectors. 34 | * 35 | * @param minPt 36 | * @param maxPt 37 | */ 38 | public PortalLocation(BlockVector3 minPt, BlockVector3 maxPt, LoadedMultiverseWorld world) { 39 | this(new Vector(minPt.getX(), minPt.getY(), minPt.getZ()), new Vector(maxPt.getX(), maxPt.getY(), maxPt.getZ()), world); 40 | } 41 | 42 | public static PortalLocation parseLocation(String locationString, LoadedMultiverseWorld world, String portalName) { 43 | String[] split = locationString.split(":"); 44 | if (split.length != 2) { 45 | Logging.warning("Failed Parsing Location for: " + portalName + " (Format Error, was expecting: `X,Y,Z:X,Y,Z`, but got: `" + locationString + "`)"); 46 | return getInvalidPortalLocation(); 47 | } 48 | if (world == null) { 49 | Logging.warning("Failed Parsing World for: " + portalName + " (World Error, World did not exist or was not imported into Multiverse-Core!)"); 50 | return getInvalidPortalLocation(); 51 | } 52 | 53 | Vector pos1 = parseVector(split[0]); 54 | Vector pos2 = parseVector(split[1]); 55 | 56 | if (pos1 == null || pos2 == null) { 57 | Logging.warning("Failed Parsing Location for: " + portalName + " (Vector Error, was expecting: `X,Y,Z:X,Y,Z`, but got: `" + locationString + "`)"); 58 | return getInvalidPortalLocation(); 59 | } 60 | return new PortalLocation(pos1, pos2, world); 61 | 62 | } 63 | 64 | private static PortalLocation getInvalidPortalLocation() { 65 | return new PortalLocation(); 66 | } 67 | 68 | private static Vector parseVector(String vectorString) { 69 | String[] stringCoords = vectorString.split(","); 70 | double[] coords = new double[3]; 71 | for (int i = 0; i < 3; i++) { 72 | try { 73 | coords[i] = Double.parseDouble(stringCoords[i]); 74 | } catch (NumberFormatException e) { 75 | coords[i] = 0; 76 | return null; 77 | } 78 | } 79 | return new Vector(coords[0], coords[1], coords[2]); 80 | } 81 | 82 | public boolean setLocation(Vector v1, Vector v2, LoadedMultiverseWorld world) { 83 | if (v1 == null || v2 == null || world == null) { 84 | this.validLocation = false; 85 | this.region = null; 86 | } else { 87 | this.validLocation = true; 88 | this.region = new MultiverseRegion(v1, v2, world); 89 | } 90 | return this.validLocation; 91 | } 92 | 93 | public boolean setLocation(String v1, String v2, LoadedMultiverseWorld world) { 94 | if (v1 == null || v2 == null) { 95 | this.validLocation = false; 96 | this.region = null; 97 | return false; 98 | } else { 99 | return this.setLocation(parseVector(v1), parseVector(v2), world); 100 | } 101 | 102 | } 103 | 104 | public boolean isValidLocation() { 105 | return this.validLocation; 106 | } 107 | 108 | public List getVectors() { 109 | return Arrays.asList(this.region.getMinimumPoint(), this.region.getMaximumPoint()); 110 | } 111 | 112 | public Vector getMinimum() { 113 | return this.region.getMinimumPoint(); 114 | } 115 | 116 | public Vector getMaximum() { 117 | return this.region.getMaximumPoint(); 118 | } 119 | 120 | @Override 121 | public String toString() { 122 | if (this.region == null) { 123 | return ""; 124 | } 125 | StringBuilder sb = new StringBuilder(); 126 | sb.append(this.region.getMinimumPoint().getX() + ","); 127 | sb.append(this.region.getMinimumPoint().getY() + ","); 128 | sb.append(this.region.getMinimumPoint().getZ() + ":"); 129 | sb.append(this.region.getMaximumPoint().getX() + ","); 130 | sb.append(this.region.getMaximumPoint().getY() + ","); 131 | sb.append(this.region.getMaximumPoint().getZ()); 132 | return sb.toString(); 133 | } 134 | 135 | public LoadedMultiverseWorld getMVWorld() { 136 | if (this.region == null) { 137 | return null; 138 | } 139 | return this.region.getWorld(); 140 | } 141 | 142 | public MultiverseRegion getRegion() { 143 | return this.region; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals; 9 | 10 | import java.util.Date; 11 | 12 | import com.dumptruckman.minecraft.util.Logging; 13 | import org.mvplugins.multiverse.core.economy.MVEconomist; 14 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 15 | import org.mvplugins.multiverse.core.world.WorldManager; 16 | import org.mvplugins.multiverse.portals.enums.MoveType; 17 | import org.mvplugins.multiverse.portals.utils.DisplayUtils; 18 | import org.bukkit.ChatColor; 19 | import org.bukkit.Location; 20 | import org.bukkit.entity.Player; 21 | import org.bukkit.util.Vector; 22 | 23 | import org.mvplugins.multiverse.portals.utils.MultiverseRegion; 24 | import org.mvplugins.multiverse.portals.utils.PortalManager; 25 | 26 | public class PortalPlayerSession { 27 | private final MultiversePortals plugin; 28 | private final PortalManager portalManager; 29 | private final WorldManager worldManager; 30 | private final DisplayUtils displayUtils; 31 | private final MVEconomist economist; 32 | private final Player player; 33 | 34 | private MVPortal portalSelection = null; 35 | private MVPortal standingIn = null; 36 | private boolean debugMode = false; 37 | private boolean staleLocation; 38 | private boolean hasMovedOutOfPortal = true; 39 | private Location loc; 40 | private Vector rightClick; 41 | private Vector leftClick; 42 | private LoadedMultiverseWorld rightClickWorld; 43 | private LoadedMultiverseWorld leftClickWorld; 44 | private Date lastTeleportTime; 45 | 46 | public PortalPlayerSession(MultiversePortals plugin, Player p) { 47 | this.plugin = plugin; 48 | this.portalManager = plugin.getServiceLocator().getService(PortalManager.class); 49 | this.worldManager = plugin.getServiceLocator().getService(WorldManager.class); 50 | this.displayUtils = plugin.getServiceLocator().getService(DisplayUtils.class); 51 | this.economist = plugin.getServiceLocator().getService(MVEconomist.class); 52 | this.player = p; 53 | this.setLocation(p.getLocation()); 54 | this.lastTeleportTime = new Date(new Date().getTime() - this.plugin.getCooldownTime()); 55 | } 56 | 57 | public boolean selectPortal(MVPortal portal) { 58 | this.portalSelection = portal; 59 | return true; 60 | } 61 | 62 | public MVPortal getSelectedPortal() { 63 | return this.portalSelection; 64 | } 65 | 66 | public void setDebugMode(boolean debugMode) { 67 | this.debugMode = debugMode; 68 | if (this.debugMode) { 69 | this.player.sendMessage("Portal debug mode " + ChatColor.GREEN + "ENABLED"); 70 | this.player.sendMessage("Use " + ChatColor.DARK_AQUA + "/mvp debug" + ChatColor.WHITE + " to disable."); 71 | } else { 72 | this.player.sendMessage("Portal debug mode " + ChatColor.RED + "DISABLED"); 73 | } 74 | } 75 | 76 | public boolean isDebugModeOn() { 77 | return this.debugMode; 78 | } 79 | 80 | public void setStaleLocation(boolean active) { 81 | this.staleLocation = active; 82 | } 83 | 84 | public boolean isStaleLocation() { 85 | return this.staleLocation; 86 | } 87 | 88 | private void setLocation(Location loc) { 89 | this.loc = loc; 90 | this.setStandingInLocation(); 91 | } 92 | 93 | private void setStandingInLocation() { 94 | // If they're not in a portal and this location is a portal 95 | if (this.standingIn == null && this.portalManager.isPortal(this.loc)) { 96 | this.standingIn = this.portalManager.getPortal(this.loc); 97 | // There is no portal here. 98 | } else if (!this.portalManager.isPortal(this.loc)) { 99 | this.hasMovedOutOfPortal = true; 100 | this.standingIn = null; 101 | } else { 102 | this.hasMovedOutOfPortal = false; 103 | } 104 | } 105 | 106 | public boolean doTeleportPlayer(MoveType eventType) { 107 | if (eventType == MoveType.PLAYER_MOVE && this.player.isInsideVehicle()) { 108 | return false; 109 | } 110 | return this.hasMovedOutOfPortal && this.standingIn != null; 111 | } 112 | 113 | public Location getLocation() { 114 | return this.loc; 115 | } 116 | 117 | public void setStaleLocation(Location loc, MoveType moveType) { 118 | if (this.player == null) { 119 | // This should never happen, but seems to when someone gets kicked. 120 | return; 121 | } 122 | if (this.player.isInsideVehicle() && moveType != MoveType.VEHICLE_MOVE) { 123 | return; 124 | } 125 | // If the player has not moved, they have a stale location 126 | if (this.getLocation().getBlockX() == loc.getBlockX() && this.getLocation().getBlockY() == loc.getBlockY() && this.getLocation().getBlockZ() == loc.getBlockZ()) { 127 | this.setStaleLocation(true); 128 | } else { 129 | // Update the Players Session to the new Location. 130 | this.setLocation(loc); 131 | // The location is no longer stale. 132 | this.setStaleLocation(false); 133 | } 134 | } 135 | 136 | public boolean setLeftClickSelection(Vector v, LoadedMultiverseWorld world) { 137 | if(!this.plugin.isWandEnabled()) { 138 | return false; 139 | } 140 | this.leftClick = v; 141 | this.leftClickWorld = world; 142 | String message = ChatColor.AQUA + "First position set to: (" + v.getBlockX() + ", " + v.getBlockY() + ", " + v.getBlockZ() + ")"; 143 | if (this.leftClickWorld == this.rightClickWorld && this.rightClick != null) { 144 | MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); 145 | message += ChatColor.GOLD + " (" + tempReg.getArea() + " blocks)"; 146 | } 147 | this.player.sendMessage(message); 148 | return true; 149 | } 150 | 151 | public boolean setRightClickSelection(Vector v, LoadedMultiverseWorld world) { 152 | if(!this.plugin.isWandEnabled()) { 153 | return false; 154 | } 155 | this.rightClick = v; 156 | this.rightClickWorld = world; 157 | String message = ChatColor.AQUA + "Second position set to: (" + v.getBlockX() + ", " + v.getBlockY() + ", " + v.getBlockZ() + ")"; 158 | if (this.leftClickWorld == this.rightClickWorld && this.leftClick != null) { 159 | MultiverseRegion tempReg = new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); 160 | message += ChatColor.GOLD + " (" + tempReg.getArea() + " blocks)"; 161 | } 162 | this.player.sendMessage(message); 163 | return true; 164 | 165 | } 166 | 167 | public MultiverseRegion getSelectedRegion() { 168 | WorldEditConnection worldEdit = plugin.getWorldEditConnection(); 169 | if (worldEdit != null && worldEdit.isConnected()) { 170 | if (worldEdit.isSelectionAvailable(this.player)) { 171 | Location minPoint = worldEdit.getSelectionMinPoint(this.player); 172 | Location maxPoint = worldEdit.getSelectionMaxPoint(this.player); 173 | if (minPoint != null && maxPoint != null && minPoint.getWorld().equals(maxPoint.getWorld())) { 174 | return new MultiverseRegion(minPoint, maxPoint, 175 | this.worldManager.getLoadedWorld(minPoint.getWorld().getName()).getOrNull()); 176 | } else { 177 | this.player.sendMessage("You haven't finished your selection."); 178 | return null; 179 | } 180 | } else { 181 | this.player.sendMessage("You must have a WorldEdit selection to do this."); 182 | return null; 183 | } 184 | } 185 | // They're using our crappy selection: 186 | if (this.leftClick == null) { 187 | this.player.sendMessage("You need to LEFT click on a block with your wand!"); 188 | return null; 189 | } 190 | if (this.rightClick == null) { 191 | this.player.sendMessage("You need to RIGHT click on a block with your wand!"); 192 | return null; 193 | } 194 | if (!this.leftClickWorld.equals(this.rightClickWorld)) { 195 | this.player.sendMessage("You need to select both coords in the same world!"); 196 | this.player.sendMessage("Left Click Position was in:" + this.leftClickWorld.getAlias()); 197 | this.player.sendMessage("Right Click Position was in:" + this.rightClickWorld.getAlias()); 198 | return null; 199 | } 200 | return new MultiverseRegion(this.leftClick, this.rightClick, this.leftClickWorld); 201 | } 202 | 203 | /** 204 | * If a player teleports from A - B, this method will report A even if the player is in B. 205 | * This is done for hysteresis. For the exact detection please use {@link #getUncachedStandingInPortal()} 206 | * 207 | * @return The {@link MVPortal} the player is standing in. 208 | */ 209 | public MVPortal getStandingInPortal() { 210 | return this.standingIn; 211 | } 212 | 213 | /** 214 | * This will ALWAYS return the portal a player is actually in. For hysteresis see {@link #getStandingInPortal()}. 215 | * 216 | * @return The {@link MVPortal} the player is standing in. 217 | */ 218 | public MVPortal getUncachedStandingInPortal() { 219 | return this.standingIn = this.portalManager.getPortal(this.loc); 220 | } 221 | 222 | /** 223 | * This method should be called every time a player teleports to a portal. 224 | * 225 | * @param location 226 | */ 227 | public void playerDidTeleport(Location location) { 228 | if (portalManager.getPortal(location) != null) { 229 | this.hasMovedOutOfPortal = false; 230 | return; 231 | } 232 | this.hasMovedOutOfPortal = true; 233 | } 234 | 235 | public boolean hasMovedOutOfPortal() { 236 | return this.hasMovedOutOfPortal; 237 | } 238 | 239 | public boolean showDebugInfo() { 240 | if (!this.isDebugModeOn()) { 241 | return false; 242 | } 243 | 244 | if (this.standingIn == null) { 245 | return false; 246 | } 247 | 248 | displayUtils.showStaticInfo(this.player, this.standingIn, "You are currently standing in "); 249 | displayUtils.showPortalPriceInfo(this.standingIn, this.player); 250 | return true; 251 | } 252 | 253 | public boolean showDebugInfo(MVPortal portal) { 254 | if (portal.playerCanEnterPortal(this.player)) { 255 | displayUtils.showStaticInfo(this.player, portal, "Portal Info "); 256 | displayUtils.showPortalPriceInfo(portal, this.player); 257 | } else { 258 | Logging.info("Player " + this.player.getName() + " walked through" + portal.getName() + " with debug on."); 259 | } 260 | return true; 261 | } 262 | 263 | public void setTeleportTime(Date date) { 264 | this.lastTeleportTime = date; 265 | } 266 | 267 | /** 268 | * Checks if the teleport cooldown is still in effect. If it is, a message is sent 269 | * to the player informing them. 270 | * 271 | * @return True if the teleport cooldown is still in effect, false otherwise. 272 | */ 273 | public boolean checkAndSendCooldownMessage() { 274 | long cooldownMs = this.getRemainingTeleportCooldown(); 275 | if (cooldownMs > 0) { 276 | this.player.sendMessage(this.getCooldownMessage(cooldownMs)); 277 | return true; 278 | } 279 | 280 | return false; 281 | } 282 | 283 | /** 284 | * Get the remaining teleport cooldown time in milliseconds. 285 | * If the value returned is not positive, the cooldown is no longer in effect. 286 | * 287 | * @return The remaining teleport cooldown time in milliseconds. Note that a 288 | * negative value may be returned if the cooldown is no longer in effect. 289 | */ 290 | private long getRemainingTeleportCooldown() { 291 | long cooldownEndMs = this.lastTeleportTime.getTime() + this.plugin.getCooldownTime(); 292 | long timeMs = (new Date()).getTime(); 293 | return cooldownEndMs - timeMs; 294 | } 295 | 296 | /** 297 | * Constructs a message informing a player about the 298 | * remaining cooldown time. 299 | * 300 | * @param cooldownMs The cooldown time in milliseconds. 301 | * @return A message to be sent to a player, informing them about the remaining cooldown time. 302 | */ 303 | private String getCooldownMessage(long cooldownMs) { 304 | return "There is a portal " + ChatColor.AQUA + "cooldown " 305 | + ChatColor.WHITE + "in effect. Please try again in " 306 | + ChatColor.GOLD + this.formatCooldownTime(cooldownMs) 307 | + ChatColor.WHITE + "."; 308 | } 309 | 310 | /** 311 | * Converts a long representing a time in milliseconds to a human-readable String. 312 | * 313 | * @param cooldownMs Time in milliseconds. 314 | * @return Human-readable String with the given time in seconds. 315 | */ 316 | private String formatCooldownTime(long cooldownMs) { 317 | if (cooldownMs < 1000) { 318 | return "1s"; 319 | } 320 | 321 | return (cooldownMs / 1000) + "s"; 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/WorldEditConnection.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals; 2 | 3 | import com.sk89q.worldedit.IncompleteRegionException; 4 | import com.sk89q.worldedit.WorldEdit; 5 | import com.sk89q.worldedit.bukkit.BukkitPlayer; 6 | import com.sk89q.worldedit.bukkit.BukkitWorld; 7 | import com.sk89q.worldedit.bukkit.WorldEditPlugin; 8 | import com.sk89q.worldedit.math.BlockVector3; 9 | import com.sk89q.worldedit.regions.Region; 10 | import org.bukkit.Location; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.plugin.Plugin; 13 | 14 | public class WorldEditConnection { 15 | 16 | private final Plugin connectingPlugin; 17 | 18 | private WorldEditPlugin worldEditPlugin; 19 | WorldEdit worldEdit; 20 | 21 | WorldEditConnection(Plugin plugin) { 22 | if (plugin == null) { 23 | throw new RuntimeException("plugin must not be null."); 24 | } 25 | this.connectingPlugin = plugin; 26 | } 27 | 28 | private WorldEditPlugin retrieveWorldEditPluginFromServer() { 29 | Plugin plugin = connectingPlugin.getServer().getPluginManager().getPlugin("WorldEdit"); 30 | if (plugin == null) { 31 | plugin = connectingPlugin.getServer().getPluginManager().getPlugin("FastAsyncWorldEdit"); 32 | } 33 | 34 | if (plugin == null) { 35 | return null; 36 | } else if (plugin instanceof WorldEditPlugin) { 37 | return (WorldEditPlugin) plugin; 38 | } else { 39 | connectingPlugin.getLogger().warning("WorldEdit v" + plugin.getDescription().getVersion() 40 | + " is incompatible with " + connectingPlugin.getDescription().getName() + " v" 41 | + connectingPlugin.getDescription().getVersion()); 42 | return null; 43 | } 44 | } 45 | 46 | /** 47 | * Attempts to connect to the WorldEdit plugin. 48 | * 49 | * @return true if the WorldEdit plugin is available and able to be interfaced with. 50 | */ 51 | boolean connect() { 52 | if (!isConnected()) { 53 | worldEditPlugin = retrieveWorldEditPluginFromServer(); 54 | if (worldEditPlugin != null) { 55 | this.worldEdit = worldEditPlugin.getWorldEdit(); 56 | connectingPlugin.getLogger().info(String.format("Found %s. Using it for selections.", worldEditPlugin.getName())); 57 | return true; 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | void disconnect() { 64 | worldEditPlugin = null; 65 | this.worldEdit = null; 66 | } 67 | 68 | /** 69 | * Tests the connection to the WorldEdit plugin. 70 | * 71 | * @return true if current connected to the WorldEdit plugin. 72 | */ 73 | public boolean isConnected() { 74 | return worldEditPlugin != null; 75 | } 76 | 77 | private Region getSelection(Player player) { 78 | if (!isConnected()) { 79 | throw new RuntimeException("WorldEdit connection is unavailable."); 80 | } 81 | try { 82 | return worldEdit.getSessionManager().get(new BukkitPlayer(worldEditPlugin, player)).getSelection(new BukkitWorld(player.getWorld())); 83 | } catch (IncompleteRegionException e) { 84 | return null; 85 | } 86 | } 87 | 88 | /** 89 | * @return the maximum point of the player's WorldEdit selection or null if the player has no selection. 90 | */ 91 | public Location getSelectionMaxPoint(Player player) { 92 | if (player == null) { 93 | throw new RuntimeException("player must not be null."); 94 | } 95 | 96 | if (!isConnected()) { 97 | throw new RuntimeException("WorldEdit connection is unavailable."); 98 | } 99 | 100 | Region selection = getSelection(player); 101 | if (selection != null) { 102 | BlockVector3 point = selection.getMaximumPoint(); 103 | return new Location(player.getWorld(), point.getBlockX(), point.getBlockY(), point.getBlockZ()); 104 | } 105 | return null; 106 | } 107 | 108 | /** 109 | * @return the minimum point of the player's WorldEdit selection or null if the player has no selection. 110 | */ 111 | public Location getSelectionMinPoint(Player player) { 112 | if (player == null) { 113 | throw new RuntimeException("player must not be null."); 114 | } 115 | 116 | if (!isConnected()) { 117 | throw new RuntimeException("WorldEdit connection is unavailable."); 118 | } 119 | 120 | Region selection = getSelection(player); 121 | if (selection != null) { 122 | BlockVector3 point = selection.getMinimumPoint(); 123 | return new Location(player.getWorld(), point.getBlockX(), point.getBlockY(), point.getBlockZ()); 124 | } 125 | return null; 126 | } 127 | 128 | /** 129 | * @return true if the player has currently has a WorldEdit selection. 130 | */ 131 | public boolean isSelectionAvailable(Player player) { 132 | if (player == null) { 133 | throw new RuntimeException("player must not be null."); 134 | } 135 | 136 | if (!isConnected()) { 137 | throw new RuntimeException("WorldEdit connection is unavailable."); 138 | } 139 | 140 | return getSelection(player) != null; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandCompletions.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.command; 2 | 3 | import org.mvplugins.multiverse.core.command.MVCommandCompletions; 4 | import org.mvplugins.multiverse.core.command.MVCommandManager; 5 | import org.mvplugins.multiverse.external.acf.commands.BukkitCommandCompletionContext; 6 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 7 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 8 | import org.jvnet.hk2.annotations.Service; 9 | import org.mvplugins.multiverse.portals.MVPortal; 10 | import org.mvplugins.multiverse.portals.enums.PortalConfigProperty; 11 | import org.mvplugins.multiverse.portals.enums.SetProperties; 12 | import org.mvplugins.multiverse.portals.utils.PortalManager; 13 | 14 | import java.util.Collection; 15 | 16 | @Service 17 | public class PortalsCommandCompletions { 18 | 19 | private final PortalManager portalManager; 20 | 21 | @Inject 22 | PortalsCommandCompletions(@NotNull PortalManager portalManager, @NotNull MVCommandManager commandManager) { 23 | this.portalManager = portalManager; 24 | registerCompletions(commandManager.getCommandCompletions()); 25 | } 26 | 27 | private void registerCompletions(MVCommandCompletions commandCompletions) { 28 | commandCompletions.registerAsyncCompletion("mvportals", this::suggestPortals); 29 | commandCompletions.registerStaticCompletion("setproperties", commandCompletions.suggestEnums(SetProperties.class)); 30 | commandCompletions.registerStaticCompletion("portalconfigproperty", commandCompletions.suggestEnums(PortalConfigProperty.class)); 31 | 32 | commandCompletions.setDefaultCompletion("mvportals", MVPortal.class); 33 | } 34 | 35 | private Collection suggestPortals(BukkitCommandCompletionContext context) { 36 | return this.portalManager.getPortals(context.getSender()).stream() 37 | .map(MVPortal::getName) 38 | .toList(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandContexts.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.command; 2 | 3 | import org.mvplugins.multiverse.core.command.MVCommandContexts; 4 | import org.mvplugins.multiverse.core.command.MVCommandManager; 5 | import org.mvplugins.multiverse.external.acf.commands.BukkitCommandExecutionContext; 6 | import org.mvplugins.multiverse.external.acf.commands.InvalidCommandArgument; 7 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 8 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 9 | import org.jvnet.hk2.annotations.Service; 10 | import org.mvplugins.multiverse.portals.MVPortal; 11 | import org.mvplugins.multiverse.portals.MultiversePortals; 12 | import org.mvplugins.multiverse.portals.utils.PortalManager; 13 | 14 | @Service 15 | public class PortalsCommandContexts { 16 | 17 | private final MultiversePortals plugin; 18 | private final PortalManager portalManager; 19 | 20 | @Inject 21 | PortalsCommandContexts(@NotNull MultiversePortals plugin, @NotNull PortalManager portalManager, @NotNull MVCommandManager commandManager) { 22 | this.plugin = plugin; 23 | this.portalManager = portalManager; 24 | registerContexts(commandManager.getCommandContexts()); 25 | } 26 | 27 | private void registerContexts(MVCommandContexts commandContexts) { 28 | commandContexts.registerIssuerAwareContext(MVPortal.class, this::parseMVPortal); 29 | } 30 | 31 | private MVPortal parseMVPortal(BukkitCommandExecutionContext context) { 32 | String resolve = context.getFlagValue("resolve", ""); 33 | 34 | MVPortal playerSelectedPortal = context.getIssuer().isPlayer() 35 | ? this.plugin.getPortalSession(context.getPlayer()).getSelectedPortal() 36 | : null; 37 | 38 | if (resolve.equals("issuerOnly")) { 39 | if (context.getIssuer().isPlayer() && playerSelectedPortal != null) { 40 | return playerSelectedPortal; 41 | } 42 | if (context.isOptional()) { 43 | return null; 44 | } 45 | throw new InvalidCommandArgument("This command can only be used by a player that has selected a portal with `/mvp select`."); 46 | } 47 | 48 | String portalName = context.getFirstArg(); 49 | MVPortal portal = this.portalManager.getPortal(portalName, context.getSender()); 50 | 51 | if (resolve.equals("issuerAware")) { 52 | if (portal != null) { 53 | context.popFirstArg(); 54 | return portal; 55 | } 56 | if (context.getIssuer().isPlayer() && playerSelectedPortal != null) { 57 | return playerSelectedPortal; 58 | } 59 | if (context.isOptional()) { 60 | return null; 61 | } 62 | throw new InvalidCommandArgument("The portal '" + portalName + "' doesn't exist or you're not allowed to use it!"); 63 | } 64 | 65 | if (portal != null) { 66 | context.popFirstArg(); 67 | return portal; 68 | } 69 | if (context.isOptional()) { 70 | return null; 71 | } 72 | throw new InvalidCommandArgument("The portal '" + portalName + "' doesn't exist or you're not allowed to use it!"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/ConfigCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 5 | import org.mvplugins.multiverse.core.command.MVCommandIssuer; 6 | import org.mvplugins.multiverse.core.command.MVCommandManager; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Single; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 15 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 16 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 17 | import org.jvnet.hk2.annotations.Service; 18 | import org.mvplugins.multiverse.portals.MultiversePortals; 19 | import org.mvplugins.multiverse.portals.enums.PortalConfigProperty; 20 | 21 | @Service 22 | class ConfigCommand extends PortalsCommand { 23 | 24 | private final MultiversePortals plugin; 25 | 26 | @Inject 27 | ConfigCommand(@NotNull MultiversePortals plugin) { 28 | this.plugin = plugin; 29 | } 30 | 31 | @Subcommand("config|conf") 32 | @CommandPermission("multiverse.portal.config") 33 | @CommandCompletion("@portalconfigproperty @empty") 34 | @Syntax(" ") 35 | @Description("Allows you to set Global MV Portals Variables.") 36 | void onConfigCommand( 37 | @NotNull MVCommandIssuer issuer, 38 | 39 | @Optional 40 | @Syntax("") 41 | @Description("The property to set.") 42 | PortalConfigProperty property, 43 | 44 | @Optional 45 | @Single 46 | @Syntax("") 47 | @Description("The value to set.") 48 | String value 49 | ) { 50 | if (property == null) { 51 | String[] allProps = PortalConfigProperty.getAllValues().split(" "); 52 | StringBuilder currentvals = new StringBuilder(); 53 | for (String prop : allProps) { 54 | currentvals.append(ChatColor.GREEN); 55 | currentvals.append(prop); 56 | currentvals.append(ChatColor.WHITE); 57 | currentvals.append(" = "); 58 | currentvals.append(ChatColor.GOLD); 59 | currentvals.append(this.plugin.getMainConfig().get(prop, "NOT SET")); 60 | currentvals.append(ChatColor.WHITE); 61 | currentvals.append(", "); 62 | } 63 | issuer.sendMessage(currentvals.substring(0,currentvals.length() - 2)); 64 | return; 65 | } 66 | 67 | if (value == null) { 68 | issuer.sendMessage(ChatColor.AQUA + property.name() + ChatColor.WHITE + " has value " 69 | + ChatColor.GREEN + this.plugin.getMainConfig().get(property.name().toLowerCase())); 70 | return; 71 | } 72 | 73 | if (property.equals(PortalConfigProperty.wand) || property.equals(PortalConfigProperty.portalcooldown)) { 74 | try { 75 | this.plugin.getMainConfig().set(property.name(), Integer.parseInt(value)); 76 | } catch (NumberFormatException e) { 77 | issuer.sendMessage(ChatColor.RED + "Sorry, " + ChatColor.AQUA + property.name() + ChatColor.WHITE + " must be an integer!"); 78 | return; 79 | } 80 | } else { 81 | try { 82 | this.plugin.getMainConfig().set(property.name().toLowerCase(), Boolean.parseBoolean(value)); 83 | } catch (Exception e) { 84 | issuer.sendMessage(ChatColor.RED + "Sorry, " + ChatColor.AQUA + property.name() + ChatColor.WHITE + " must be true or false!"); 85 | return; 86 | } 87 | } 88 | 89 | if (this.plugin.saveMainConfig()) { 90 | issuer.sendMessage(ChatColor.GREEN + "SUCCESS!" + ChatColor.WHITE + " Values were updated successfully!"); 91 | this.plugin.reloadConfigs(false); 92 | } else { 93 | issuer.sendMessage(ChatColor.RED + "FAIL!" + ChatColor.WHITE + " Check your console for details!"); 94 | } 95 | } 96 | 97 | @Service 98 | private final static class LegacyAlias extends ConfigCommand implements LegacyAliasCommand { 99 | @Inject 100 | LegacyAlias(MultiversePortals plugin) { 101 | super(plugin); 102 | } 103 | 104 | @Override 105 | @CommandAlias("mvpconfig|mvpconf") 106 | void onConfigCommand(MVCommandIssuer issuer, PortalConfigProperty property, String value) { 107 | super.onConfigCommand(issuer, property, value); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.entity.Player; 5 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 6 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 7 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 15 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 16 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 17 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 18 | import org.jvnet.hk2.annotations.Service; 19 | import org.mvplugins.multiverse.portals.MVPortal; 20 | import org.mvplugins.multiverse.portals.MultiversePortals; 21 | import org.mvplugins.multiverse.portals.PortalLocation; 22 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 23 | import org.mvplugins.multiverse.portals.utils.MultiverseRegion; 24 | import org.mvplugins.multiverse.portals.utils.PortalManager; 25 | 26 | @Service 27 | class CreateCommand extends PortalsCommand { 28 | 29 | private final MultiversePortals plugin; 30 | private final PortalManager portalManager; 31 | 32 | @Inject 33 | CreateCommand(@NotNull MultiversePortals plugin, @NotNull PortalManager portalManager) { 34 | this.plugin = plugin; 35 | this.portalManager = portalManager; 36 | } 37 | 38 | @Subcommand("create") 39 | @CommandPermission("multiverse.portal.create") 40 | @CommandCompletion("@empty @mvworlds|@destinations") 41 | @Syntax(" [destination]") 42 | @Description("Creates a new portal, assuming you have a region selected.") 43 | void onCreateCommand( 44 | @Flags("resolve=issuerOnly") 45 | Player player, 46 | 47 | @Flags("resolve=issuerOnly") 48 | LoadedMultiverseWorld world, 49 | 50 | @Syntax("") 51 | String portalName, 52 | 53 | @Optional 54 | @Syntax("[destination]") 55 | DestinationInstance destination 56 | ) { 57 | // todo: maybe make a CommandContext for PortalPlayerSession 58 | PortalPlayerSession ps = this.plugin.getPortalSession(player); 59 | 60 | MultiverseRegion region = ps.getSelectedRegion(); 61 | if (region == null) { 62 | return; 63 | } 64 | 65 | if (!MVPortal.PORTAL_NAME_PATTERN.matcher(portalName).matches()) { 66 | player.sendMessage(String.format("%sInvalid portal name. It must not contain dot or special characters.", ChatColor.RED)); 67 | return; 68 | } 69 | 70 | MVPortal portal = this.portalManager.getPortal(portalName); 71 | PortalLocation location = new PortalLocation(region.getMinimumPoint(), region.getMaximumPoint(), world); 72 | if (this.portalManager.addPortal(world, portalName, player.getName(), location)) { 73 | player.sendMessage("New portal (" + ChatColor.DARK_AQUA + portalName + ChatColor.WHITE + ") created and selected!"); 74 | // If the portal did not exist, ie: we're creating it. 75 | // we have to re select it, because it would be null 76 | portal = this.portalManager.getPortal(portalName); 77 | 78 | } else { 79 | player.sendMessage("New portal (" + ChatColor.DARK_AQUA + portalName + ChatColor.WHITE + ") was NOT created!"); 80 | player.sendMessage("It already existed and has been selected."); 81 | } 82 | 83 | ps.selectPortal(portal); 84 | if (destination != null) { 85 | portal.setDestination(destination); 86 | } else { 87 | player.sendMessage(ChatColor.RED + "Portal destination not set. Use " + ChatColor.DARK_AQUA + "/mvp modify destination " + ChatColor.RED + " to set one."); 88 | } 89 | 90 | // todo: Automatically get exact destination from player location 91 | // todo: Automatically get portal destination from player location 92 | } 93 | 94 | @Service 95 | private final static class LegacyAlias extends CreateCommand implements LegacyAliasCommand { 96 | @Inject 97 | LegacyAlias(MultiversePortals plugin, PortalManager portalManager) { 98 | super(plugin, portalManager); 99 | } 100 | 101 | @Override 102 | @CommandAlias("mvpcreate|mvpc") 103 | void onCreateCommand(Player player, LoadedMultiverseWorld world, String portalName, DestinationInstance destination) { 104 | super.onCreateCommand(player, world, portalName, destination); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/DebugCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 5 | import org.mvplugins.multiverse.core.command.MVCommandManager; 6 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Single; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 15 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 16 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 17 | import org.jvnet.hk2.annotations.Service; 18 | import org.mvplugins.multiverse.portals.MultiversePortals; 19 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 20 | 21 | @Service 22 | class DebugCommand extends PortalsCommand { 23 | 24 | private final MultiversePortals plugin; 25 | 26 | @Inject 27 | DebugCommand(@NotNull MultiversePortals plugin) { 28 | this.plugin = plugin; 29 | } 30 | 31 | @Subcommand("debug") 32 | @CommandPermission("multiverse.portal.debug") 33 | @CommandCompletion("on|off") 34 | @Syntax("[on|off]") 35 | @Description("Instead of teleporting you to a place when you walk into a portal you will see the details about it. This command toggles.") 36 | void onDebugCommand( 37 | @Flags("resolve=issuerOnly") 38 | Player player, 39 | 40 | @Optional 41 | @Single 42 | @Syntax("[on|off]") 43 | String toggle 44 | ) { 45 | PortalPlayerSession ps = this.plugin.getPortalSession(player); 46 | if (toggle != null) { 47 | ps.setDebugMode(toggle.equalsIgnoreCase("on")); 48 | return; 49 | } 50 | ps.setDebugMode(!ps.isDebugModeOn()); 51 | } 52 | 53 | @Service 54 | private final static class LegacyAlias extends DebugCommand implements LegacyAliasCommand { 55 | @Inject 56 | LegacyAlias(MultiversePortals plugin) { 57 | super(plugin); 58 | } 59 | 60 | @Override 61 | @CommandAlias("mvpdebug|mvpd") 62 | void onDebugCommand(Player player, String toggle) { 63 | super.onDebugCommand(player, toggle); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/InfoCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 5 | import org.mvplugins.multiverse.core.command.MVCommandIssuer; 6 | import org.mvplugins.multiverse.core.command.MVCommandManager; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 14 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 15 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 16 | import org.jvnet.hk2.annotations.Service; 17 | import org.mvplugins.multiverse.portals.MVPortal; 18 | import org.mvplugins.multiverse.portals.MultiversePortals; 19 | import org.mvplugins.multiverse.portals.utils.DisplayUtils; 20 | 21 | @Service 22 | class InfoCommand extends PortalsCommand { 23 | 24 | private final MultiversePortals plugin; 25 | private final DisplayUtils displayUtils; 26 | 27 | @Inject 28 | InfoCommand(@NotNull MultiversePortals plugin, @NotNull DisplayUtils displayUtils) { 29 | this.plugin = plugin; 30 | this.displayUtils = displayUtils; 31 | } 32 | 33 | @Subcommand("info") 34 | @CommandPermission("multiverse.portal.info") 35 | @CommandCompletion("@mvportals") 36 | @Syntax("[portal]") 37 | @Description("Displays information about a portal.") 38 | void onInfoCommand( 39 | @NotNull MVCommandIssuer issuer, 40 | 41 | @Flags("resolve=issuerAware") 42 | @Syntax("[portal]") 43 | @Description("The portal to show info") 44 | MVPortal portal 45 | ) { 46 | if(issuer.isPlayer()) { 47 | Player p = issuer.getPlayer(); 48 | this.plugin.getPortalSession(p).showDebugInfo(portal); 49 | } else { 50 | displayUtils.showStaticInfo(issuer.getIssuer(), portal, "Portal Info: "); 51 | } 52 | } 53 | 54 | @Service 55 | private final static class LegacyAlias extends InfoCommand implements LegacyAliasCommand { 56 | @Inject 57 | LegacyAlias(MultiversePortals plugin, DisplayUtils displayUtils) { 58 | super(plugin, displayUtils); 59 | } 60 | 61 | @Override 62 | @CommandAlias("mvpinfo|mvpi") 63 | void onInfoCommand(MVCommandIssuer issuer, MVPortal portal) { 64 | super.onInfoCommand(issuer, portal); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/ListCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.CommandSender; 5 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 6 | import org.mvplugins.multiverse.core.world.MultiverseWorld; 7 | import org.mvplugins.multiverse.core.world.WorldManager; 8 | import org.mvplugins.multiverse.core.command.MVCommandManager; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Default; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 15 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 16 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 17 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 18 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 19 | import org.jvnet.hk2.annotations.Service; 20 | import org.mvplugins.multiverse.portals.MVPortal; 21 | import org.mvplugins.multiverse.portals.utils.PortalManager; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | @Service 27 | class ListCommand extends PortalsCommand { 28 | 29 | private static final int ITEMS_PER_PAGE = 9; 30 | 31 | private final PortalManager portalManager; 32 | private final WorldManager worldManager; 33 | 34 | @Inject 35 | ListCommand(@NotNull PortalManager portalManager, @NotNull WorldManager worldManager) { 36 | this.portalManager = portalManager; 37 | this.worldManager = worldManager; 38 | } 39 | 40 | @Subcommand("list") 41 | @CommandPermission("multiverse.portal.list") 42 | @CommandCompletion("@empty @empty") 43 | @Syntax("[filter/world] [page]") 44 | @Description("Displays a listing of all portals that you can enter.") 45 | void onListCommand( 46 | @NotNull CommandSender sender, 47 | 48 | @Optional 49 | @Syntax("[filter/world]") 50 | @Description("Filter by name or world") 51 | String filterOrWorld, 52 | 53 | @Default("1") 54 | @Syntax("[page]") 55 | @Description("Page to display") 56 | int page 57 | ) { 58 | String filter = filterOrWorld; 59 | 60 | MultiverseWorld world = this.worldManager.getLoadedWorld(filter).getOrNull(); 61 | if (world != null) { 62 | filter = null; 63 | } 64 | 65 | List portals = new ArrayList<>(getPortals(sender, world, filter, page)); 66 | 67 | if (portals.isEmpty() && filter == null) { 68 | page = (int) Math.ceil(1F * getPortals(sender, world, filter).size() / ITEMS_PER_PAGE); 69 | portals.addAll(getPortals(sender, world, filter, page)); 70 | } 71 | 72 | String titleString = ChatColor.AQUA + String.valueOf(getPortals(sender, world, filter).size()) + " Portals"; 73 | if (world != null) { 74 | titleString += " in " + ChatColor.YELLOW + world.getAlias(); 75 | } 76 | if (filter != null) { 77 | titleString += ChatColor.GOLD + " [" + filter + "]"; 78 | } 79 | 80 | titleString += ChatColor.GOLD + " - Page " + page + "/" + (int) Math.ceil(1F * getPortals(sender, world, filter).size() / ITEMS_PER_PAGE); 81 | sender.sendMessage(ChatColor.AQUA + "--- " + titleString + ChatColor.AQUA + " ---"); 82 | 83 | for (String portal : portals) { 84 | sender.sendMessage(portal); 85 | } 86 | } 87 | 88 | private List getPortals(CommandSender sender, MultiverseWorld world, String filter) { 89 | List portals = new ArrayList<>(); 90 | if (filter == null) { 91 | filter = ""; 92 | } 93 | for (MVPortal portal : (world == null) ? this.portalManager.getPortals(sender) : this.portalManager.getPortals(sender, world)) { 94 | String destination = ""; 95 | if (portal.getDestination() != null) { 96 | destination = portal.getDestination().toString(); 97 | String destType = portal.getDestination().getIdentifier(); 98 | if (destType.equals("w")) { 99 | MultiverseWorld destWorld = this.worldManager.getLoadedWorld(destination).getOrNull(); 100 | if (destWorld != null) { 101 | destination = "(World) " + ChatColor.DARK_AQUA + destination; 102 | } 103 | } 104 | if (destType.equals("p")) { 105 | // todo: I think should use instance check instead of destType prefix 106 | // String targetWorldName = this.portalManager.getPortal(portal.getDestination().getName()).getWorld().getName(); 107 | // destination = "(Portal) " + ChatColor.DARK_AQUA + portal.getDestination().getName() + ChatColor.GRAY + " (" + targetWorldName + ")"; 108 | } 109 | if (destType.equals("e")) { 110 | String destinationWorld = portal.getDestination().toString().split(":")[1]; 111 | String destPart = portal.getDestination().toString().split(":")[2]; 112 | String[] locParts = destPart.split(","); 113 | int x, y, z; 114 | try { 115 | x = (int) Double.parseDouble(locParts[0]); 116 | y = (int) Double.parseDouble(locParts[1]); 117 | z = (int) Double.parseDouble(locParts[2]); 118 | } catch (NumberFormatException e) { 119 | e.printStackTrace(); 120 | continue; 121 | } 122 | if (destType.equals("i")) { 123 | destination = ChatColor.RED + "Invalid destination"; 124 | } 125 | destination = "(Location) " + ChatColor.DARK_AQUA + destinationWorld + ", " + x + ", " + y + ", " + z; 126 | } 127 | } 128 | 129 | if (portal.getName().toLowerCase().contains(filter.toLowerCase()) || (portal.getDestination() != null && destination.toLowerCase().contains(filter.toLowerCase()))) { 130 | portals.add(ChatColor.YELLOW + portal.getName() + ((portal.getDestination() != null) ? (ChatColor.AQUA + " -> " + ChatColor.GOLD + destination) : "")); 131 | } 132 | } 133 | java.util.Collections.sort(portals); 134 | return portals; 135 | } 136 | 137 | private List getPortals(CommandSender sender, MultiverseWorld world, String filter, int page) { 138 | List portals = new ArrayList<>(); 139 | for (int i = 0; i < getPortals(sender, world, filter).size(); i++) { 140 | if ((i >= (page * ITEMS_PER_PAGE) - ITEMS_PER_PAGE && i <= (page * ITEMS_PER_PAGE) - 1)) { 141 | portals.add(getPortals(sender, world, filter).get(i)); 142 | } 143 | } 144 | return portals; 145 | } 146 | 147 | @Service 148 | private final static class LegacyAlias extends ListCommand implements LegacyAliasCommand { 149 | @Inject 150 | LegacyAlias(PortalManager portalManager, WorldManager worldManager) { 151 | super(portalManager, worldManager); 152 | } 153 | 154 | @Override 155 | @CommandAlias("mvplist|mvpl") 156 | void onListCommand(CommandSender sender, String filterOrWorld, int page) { 157 | super.onListCommand(sender, filterOrWorld, page); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/ModifyCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import com.dumptruckman.minecraft.util.Logging; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.entity.Player; 6 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 7 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 8 | import org.mvplugins.multiverse.core.world.WorldManager; 9 | import org.mvplugins.multiverse.core.command.MVCommandIssuer; 10 | import org.mvplugins.multiverse.core.command.MVCommandManager;import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 15 | import org.mvplugins.multiverse.external.acf.commands.annotation.Single; 16 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 17 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 18 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 19 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 20 | import org.jvnet.hk2.annotations.Service; 21 | import org.mvplugins.multiverse.portals.MVPortal; 22 | import org.mvplugins.multiverse.portals.MultiversePortals; 23 | import org.mvplugins.multiverse.portals.PortalLocation; 24 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 25 | import org.mvplugins.multiverse.portals.enums.SetProperties; 26 | import org.mvplugins.multiverse.portals.utils.MultiverseRegion; 27 | 28 | @Service 29 | class ModifyCommand extends PortalsCommand { 30 | 31 | private final MultiversePortals plugin; 32 | private final WorldManager worldManager; 33 | 34 | @Inject 35 | ModifyCommand(@NotNull MultiversePortals plugin, @NotNull WorldManager worldManager) { 36 | this.plugin = plugin; 37 | this.worldManager = worldManager; 38 | } 39 | 40 | @Subcommand("modify") 41 | @CommandPermission("multiverse.portal.modify") 42 | @CommandCompletion("@mvportals @setproperties @empty") 43 | @Syntax("[portal] ") 44 | @Description("Allows you to modify all values that can be set.") 45 | public void onModifyCommand( 46 | MVCommandIssuer issuer, 47 | 48 | @Flags("resolve=issuerAware") 49 | @Syntax("[portal]") 50 | @Description("The portal to modify.") 51 | MVPortal portal, 52 | 53 | @Syntax("") 54 | @Description("The property to modify.") 55 | SetProperties property, 56 | 57 | @Single 58 | @Syntax("") 59 | @Description("The value to set.") 60 | String value 61 | ) { 62 | Logging.info("Modifying portal: " + portal.getName() + " property: " + property + " value: " + value); 63 | // Simply chop off the rest, if they have loc, that's good enough! 64 | if (property == SetProperties.loc || property == SetProperties.location) { 65 | if (!issuer.isPlayer()) { 66 | issuer.sendMessage("You must be a player to use location property!"); 67 | return; 68 | } 69 | this.setLocation(portal, issuer.getPlayer()); 70 | return; 71 | } 72 | String propertyString = property.toString().toLowerCase(); 73 | if (this.setProperty(portal, propertyString, value)) { 74 | issuer.sendMessage("Property " + property + " of Portal " + ChatColor.YELLOW + portal.getName() + ChatColor.GREEN + " was set to " + ChatColor.AQUA + value); 75 | } else { 76 | issuer.sendMessage("Property " + property + " of Portal " + ChatColor.YELLOW + portal.getName() + ChatColor.RED + " was NOT set to " + ChatColor.AQUA + value); 77 | if (propertyString.equalsIgnoreCase("dest") || propertyString.equalsIgnoreCase("destination")) { 78 | issuer.sendMessage("Multiverse could not find the destination: " + ChatColor.GOLD + value); 79 | } 80 | } 81 | } 82 | 83 | private boolean setProperty(MVPortal selectedPortal, String property, String value) { 84 | return selectedPortal.setProperty(property, value); 85 | } 86 | 87 | private void setLocation(MVPortal selectedPortal, Player player) { 88 | PortalPlayerSession ps = this.plugin.getPortalSession(player); 89 | MultiverseRegion r = ps.getSelectedRegion(); 90 | if (r != null) { 91 | LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(player.getWorld().getName()).getOrNull(); 92 | PortalLocation location = new PortalLocation(r.getMinimumPoint(), r.getMaximumPoint(), world); 93 | selectedPortal.setPortalLocation(location); 94 | player.sendMessage("Portal location has been set to your " + ChatColor.GREEN + "selection" + ChatColor.WHITE + "!"); 95 | } 96 | } 97 | 98 | @Service 99 | private final static class LegacyAlias extends ModifyCommand implements LegacyAliasCommand { 100 | @Inject 101 | LegacyAlias(MultiversePortals plugin, WorldManager worldManager) { 102 | super(plugin, worldManager); 103 | } 104 | 105 | @Override 106 | @CommandAlias("mvpmodify|mvpm") 107 | public void onModifyCommand(MVCommandIssuer issuer, MVPortal portal, SetProperties property, String value) { 108 | super.onModifyCommand(issuer, portal, property, value); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/PortalsCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.jvnet.hk2.annotations.Contract; 4 | import org.mvplugins.multiverse.core.command.MultiverseCommand; 5 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 6 | 7 | /** 8 | * Base class for all portal commands 9 | */ 10 | @Contract 11 | @CommandAlias("mvp") 12 | public abstract class PortalsCommand extends MultiverseCommand { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/RemoveCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 5 | import org.mvplugins.multiverse.core.command.MVCommandIssuer; 6 | import org.mvplugins.multiverse.core.command.MVCommandManager; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 13 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 14 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 15 | import org.jvnet.hk2.annotations.Service; 16 | import org.mvplugins.multiverse.portals.MVPortal; 17 | import org.mvplugins.multiverse.portals.utils.PortalManager; 18 | 19 | @Service 20 | class RemoveCommand extends PortalsCommand { 21 | 22 | private final PortalManager portalManager; 23 | 24 | @Inject 25 | RemoveCommand(@NotNull PortalManager portalManager) { 26 | this.portalManager = portalManager; 27 | } 28 | 29 | @Subcommand("remove") 30 | @CommandPermission("multiverse.portal.remove") 31 | @CommandCompletion("@mvportals") 32 | @Syntax("") 33 | @Description("Removes a existing portal.") 34 | void onRemoveCommand( 35 | MVCommandIssuer issuer, 36 | 37 | @Syntax("") 38 | @Description("The name of the portal to remove.") 39 | String portalName 40 | ) { 41 | if (!this.portalManager.isPortal(portalName)) { 42 | issuer.sendMessage("The portal Portal " + ChatColor.DARK_AQUA + portalName + ChatColor.WHITE + " does NOT exist!"); 43 | return; 44 | } 45 | 46 | MVPortal portal = this.portalManager.removePortal(portalName, true); 47 | issuer.sendMessage("Portal " + ChatColor.DARK_AQUA + portal.getName() + ChatColor.WHITE + " was removed successfully!"); 48 | } 49 | 50 | @Service 51 | private final static class LegacyAlias extends RemoveCommand implements LegacyAliasCommand { 52 | @Inject 53 | LegacyAlias(PortalManager portalManager) { 54 | super(portalManager); 55 | } 56 | 57 | @Override 58 | @CommandAlias("mvpremove|mvpr") 59 | void onRemoveCommand(MVCommandIssuer issuer, String portalName) { 60 | super.onRemoveCommand(issuer, portalName); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/SelectCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.entity.Player; 5 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 6 | import org.mvplugins.multiverse.core.command.MVCommandManager; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 15 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 16 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 17 | import org.jvnet.hk2.annotations.Service; 18 | import org.mvplugins.multiverse.portals.MVPortal; 19 | import org.mvplugins.multiverse.portals.MultiversePortals; 20 | 21 | @Service 22 | class SelectCommand extends PortalsCommand { 23 | 24 | private final MultiversePortals plugin; 25 | 26 | @Inject 27 | SelectCommand(@NotNull MultiversePortals plugin) { 28 | this.plugin = plugin; 29 | } 30 | 31 | @Subcommand("select") 32 | @CommandPermission("multiverse.portal.select,multiverse.portal.create") 33 | @CommandCompletion("@mvportals") 34 | @Syntax("") 35 | @Description("Selects a portal so you can perform multiple modifications on it.") 36 | void onSelectCommand( 37 | @Flags("resolve=issuerOnly") 38 | Player player, 39 | 40 | @Optional 41 | @Syntax("") 42 | @Description("The portal to select") 43 | MVPortal portal 44 | ) { 45 | if (portal == null) { 46 | MVPortal selected = this.plugin.getPortalSession(player).getSelectedPortal(); 47 | if (this.plugin.getPortalSession(player).getSelectedPortal() == null) { 48 | player.sendMessage("You have not selected a portal yet!"); 49 | player.sendMessage("Use a " + ChatColor.GREEN + plugin.getWandMaterial() + ChatColor.WHITE + " to do so!"); 50 | return; 51 | } 52 | player.sendMessage("You have selected: " + ChatColor.DARK_AQUA + selected.getName()); 53 | return; 54 | } 55 | 56 | this.plugin.getPortalSession(player).selectPortal(portal); 57 | player.sendMessage("Portal: " + ChatColor.DARK_AQUA + portal.getName() + ChatColor.WHITE + " has been selected."); 58 | } 59 | 60 | @Service 61 | private final static class LegacyAlias extends SelectCommand implements LegacyAliasCommand { 62 | @Inject 63 | LegacyAlias(MultiversePortals plugin) { 64 | super(plugin); 65 | } 66 | 67 | @Override 68 | @CommandAlias("mvpselect|mvps") 69 | void onSelectCommand(Player player, MVPortal portal) { 70 | super.onSelectCommand(player, portal); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/commands/WandCommand.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.inventory.ItemStack; 6 | import org.mvplugins.multiverse.core.command.LegacyAliasCommand; 7 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; 8 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; 9 | import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; 10 | import org.mvplugins.multiverse.external.acf.commands.annotation.Description; 11 | import org.mvplugins.multiverse.external.acf.commands.annotation.Flags; 12 | import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; 13 | import org.mvplugins.multiverse.external.acf.commands.annotation.Single; 14 | import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; 15 | import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; 16 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 17 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 18 | import org.jvnet.hk2.annotations.Service; 19 | import org.mvplugins.multiverse.portals.MultiversePortals; 20 | import org.mvplugins.multiverse.portals.WorldEditConnection; 21 | 22 | @Service 23 | class WandCommand extends PortalsCommand { 24 | 25 | private final MultiversePortals plugin; 26 | 27 | @Inject 28 | WandCommand(@NotNull MultiversePortals plugin) { 29 | this.plugin = plugin; 30 | } 31 | 32 | @Subcommand("wand") 33 | @CommandPermission("multiverse.portal.givewand") 34 | @CommandCompletion("enable|disable|toggle") 35 | @Syntax("[enable|disable|toggle]") 36 | @Description("Gives you the wand that MV uses. This will only work if you are NOT using WorldEdit.") 37 | void onWandCommand( 38 | @Flags("resolve=issuerOnly") 39 | Player player, 40 | 41 | @Optional 42 | @Single 43 | @Syntax("[enable|disable|toggle]") 44 | @Description("Enable, disable, or toggle the wand.") 45 | String action 46 | ) { 47 | if (action != null) { 48 | if (action.equals("enable")) { 49 | this.plugin.setWandEnabled(true); 50 | } else if (action.equals("disable")) { 51 | this.plugin.setWandEnabled(false); 52 | } else if (action.equals("toggle")) { 53 | this.plugin.setWandEnabled(!this.plugin.isWandEnabled()); 54 | } else { 55 | player.sendMessage(ChatColor.RED + "You must specify one of 'enable,' 'disable,' or 'toggle!'"); 56 | } 57 | return; 58 | } 59 | 60 | WorldEditConnection worldEdit = plugin.getWorldEditConnection(); 61 | if (worldEdit != null && worldEdit.isConnected()) { 62 | player.sendMessage(ChatColor.GREEN + "Cool!" + ChatColor.WHITE + " You're using" + ChatColor.AQUA + " WorldEdit! "); 63 | player.sendMessage("Just use " + ChatColor.GOLD + "the WorldEdit wand " + ChatColor.WHITE + "to perform portal selections!"); 64 | return; 65 | } 66 | ItemStack wand = new ItemStack(plugin.getWandMaterial(), 1); 67 | 68 | if (player.getInventory().getItemInMainHand().getAmount() == 0) { 69 | player.getInventory().setItemInMainHand(wand); 70 | player.sendMessage("You have been given a " + ChatColor.GREEN + "Multiverse Portal Wand(" + wand.getType() + ")!"); 71 | } else { 72 | if (player.getInventory().addItem(wand).isEmpty()) { 73 | player.sendMessage("A " + ChatColor.GREEN + "Multiverse Portal Wand(" + wand.getType() + ")" + ChatColor.WHITE + " has been placed in your inventory."); 74 | } else { 75 | player.sendMessage("Your Inventory is full. A " + ChatColor.GREEN + "Multiverse Portal Wand(" + wand.getType() + ")" + ChatColor.WHITE + " has been placed dropped nearby."); 76 | player.getWorld().dropItemNaturally(player.getLocation(), wand); 77 | } 78 | } 79 | } 80 | 81 | @Service 82 | private final static class LegacyAlias extends WandCommand implements LegacyAliasCommand { 83 | @Inject 84 | LegacyAlias(MultiversePortals plugin) { 85 | super(plugin); 86 | } 87 | 88 | @Override 89 | @CommandAlias("mvpwand|mvpw") 90 | void onWandCommand(Player player, String action) { 91 | super.onWandCommand(player, action); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/destination/PortalDestination.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.destination; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.mvplugins.multiverse.core.destination.Destination; 5 | import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; 6 | import org.mvplugins.multiverse.core.locale.MVCorei18n; 7 | import org.mvplugins.multiverse.core.locale.message.Message; 8 | import org.mvplugins.multiverse.core.teleportation.LocationManipulation; 9 | import org.mvplugins.multiverse.core.utils.result.Attempt; 10 | import org.mvplugins.multiverse.core.utils.result.FailureReason; 11 | import org.mvplugins.multiverse.external.acf.locales.MessageKey; 12 | import org.mvplugins.multiverse.external.acf.locales.MessageKeyProvider; 13 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 14 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 15 | import org.mvplugins.multiverse.external.jetbrains.annotations.Nullable; 16 | import org.jvnet.hk2.annotations.Service; 17 | import org.mvplugins.multiverse.portals.MVPortal; 18 | import org.mvplugins.multiverse.portals.utils.PortalManager; 19 | 20 | import java.util.Collection; 21 | 22 | @Service 23 | public class PortalDestination implements Destination { 24 | 25 | private final PortalManager portalManager; 26 | private final LocationManipulation locationManipulation; 27 | 28 | @Inject 29 | PortalDestination(@NotNull PortalManager portalManager, @NotNull LocationManipulation locationManipulation) { 30 | this.portalManager = portalManager; 31 | this.locationManipulation = locationManipulation; 32 | } 33 | 34 | @Override 35 | public @NotNull String getIdentifier() { 36 | return "p"; 37 | } 38 | 39 | @Override 40 | public @NotNull Attempt getDestinationInstance(@Nullable String destinationParams) { 41 | String[] items = destinationParams.split(":"); 42 | if (items.length > 3) { 43 | return Attempt.failure(InstanceFailureReason.INVALID_FORMAT, Message.of("Invalid format! Expected format is: p:portalName:[direction]")); 44 | } 45 | 46 | String portalName = items[0]; 47 | MVPortal portal = portalManager.getPortal(portalName); 48 | if (portal == null) { 49 | return Attempt.failure(InstanceFailureReason.PORTAL_NOT_FOUND, Message.of("Portal '" + portalName + "' does not exist!")); 50 | } 51 | 52 | String direction = (items.length == 2) ? items[1] : null; 53 | float yaw = direction != null ? this.locationManipulation.getYaw(direction) : -1; 54 | 55 | return Attempt.success(new PortalDestinationInstance(this, portal, direction, yaw)); 56 | } 57 | 58 | @Override 59 | public @NotNull Collection suggestDestinations(@NotNull CommandSender sender, @Nullable String s) { 60 | return portalManager.getAllPortals().stream() 61 | .map(p -> new DestinationSuggestionPacket(this, p.getName(), p.getName())) 62 | .toList(); 63 | } 64 | 65 | public enum InstanceFailureReason implements FailureReason { 66 | INVALID_FORMAT(MVCorei18n.GENERIC_FAILURE), 67 | PORTAL_NOT_FOUND(MVCorei18n.GENERIC_FAILURE), 68 | ; 69 | 70 | private final MessageKeyProvider messageKey; 71 | 72 | InstanceFailureReason(MessageKeyProvider message) { 73 | this.messageKey = message; 74 | } 75 | 76 | /** 77 | * {@inheritDoc} 78 | */ 79 | @Override 80 | public MessageKey getMessageKey() { 81 | return messageKey.getMessageKey(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/destination/PortalDestinationInstance.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.destination; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.entity.Entity; 5 | import org.bukkit.util.Vector; 6 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 7 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 8 | import org.mvplugins.multiverse.external.jetbrains.annotations.Nullable; 9 | import org.mvplugins.multiverse.external.vavr.control.Option; 10 | import org.mvplugins.multiverse.portals.MVPortal; 11 | 12 | public class PortalDestinationInstance extends DestinationInstance { 13 | 14 | private final MVPortal portal; 15 | private final String direction; 16 | private final float yaw; 17 | 18 | protected PortalDestinationInstance( 19 | @NotNull PortalDestination destination, 20 | @NotNull MVPortal portal, 21 | @Nullable String direction, 22 | float yaw 23 | ) { 24 | super(destination); 25 | this.portal = portal; 26 | this.direction = direction; 27 | this.yaw = yaw; 28 | } 29 | 30 | public String getDirection() { 31 | return direction; 32 | } 33 | 34 | @Override 35 | public @NotNull Option getLocation(@NotNull Entity teleportee) { 36 | return Option.of(portal.getSafePlayerSpawnLocation()) 37 | .peek(l -> l.setYaw(yaw)); 38 | } 39 | 40 | @Override 41 | public @NotNull Option getVelocity(@NotNull Entity teleportee) { 42 | return Option.none(); 43 | } 44 | 45 | @Override 46 | public boolean checkTeleportSafety() { 47 | return portal.useSafeTeleporter(); 48 | } 49 | 50 | @Override 51 | public @NotNull Option getFinerPermissionSuffix() { 52 | return Option.of(portal.getName()); 53 | } 54 | 55 | @Override 56 | protected @NotNull String serialise() { 57 | if (this.direction != null) { 58 | return this.portal.getName() + ":" + this.direction; 59 | } 60 | return this.portal.getName(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/destination/RandomPortalDestination.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.destination; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.jvnet.hk2.annotations.Service; 5 | import org.mvplugins.multiverse.core.destination.Destination; 6 | import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; 7 | import org.mvplugins.multiverse.core.utils.StringFormatter; 8 | import org.mvplugins.multiverse.core.utils.result.Attempt; 9 | import org.mvplugins.multiverse.core.utils.result.FailureReason; 10 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 11 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 12 | import org.mvplugins.multiverse.external.jetbrains.annotations.Nullable; 13 | import org.mvplugins.multiverse.portals.MVPortal; 14 | import org.mvplugins.multiverse.portals.utils.PortalManager; 15 | 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | import java.util.List; 19 | 20 | @Service 21 | public class RandomPortalDestination implements Destination { 22 | 23 | private final PortalManager portalManager; 24 | 25 | @Inject 26 | RandomPortalDestination(PortalManager portalManager) { 27 | this.portalManager = portalManager; 28 | } 29 | 30 | @Override 31 | public @NotNull String getIdentifier() { 32 | return "rp"; 33 | } 34 | 35 | @Override 36 | public @Nullable Attempt getDestinationInstance(@Nullable String destinationParams) { 37 | List portalNames = Arrays.stream(destinationParams.split(",")).toList(); 38 | return Attempt.success(new RandomPortalDestinationInstance(this, portalManager, portalNames)); 39 | } 40 | 41 | @Override 42 | public @NotNull Collection suggestDestinations(@NotNull CommandSender sender, @Nullable String input) { 43 | Collection strings = StringFormatter.addonToCommaSeperated(input, portalManager.getAllPortals().stream() 44 | .map(MVPortal::getName) 45 | .toList()); 46 | 47 | return strings.stream() 48 | .map(s -> new DestinationSuggestionPacket(this, s, null)) 49 | .toList(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/destination/RandomPortalDestinationInstance.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.destination; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.entity.Entity; 5 | import org.bukkit.util.Vector; 6 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 7 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 8 | import org.mvplugins.multiverse.external.vavr.control.Option; 9 | import org.mvplugins.multiverse.portals.MVPortal; 10 | import org.mvplugins.multiverse.portals.utils.PortalManager; 11 | 12 | import java.util.List; 13 | 14 | import static org.mvplugins.multiverse.external.acf.commands.ACFUtil.RANDOM; 15 | 16 | public class RandomPortalDestinationInstance extends DestinationInstance { 17 | 18 | private final PortalManager portalManager; 19 | private final List portalNames; 20 | 21 | public RandomPortalDestinationInstance( 22 | @NotNull RandomPortalDestination destination, 23 | @NotNull PortalManager portalManager, 24 | @NotNull List portalNames) { 25 | super(destination); 26 | this.portalManager = portalManager; 27 | this.portalNames = portalNames; 28 | } 29 | 30 | @Override 31 | public @NotNull Option getLocation(@NotNull Entity teleportee) { 32 | List portalNames = this.portalNames.isEmpty() 33 | ? portalManager.getAllPortals().stream().map(MVPortal::getName).toList() 34 | : this.portalNames; 35 | String targetPortalName = portalNames.get(RANDOM.nextInt(portalNames.size())); 36 | return Option.of(portalManager.getPortal(targetPortalName)) 37 | .map(MVPortal::getSafePlayerSpawnLocation); 38 | } 39 | 40 | @Override 41 | public @NotNull Option getVelocity(@NotNull Entity teleportee) { 42 | return Option.none(); 43 | } 44 | 45 | @Override 46 | public boolean checkTeleportSafety() { 47 | return true; 48 | } 49 | 50 | @Override 51 | public @NotNull Option getFinerPermissionSuffix() { 52 | return Option.none(); 53 | } 54 | 55 | @Override 56 | protected @NotNull String serialise() { 57 | return String.join(",", portalNames); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/enums/MoveType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2012. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.enums; 9 | 10 | /** 11 | * This is here until I decide what to do with the old 12 | * Type enum that we were using. 13 | */ 14 | public enum MoveType { 15 | VEHICLE_MOVE, PLAYER_MOVE 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/enums/PortalConfigProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.enums; 9 | 10 | /** 11 | * Multiverse 2 12 | * 13 | * @author fernferret 14 | */ 15 | public enum PortalConfigProperty { 16 | wand, useonmove, portalsdefaulttonether, enforceportalaccess, 17 | portalcooldown, clearonremove, teleportvehicles; 18 | 19 | 20 | public static String getAllValues() { 21 | String buffer = ""; 22 | for (PortalConfigProperty c : PortalConfigProperty.values()) { 23 | // All values will NOT Contain spaces. 24 | buffer += c.toString() + " "; 25 | } 26 | return buffer; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/enums/PortalType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.enums; 9 | 10 | /** 11 | * What type of portal was used? 12 | * 13 | * If Legacy, a MV1 style portal was used. 14 | * If Normal, a Nether style portal (with purple goo) was used. 15 | * 16 | */ 17 | public enum PortalType { Legacy, Normal } 18 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/enums/SetProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.enums; 9 | 10 | public enum SetProperties { 11 | destination, dest, owner, loc, location, price, currency, curr, safe, telenonplayers, handlerscript 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/event/MVPortalEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.event; 9 | 10 | import org.bukkit.Location; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.Cancellable; 13 | import org.bukkit.event.Event; 14 | import org.bukkit.event.HandlerList; 15 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 16 | import org.mvplugins.multiverse.portals.MVPortal; 17 | import org.mvplugins.multiverse.portals.enums.PortalType; 18 | 19 | /** 20 | * Multiverse 2 21 | * 22 | * @author fernferret 23 | */ 24 | public class MVPortalEvent extends Event implements Cancellable { 25 | private Player teleportee; 26 | private MVPortal sendingPortal; 27 | private DestinationInstance destination; 28 | private boolean isCancelled; 29 | 30 | public MVPortalEvent(DestinationInstance destination, Player teleportee, MVPortal sendingPortal) { 31 | this.teleportee = teleportee; 32 | this.destination = destination; 33 | this.sendingPortal = sendingPortal; 34 | } 35 | 36 | public MVPortalEvent(DestinationInstance destination, Player teleportee) { 37 | this(destination, teleportee, null); 38 | } 39 | 40 | private static final HandlerList HANDLERS = new HandlerList(); 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | @Override 46 | public HandlerList getHandlers() { 47 | return HANDLERS; 48 | } 49 | 50 | /** 51 | * Gets the handler list. This is required by the event system. 52 | * @return A list of HANDLERS. 53 | */ 54 | public static HandlerList getHandlerList() { 55 | return HANDLERS; 56 | } 57 | 58 | /** 59 | * Returns the player who will be teleported by this event. 60 | * 61 | * @return The player who will be teleported by this event. 62 | */ 63 | public Player getTeleportee() { 64 | return this.teleportee; 65 | } 66 | 67 | /** 68 | * Returns the location the player was before the teleport. 69 | * 70 | * @return The location the player was before the teleport. 71 | */ 72 | public Location getFrom() { 73 | return this.teleportee.getLocation(); 74 | } 75 | 76 | /** 77 | * Returns the destination that the player will spawn at. 78 | * 79 | * @return The destination the player will spawn at. 80 | */ 81 | public DestinationInstance getDestination() { 82 | return this.destination; 83 | } 84 | 85 | /** 86 | * Returns the type of portal that was used. 87 | * 88 | * This will be Legacy for MV1 style portals and Normal for Portals that use the swirly purple goo. 89 | * 90 | * @return The {@link PortalType} of the sending portal. 91 | * @throws IllegalStateException If this portal's location is no longer valid. 92 | * @deprecated Use {@link MVPortal#getPortalType()} instead (i.e. {@code getSendingPortal().getPortalType()}). 93 | */ 94 | @Deprecated 95 | public PortalType getPortalType() throws IllegalStateException { 96 | return this.getSendingPortal().getPortalType(); 97 | } 98 | 99 | /** 100 | * Returns the Portal sending the player 101 | * 102 | * @return The portal the player is sent from 103 | */ 104 | public MVPortal getSendingPortal() { 105 | return this.sendingPortal; 106 | } 107 | 108 | @Override 109 | public boolean isCancelled() { 110 | return this.isCancelled; 111 | } 112 | 113 | @Override 114 | public void setCancelled(boolean cancelled) { 115 | this.isCancelled = cancelled; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/MVPBlockListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.listeners; 9 | 10 | import org.bukkit.Material; 11 | import org.bukkit.event.EventHandler; 12 | import org.bukkit.event.block.BlockPhysicsEvent; 13 | 14 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 15 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 16 | import org.jvnet.hk2.annotations.Service; 17 | import org.mvplugins.multiverse.portals.utils.PortalManager; 18 | 19 | @Service 20 | public class MVPBlockListener implements PortalsListener { 21 | private final PortalManager portalManager; 22 | 23 | @Inject 24 | MVPBlockListener(@NotNull PortalManager portalManager) { 25 | this.portalManager = portalManager; 26 | } 27 | 28 | @EventHandler 29 | public void blockPhysics(BlockPhysicsEvent event) { 30 | if (event.isCancelled()) { 31 | return; 32 | } 33 | if (event.getChangedType() == Material.NETHER_PORTAL || event.getBlock().getType() == Material.NETHER_PORTAL) { 34 | if (portalManager.isPortal(event.getBlock().getLocation())) { 35 | event.setCancelled(true); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/MVPCoreListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.listeners; 9 | 10 | import java.io.File; 11 | 12 | import com.dumptruckman.minecraft.util.Logging; 13 | import org.mvplugins.multiverse.core.event.MVConfigReloadEvent; 14 | import org.mvplugins.multiverse.core.event.MVDebugModeEvent; 15 | import org.mvplugins.multiverse.core.event.MVDumpsDebugInfoEvent; 16 | import org.mvplugins.multiverse.core.event.MVPlayerTouchedPortalEvent; 17 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 18 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 19 | import org.jvnet.hk2.annotations.Service; 20 | import org.mvplugins.multiverse.portals.MVPortal; 21 | import org.bukkit.Location; 22 | import org.bukkit.event.EventHandler; 23 | 24 | import org.mvplugins.multiverse.portals.MultiversePortals; 25 | import org.mvplugins.multiverse.portals.utils.PortalManager; 26 | 27 | @Service 28 | public class MVPCoreListener implements PortalsListener { 29 | private final MultiversePortals plugin; 30 | private final PortalManager portalManager; 31 | 32 | @Inject 33 | MVPCoreListener(@NotNull MultiversePortals plugin, @NotNull PortalManager portalManager) { 34 | this.plugin = plugin; 35 | this.portalManager = portalManager; 36 | } 37 | 38 | /** 39 | * This method is called when Multiverse-Core wants to know what version we are. 40 | * @param event The Version event. 41 | */ 42 | @EventHandler 43 | public void dumpsDebugInfoRequest(MVDumpsDebugInfoEvent event) { 44 | event.appendDebugInfo(this.plugin.getVersionInfo()); 45 | File configFile = new File(this.plugin.getDataFolder(), "config.yml"); 46 | File portalsFile = new File(this.plugin.getDataFolder(), "portals.yml"); 47 | event.putDetailedDebugInfo("multiverse-portals/config.yml", configFile); 48 | event.putDetailedDebugInfo("multiverse-portals/portals.yml", portalsFile); 49 | } 50 | 51 | /** 52 | * This method is called when Multiverse-Core wants to reload the configs. 53 | * @param event The Config Reload event. 54 | */ 55 | @EventHandler 56 | public void configReload(MVConfigReloadEvent event) { 57 | plugin.reloadConfigs(); 58 | event.addConfig("Multiverse-Portals - portals.yml"); 59 | event.addConfig("Multiverse-Portals - config.yml"); 60 | } 61 | 62 | @EventHandler 63 | public void debugModeChange(MVDebugModeEvent event) { 64 | Logging.setDebugLevel(event.getLevel()); 65 | } 66 | 67 | /** 68 | * This method is called when a player touches a portal. 69 | * It's used to handle the intriquite messiness of priority between MV plugins. 70 | * @param event The PTP event. 71 | */ 72 | @EventHandler 73 | public void portalTouchEvent(MVPlayerTouchedPortalEvent event) { 74 | Logging.finer("Found The TouchedPortal event."); 75 | Location l = event.getBlockTouched(); 76 | if (event.canUseThisPortal() && (this.portalManager.isPortal(l))) { 77 | if (this.plugin.getPortalSession(event.getPlayer()).isDebugModeOn()) { 78 | event.setCancelled(true); 79 | return; 80 | } 81 | // This is a valid portal, and they can use it so far... 82 | MVPortal p = this.portalManager.getPortal(event.getPlayer(), l); 83 | if (p == null) { 84 | // The player can't see this portal, and can't use it. 85 | Logging.finer(String.format("'%s' was DENIED access to this portal event.", event.getPlayer().getName())); 86 | event.setCanUseThisPortal(false); 87 | } else if (p.getDestination() == null) { 88 | if (this.plugin.getMainConfig().getBoolean("portalsdefaulttonether", false)) { 89 | Logging.finer("Allowing MVPortal to act as nether portal."); 90 | return; 91 | } 92 | // They can see it, is it val 93 | event.getPlayer().sendMessage("This Multiverse Portal does not have a valid destination!"); 94 | event.setCanUseThisPortal(false); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.listeners; 9 | 10 | import com.dumptruckman.minecraft.util.Logging; 11 | import org.mvplugins.multiverse.core.teleportation.BlockSafety; 12 | import org.mvplugins.multiverse.core.teleportation.LocationManipulation; 13 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 14 | import org.mvplugins.multiverse.core.economy.MVEconomist; 15 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 16 | import org.mvplugins.multiverse.core.world.WorldManager; 17 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 18 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 19 | import org.jvnet.hk2.annotations.Service; 20 | import org.mvplugins.multiverse.portals.MVPortal; 21 | import org.mvplugins.multiverse.portals.MultiversePortals; 22 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 23 | import org.mvplugins.multiverse.portals.WorldEditConnection; 24 | import org.mvplugins.multiverse.portals.event.MVPortalEvent; 25 | import org.mvplugins.multiverse.portals.utils.PortalFiller; 26 | import org.mvplugins.multiverse.portals.utils.PortalManager; 27 | import org.bukkit.ChatColor; 28 | import org.bukkit.Location; 29 | import org.bukkit.Material; 30 | import org.bukkit.block.Block; 31 | import org.bukkit.block.BlockFace; 32 | import org.bukkit.entity.Player; 33 | import org.bukkit.event.EventHandler; 34 | import org.bukkit.event.EventPriority; 35 | import org.bukkit.event.block.Action; 36 | import org.bukkit.event.player.PlayerBucketEmptyEvent; 37 | import org.bukkit.event.player.PlayerBucketFillEvent; 38 | import org.bukkit.event.player.PlayerInteractEvent; 39 | import org.bukkit.event.player.PlayerPortalEvent; 40 | import org.bukkit.event.player.PlayerQuitEvent; 41 | import org.bukkit.event.player.PlayerTeleportEvent; 42 | import org.bukkit.inventory.EquipmentSlot; 43 | 44 | @Service 45 | public class MVPPlayerListener implements PortalsListener { 46 | 47 | private final MultiversePortals plugin; 48 | private final PortalFiller filler; 49 | private final PortalManager portalManager; 50 | private final PlayerListenerHelper helper; 51 | private final LocationManipulation locationManipulation; 52 | private final WorldManager worldManager; 53 | private final BlockSafety blockSafety; 54 | private final MVEconomist economist; 55 | 56 | @Inject 57 | MVPPlayerListener( 58 | @NotNull MultiversePortals plugin, 59 | @NotNull PlayerListenerHelper helper, 60 | @NotNull PortalManager portalManager, 61 | @NotNull PortalFiller filler, 62 | @NotNull LocationManipulation locationManipulation, 63 | @NotNull WorldManager worldManager, 64 | @NotNull BlockSafety blockSafety, 65 | @NotNull MVEconomist economist) { 66 | this.plugin = plugin; 67 | this.helper = helper; 68 | this.portalManager = portalManager; 69 | this.filler = filler; 70 | this.locationManipulation = locationManipulation; 71 | this.worldManager = worldManager; 72 | this.blockSafety = blockSafety; 73 | this.economist = economist; 74 | } 75 | 76 | @EventHandler 77 | public void playerQuit(PlayerQuitEvent event) { 78 | this.plugin.destroyPortalSession(event.getPlayer()); 79 | } 80 | 81 | @EventHandler(priority = EventPriority.MONITOR) 82 | public void playerTeleport(PlayerTeleportEvent event) { 83 | if (event.isCancelled()) { 84 | Logging.fine("The PlayerTeleportEvent was already cancelled. Doing nothing."); 85 | return; 86 | } 87 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 88 | ps.playerDidTeleport(event.getTo()); 89 | } 90 | 91 | @EventHandler(priority = EventPriority.LOW) 92 | public void playerBucketFill(PlayerBucketFillEvent event) { 93 | if (event.isCancelled()) { 94 | Logging.fine("The PlayerBucketFillEvent was already cancelled. Doing nothing."); 95 | return; 96 | } 97 | 98 | Logging.finer("Fill: "); 99 | Logging.finer("Block Clicked: " + event.getBlockClicked() + ":" + event.getBlockClicked().getType()); 100 | 101 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 102 | MVPortal portal = portalManager.getPortal(event.getPlayer(), event.getBlockClicked().getLocation()); 103 | if (portal != null) { 104 | if (ps.isDebugModeOn()) { 105 | ps.showDebugInfo(portal); 106 | event.setCancelled(true); 107 | } else { 108 | Material fillMaterial = Material.AIR; 109 | Logging.finer("Fill Material: " + fillMaterial); 110 | this.filler.fillRegion(portal.getLocation().getRegion(), event.getBlockClicked().getLocation(), fillMaterial, event.getPlayer()); 111 | } 112 | } 113 | } 114 | 115 | @EventHandler(priority = EventPriority.LOW) 116 | public void playerBucketEmpty(PlayerBucketEmptyEvent event) { 117 | if (event.isCancelled()) { 118 | Logging.fine("The PlayerBucketEmptyEvent was already cancelled. Doing nothing."); 119 | return; 120 | } 121 | 122 | if (!MultiversePortals.bucketFilling) { 123 | Logging.fine("The bucket filling functionality has been disabled in config, doing nothing"); 124 | return; 125 | } 126 | 127 | Location translatedLocation = this.getTranslatedLocation(event.getBlockClicked(), event.getBlockFace()); 128 | Logging.finer("Fill: "); 129 | Logging.finer("Block Clicked: " + event.getBlockClicked() + ":" + event.getBlockClicked().getType()); 130 | Logging.finer("Translated Block: " + event.getPlayer().getWorld().getBlockAt(translatedLocation) + ":" + event.getPlayer().getWorld().getBlockAt(translatedLocation).getType()); 131 | 132 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 133 | MVPortal portal = portalManager.getPortal(event.getPlayer(), translatedLocation); 134 | if (portal != null) { 135 | if (ps.isDebugModeOn()) { 136 | ps.showDebugInfo(portal); 137 | event.setCancelled(true); 138 | } else { 139 | if (!portal.playerCanFillPortal(event.getPlayer())) { 140 | event.setCancelled(true); 141 | return; 142 | } 143 | Material fillMaterial = Material.WATER; 144 | if (event.getBucket().equals(Material.LAVA_BUCKET)) { 145 | fillMaterial = Material.LAVA; 146 | } 147 | 148 | Logging.finer("Fill Material: " + fillMaterial); 149 | this.filler.fillRegion(portal.getLocation().getRegion(), translatedLocation, fillMaterial, event.getPlayer()); 150 | } 151 | } 152 | } 153 | 154 | @EventHandler(priority = EventPriority.LOW) 155 | public void playerInteract(PlayerInteractEvent event) { 156 | if (event.isCancelled()) { 157 | Logging.fine("The PlayerInteractEvent was already cancelled. Doing nothing."); 158 | return; 159 | } 160 | 161 | // Portal lighting stuff 162 | if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getMaterial() == Material.FLINT_AND_STEEL) { 163 | // They're lighting somethin' 164 | Logging.finer("Player is lighting block: " + this.locationManipulation.strCoordsRaw(event.getClickedBlock().getLocation())); 165 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 166 | Location translatedLocation = this.getTranslatedLocation(event.getClickedBlock(), event.getBlockFace()); 167 | if (!portalManager.isPortal(translatedLocation)) { 168 | return; 169 | } 170 | MVPortal portal = portalManager.getPortal(event.getPlayer(), translatedLocation); 171 | if (event.getItem() == null) { 172 | return; 173 | } 174 | if (!event.getPlayer().hasPermission("multiverse.portal.create")) { 175 | return; 176 | } 177 | Material inHand = event.getItem().getType(); 178 | 179 | // Cancel the event if there was a portal. 180 | 181 | if (portal != null) { 182 | 183 | // Make sure the portal's frame around this point is made out of 184 | // a valid material. 185 | if (!portal.isFrameValid(translatedLocation)) { 186 | return; 187 | } 188 | 189 | Logging.finer("Right Clicked: "); 190 | Logging.finer("Block Clicked: " + event.getClickedBlock() + ":" + event.getClickedBlock().getType()); 191 | Logging.finer("Translated Block: " + event.getPlayer().getWorld().getBlockAt(translatedLocation) + ":" + event.getPlayer().getWorld().getBlockAt(translatedLocation).getType()); 192 | Logging.finer("In Hand: " + inHand); 193 | if (ps.isDebugModeOn()) { 194 | ps.showDebugInfo(portal); 195 | event.setCancelled(true); 196 | } else { 197 | Material fillMaterial = Material.NETHER_PORTAL; 198 | if (translatedLocation.getWorld().getBlockAt(translatedLocation).getType() == Material.NETHER_PORTAL) { 199 | fillMaterial = Material.AIR; 200 | } 201 | Logging.finer("Fill Material: " + fillMaterial); 202 | event.setCancelled(this.filler.fillRegion(portal.getLocation().getRegion(), translatedLocation, fillMaterial, event.getPlayer())); 203 | } 204 | } 205 | return; 206 | } 207 | 208 | Material itemType = plugin.getWandMaterial(); 209 | // If we Found WorldEdit, return, we're not needed here. 210 | // If the item is not the Wand we've setup we're not needed either 211 | // If the player doesn't have the perms, return also. 212 | // Also return if this isn't the player's main hand 213 | WorldEditConnection worldEdit = plugin.getWorldEditConnection(); 214 | if ((worldEdit != null && worldEdit.isConnected()) 215 | || event.getPlayer().getInventory().getItemInMainHand().getType() != itemType 216 | || !event.getPlayer().hasPermission("multiverse.portal.create") 217 | || event.getHand() != EquipmentSlot.HAND) { 218 | return; 219 | } 220 | 221 | if (event.getAction() == Action.LEFT_CLICK_BLOCK) { 222 | LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(event.getPlayer().getWorld().getName()).getOrNull(); 223 | event.setCancelled(this.plugin.getPortalSession(event.getPlayer()).setLeftClickSelection(event.getClickedBlock().getLocation().toVector(), world)); 224 | } else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { 225 | LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(event.getPlayer().getWorld().getName()).getOrNull(); 226 | event.setCancelled(this.plugin.getPortalSession(event.getPlayer()).setRightClickSelection(event.getClickedBlock().getLocation().toVector(), world)); 227 | } 228 | } 229 | 230 | private Location getTranslatedLocation(Block clickedBlock, BlockFace face) { 231 | Location clickedLoc = clickedBlock.getLocation(); 232 | Location newLoc = new Location(clickedBlock.getWorld(), face.getModX() + clickedLoc.getBlockX(), face.getModY() + clickedLoc.getBlockY(), face.getModZ() + clickedLoc.getBlockZ()); 233 | Logging.finest("Clicked Block: " + clickedBlock.getLocation()); 234 | Logging.finest("Translated Block: " + newLoc); 235 | return newLoc; 236 | } 237 | 238 | @EventHandler 239 | public void playerPortal(PlayerPortalEvent event) { 240 | if (event.isCancelled()) { 241 | Logging.fine("This Portal event was already cancelled."); 242 | return; 243 | } 244 | Logging.finer("onPlayerPortal called!"); 245 | Location playerPortalLoc = event.getPlayer().getLocation(); 246 | // Determine if we're in a portal 247 | MVPortal portal = portalManager.getPortal(event.getPlayer(), playerPortalLoc, false); 248 | Player p = event.getPlayer(); 249 | // Even if the location was null, we still have to see if 250 | // someone wasn't exactly on (because they can do this). 251 | if (portal == null) { 252 | // Check around the player to make sure 253 | playerPortalLoc = this.blockSafety.findPortalBlockNextTo(event.getFrom()); 254 | if (playerPortalLoc != null) { 255 | Logging.finer("Player was outside of portal, The location has been successfully translated."); 256 | portal = portalManager.getPortal(event.getPlayer(), playerPortalLoc, false); 257 | } 258 | } 259 | if (portal != null) { 260 | Logging.finer("There was a portal found!"); 261 | DestinationInstance portalDest = portal.getDestination(); 262 | if (portalDest != null) { 263 | // this is a valid MV Portal, so we'll cancel the event 264 | event.setCancelled(true); 265 | 266 | if (!portal.isFrameValid(playerPortalLoc)) { 267 | event.getPlayer().sendMessage("This portal's frame is made of an " + ChatColor.RED + "incorrect material." + ChatColor.RED + " You should exit it now."); 268 | return; 269 | } 270 | 271 | Location destLocation = portalDest.getLocation(event.getPlayer()).getOrNull(); 272 | if (destLocation == null) { 273 | Logging.fine("Unable to teleport player because destination is null!"); 274 | return; 275 | } 276 | 277 | if (!this.worldManager.isLoadedWorld(destLocation.getWorld())) { 278 | Logging.fine("Unable to teleport player because the destination world is not managed by Multiverse!"); 279 | return; 280 | } 281 | 282 | if (portalDest.checkTeleportSafety()) { 283 | Location safeLocation = blockSafety.findSafeSpawnLocation(portalDest.getLocation(event.getPlayer()).getOrNull()); 284 | if (safeLocation == null) { 285 | event.setCancelled(true); 286 | Logging.warning("Portal " + portal.getName() + " destination is not safe!"); 287 | event.getPlayer().sendMessage(ChatColor.RED + "Portal " + portal.getName() + " destination is not safe!"); 288 | return; 289 | } 290 | destLocation = safeLocation; 291 | } 292 | event.setTo(destLocation); 293 | 294 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 295 | 296 | if (ps.checkAndSendCooldownMessage()) { 297 | Logging.fine("Player denied teleportation due to cooldown."); 298 | return; 299 | } 300 | // If they're using Access and they don't have permission and they're NOT exempt, return, they're not allowed to tp. 301 | // No longer checking exemption status 302 | if (MultiversePortals.EnforcePortalAccess && !event.getPlayer().hasPermission(portal.getPermission())) { 303 | this.helper.stateFailure(p.getDisplayName(), portal.getName()); 304 | return; 305 | } 306 | 307 | boolean shouldPay = false; 308 | double price = portal.getPrice(); 309 | Material currency = portal.getCurrency(); 310 | 311 | // Stop the player if the portal costs and they can't pay 312 | if (price != 0D && !p.hasPermission(portal.getExempt())) { 313 | shouldPay = true; 314 | if (price > 0D && !economist.isPlayerWealthyEnough(p, price, currency)) { 315 | p.sendMessage(economist.getNSFMessage(currency, 316 | "You need " + economist.formatPrice(price, currency) + " to enter the " + portal.getName() + " portal.")); 317 | return; 318 | } 319 | } 320 | 321 | MVPortalEvent portalEvent = new MVPortalEvent(portalDest, event.getPlayer(), portal); 322 | this.plugin.getServer().getPluginManager().callEvent(portalEvent); 323 | 324 | if (portalEvent.isCancelled()) { 325 | Logging.fine("Someone cancelled the MVPlayerPortal Event!"); 326 | return; 327 | } else if (shouldPay) { 328 | if (price < 0D) { 329 | economist.deposit(p, -price, currency); 330 | } else { 331 | economist.withdraw(p, price, currency); 332 | } 333 | p.sendMessage(String.format("You have %s %s for using %s.", 334 | price > 0D ? "been charged" : "earned", 335 | economist.formatPrice(price, currency), 336 | portal.getName())); 337 | } 338 | 339 | event.getPlayer().teleport(event.getTo()); 340 | } else if (!this.plugin.getMainConfig().getBoolean("portalsdefaulttonether", false)) { 341 | // If portals should not default to the nether, cancel the event 342 | event.getPlayer().sendMessage(String.format( 343 | "This portal %sdoesn't go anywhere. You should exit it now.", ChatColor.RED)); 344 | Logging.fine("Event canceled because this was a MVPortal with an invalid destination. But you had 'portalsdefaulttonether' set to false!"); 345 | event.setCancelled(true); 346 | } 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerMoveListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.listeners; 9 | 10 | import com.dumptruckman.minecraft.util.Logging; 11 | import org.bukkit.event.Listener; 12 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 13 | import org.mvplugins.multiverse.core.economy.MVEconomist; 14 | import org.mvplugins.multiverse.core.world.WorldManager; 15 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 16 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 17 | import org.jvnet.hk2.annotations.Service; 18 | import org.mvplugins.multiverse.portals.MVPortal; 19 | import org.mvplugins.multiverse.portals.MultiversePortals; 20 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 21 | import org.mvplugins.multiverse.portals.enums.MoveType; 22 | import org.mvplugins.multiverse.portals.event.MVPortalEvent; 23 | import org.bukkit.ChatColor; 24 | import org.bukkit.Location; 25 | import org.bukkit.Material; 26 | import org.bukkit.entity.Player; 27 | import org.bukkit.event.EventHandler; 28 | import org.bukkit.event.EventPriority; 29 | import org.bukkit.event.block.BlockFromToEvent; 30 | import org.bukkit.event.player.PlayerMoveEvent; 31 | import org.mvplugins.multiverse.portals.utils.PortalManager; 32 | 33 | @Service 34 | public class MVPPlayerMoveListener implements Listener { 35 | 36 | private final MultiversePortals plugin; 37 | private final PlayerListenerHelper helper; 38 | private final PortalManager portalManager; 39 | private final WorldManager worldManager; 40 | private final MVEconomist economist; 41 | 42 | @Inject 43 | MVPPlayerMoveListener( 44 | @NotNull MultiversePortals plugin, 45 | @NotNull PlayerListenerHelper helper, 46 | @NotNull PortalManager portalManager, 47 | @NotNull WorldManager worldManager, 48 | @NotNull MVEconomist economist) { 49 | this.plugin = plugin; 50 | this.helper = helper; 51 | this.portalManager = portalManager; 52 | this.worldManager = worldManager; 53 | this.economist = economist; 54 | } 55 | 56 | @EventHandler(priority = EventPriority.LOW) 57 | public void blockFromTo(BlockFromToEvent event) { 58 | // Always check if the event has been canceled by someone else. 59 | if(event.isCancelled()) { 60 | return; 61 | } 62 | 63 | // The to block should never be null, but apparently it is sometimes... 64 | if (event.getBlock() == null || event.getToBlock() == null) { 65 | return; 66 | } 67 | 68 | // If lava/something else is trying to flow in... 69 | if (portalManager.isPortal(event.getToBlock().getLocation())) { 70 | event.setCancelled(true); 71 | return; 72 | } 73 | // If something is trying to flow out, stop that too, unless bucketFilling has been disabled 74 | if (portalManager.isPortal(event.getBlock().getLocation()) && MultiversePortals.bucketFilling) { 75 | event.setCancelled(true); 76 | } 77 | } 78 | 79 | @EventHandler(priority = EventPriority.LOW) 80 | public void playerMove(PlayerMoveEvent event) { 81 | if (event.isCancelled()) { 82 | return; 83 | } 84 | Player p = event.getPlayer(); // Grab Player 85 | Location loc = p.getLocation(); // Grab Location 86 | 87 | // Check the Player has actually moved a block to prevent unneeded calculations... This is to prevent huge performance drops on high player count servers. 88 | PortalPlayerSession ps = this.plugin.getPortalSession(event.getPlayer()); 89 | ps.setStaleLocation(loc, MoveType.PLAYER_MOVE); 90 | 91 | // If the location is stale, ie: the player isn't actually moving xyz coords, they're looking around 92 | if (ps.isStaleLocation()) { 93 | return; 94 | } 95 | MVPortal portal = ps.getStandingInPortal(); 96 | // If the portal is not null, and it's a legacy portal, 97 | // and we didn't show debug info (the debug is meant to toggle), do the stuff. 98 | if (portal != null 99 | && (!MultiversePortals.NetherAnimation || portal.isLegacyPortal()) 100 | && ps.doTeleportPlayer(MoveType.PLAYER_MOVE) 101 | && !ps.showDebugInfo()) { 102 | 103 | DestinationInstance d = portal.getDestination(); 104 | if (d == null) { 105 | Logging.fine("Invalid Destination!"); 106 | return; 107 | } 108 | p.setFallDistance(0); 109 | 110 | Location destLocation = d.getLocation(p).getOrNull(); 111 | if (destLocation == null) { 112 | Logging.fine("Unable to teleport player because destination is null!"); 113 | return; 114 | } 115 | 116 | if (!this.worldManager.isLoadedWorld(destLocation.getWorld())) { 117 | Logging.fine("Unable to teleport player because the destination world is not managed by Multiverse!"); 118 | return; 119 | } 120 | if (!portal.isFrameValid(loc)) { 121 | p.sendMessage("This portal's frame is made of an " + ChatColor.RED + "incorrect material. You should exit it now."); 122 | return; 123 | } 124 | if (ps.checkAndSendCooldownMessage()) { 125 | return; 126 | } 127 | // If they're using Access and they don't have permission and they're NOT excempt, return, they're not allowed to tp. 128 | // No longer checking exemption status 129 | if (MultiversePortals.EnforcePortalAccess && !event.getPlayer().hasPermission(portal.getPermission())) { 130 | this.helper.stateFailure(p.getDisplayName(), portal.getName()); 131 | return; 132 | } 133 | 134 | double price = portal.getPrice(); 135 | Material currency = portal.getCurrency(); 136 | 137 | if (price != 0D && !p.hasPermission(portal.getExempt())) { 138 | if (price < 0D || economist.isPlayerWealthyEnough(p, price, currency)) { 139 | // call event for other plugins 140 | MVPortalEvent portalEvent = new MVPortalEvent(d, event.getPlayer(), portal); 141 | this.plugin.getServer().getPluginManager().callEvent(portalEvent); 142 | if (!portalEvent.isCancelled()) { 143 | if (price < 0D) { 144 | economist.deposit(p, -price, currency); 145 | } else { 146 | economist.withdraw(p, price, currency); 147 | } 148 | p.sendMessage(String.format("You have %s %s for using %s.", 149 | price > 0D ? "been charged" : "earned", 150 | economist.formatPrice(price, currency), 151 | portal.getName())); 152 | helper.performTeleport(event.getPlayer(), event.getTo(), ps, d); 153 | } 154 | } else { 155 | p.sendMessage(economist.getNSFMessage(currency, 156 | "You need " + economist.formatPrice(price, currency) + " to enter the " + portal.getName() + " portal.")); 157 | } 158 | } else { 159 | // call event for other plugins 160 | MVPortalEvent portalEvent = new MVPortalEvent(d, event.getPlayer(), portal); 161 | this.plugin.getServer().getPluginManager().callEvent(portalEvent); 162 | if (!portalEvent.isCancelled()) { 163 | helper.performTeleport(event.getPlayer(), event.getTo(), ps, d); 164 | } 165 | } 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/MVPVehicleListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.listeners; 9 | 10 | import java.util.Date; 11 | import java.util.List; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | import com.dumptruckman.minecraft.util.Logging; 15 | import org.bukkit.Bukkit; 16 | import org.bukkit.event.Listener; 17 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 18 | import org.mvplugins.multiverse.core.teleportation.LocationManipulation; 19 | import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; 20 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 21 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 22 | import org.jvnet.hk2.annotations.Service; 23 | import org.mvplugins.multiverse.external.paperlib.PaperLib; 24 | import org.mvplugins.multiverse.portals.destination.PortalDestinationInstance; 25 | import org.mvplugins.multiverse.portals.enums.MoveType; 26 | import org.bukkit.Location; 27 | import org.bukkit.entity.Entity; 28 | import org.bukkit.entity.Player; 29 | import org.bukkit.entity.Vehicle; 30 | import org.bukkit.event.EventHandler; 31 | import org.bukkit.event.vehicle.VehicleMoveEvent; 32 | import org.bukkit.util.Vector; 33 | 34 | import org.mvplugins.multiverse.portals.MVPortal; 35 | import org.mvplugins.multiverse.portals.MultiversePortals; 36 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 37 | import org.mvplugins.multiverse.portals.utils.PortalManager; 38 | 39 | @Service 40 | public class MVPVehicleListener implements Listener { 41 | private final MultiversePortals plugin; 42 | private final PortalManager portalManager; 43 | private final LocationManipulation locationManipulation; 44 | private final AsyncSafetyTeleporter safetyTeleporter; 45 | 46 | @Inject 47 | MVPVehicleListener( 48 | @NotNull MultiversePortals plugin, 49 | @NotNull PortalManager portalManager, 50 | @NotNull LocationManipulation locationManipulation, 51 | @NotNull AsyncSafetyTeleporter safetyTeleporter) { 52 | this.plugin = plugin; 53 | this.portalManager = portalManager; 54 | this.locationManipulation = locationManipulation; 55 | this.safetyTeleporter = safetyTeleporter; 56 | } 57 | 58 | @EventHandler 59 | public void vehicleMove(VehicleMoveEvent event) { 60 | // todo: add support for multiple passengers, e.g. boats 61 | if (event.getVehicle().getPassenger() instanceof Player) { 62 | Vehicle v = event.getVehicle(); 63 | Player p = (Player) v.getPassenger(); 64 | PortalPlayerSession ps = this.plugin.getPortalSession(p); 65 | ps.setStaleLocation(v.getLocation(), MoveType.VEHICLE_MOVE); 66 | 67 | if (ps.isStaleLocation()) { 68 | return; 69 | } 70 | 71 | // Teleport the Player 72 | if (!teleportVehicle(p, v, event.getTo())) { 73 | // todo: revamp vehicle teleport in 5.1 74 | // Logging.warning("Failed to teleport vehicle: " + event.getVehicle()); 75 | } 76 | } else { 77 | MVPortal portal = this.portalManager.getPortal(event.getFrom()); 78 | if ((portal != null) && (portal.getTeleportNonPlayers())) { 79 | DestinationInstance dest = portal.getDestination(); 80 | if (dest == null) 81 | return; 82 | 83 | // Check the portal's frame. 84 | if (!portal.isFrameValid(event.getVehicle().getLocation())) { 85 | return; 86 | } 87 | 88 | Class vehicleClass = event.getVehicle().getType().getEntityClass(); 89 | if (vehicleClass == null) { 90 | Logging.warning("No vehicle class found for vehicle: " + event.getVehicle()); 91 | return; 92 | } 93 | 94 | Vector vehicleVec = event.getVehicle().getVelocity(); 95 | Location target = dest.getLocation(event.getVehicle()).getOrNull(); 96 | if (dest instanceof PortalDestinationInstance pd) { 97 | // Translate the direction of travel. 98 | vehicleVec = this.locationManipulation.getTranslatedVector(vehicleVec, pd.getDirection()); 99 | } 100 | 101 | this.setVehicleVelocity(vehicleVec, dest, event.getVehicle()); 102 | 103 | List formerPassengers = event.getVehicle().getPassengers(); 104 | event.getVehicle().eject(); 105 | 106 | Entity newVehicle = target.getWorld().spawn(target, vehicleClass); 107 | Vector finalVehicleVec = vehicleVec; 108 | CompletableFuture.allOf(formerPassengers.stream() 109 | .map(passenger -> PaperLib.teleportAsync(passenger, target)) 110 | .toArray(CompletableFuture[]::new)) 111 | .thenRun(() -> { 112 | Bukkit.getScheduler().runTask(plugin, () -> { 113 | formerPassengers.forEach(newVehicle::addPassenger); 114 | this.setVehicleVelocity(finalVehicleVec, dest, newVehicle); 115 | }); 116 | }); 117 | event.getVehicle().remove(); 118 | } 119 | } 120 | } 121 | 122 | private boolean teleportVehicle(Player p, Vehicle v, Location to) { 123 | PortalPlayerSession ps = this.plugin.getPortalSession(p); 124 | MVPortal portal = ps.getStandingInPortal(); 125 | // If the portal is not null 126 | // AND if we did not show debug info, do the stuff 127 | // The debug is meant to toggle. 128 | if (portal != null && ps.doTeleportPlayer(MoveType.VEHICLE_MOVE) && !ps.showDebugInfo()) { 129 | if (ps.checkAndSendCooldownMessage()) { 130 | return false; 131 | } 132 | // TODO: Money 133 | DestinationInstance d = portal.getDestination(); 134 | if (d == null) { 135 | return false; 136 | } 137 | 138 | // Check the portal's frame. 139 | if (!portal.isFrameValid(v.getLocation())) { 140 | return false; 141 | } 142 | 143 | Location l = d.getLocation(p).getOrNull(); 144 | Vector vehicleVec = v.getVelocity(); 145 | 146 | // 0 Yaw in dest = 0,X 147 | if (d instanceof PortalDestinationInstance pd) { 148 | // Translate the direction of travel. 149 | vehicleVec = this.locationManipulation.getTranslatedVector(vehicleVec, pd.getDirection()); 150 | } 151 | 152 | // Set the velocity 153 | // Will set to the destination's velocity if one is present 154 | // Or 155 | this.setVehicleVelocity(vehicleVec, d, v); 156 | 157 | p.setFallDistance(0); 158 | 159 | // The worlds are different! Ahhh! 160 | if (!l.getWorld().equals(p.getWorld())) { 161 | return teleportVehicleSeperately(p, v, d, ps); 162 | } 163 | 164 | this.safetyTeleporter.to(d).by(p).teleport(v) 165 | .onSuccess(() -> { 166 | ps.playerDidTeleport(to); 167 | ps.setTeleportTime(new Date()); 168 | }); 169 | return true; 170 | } 171 | return false; 172 | } 173 | 174 | private boolean teleportVehicleSeperately( 175 | Player player, 176 | Vehicle vehicle, 177 | DestinationInstance destination, 178 | PortalPlayerSession ps) { 179 | // Remove the player from the old one. 180 | vehicle.eject(); 181 | Location vehicleToLocation = destination.getLocation(vehicle).getOrNull(); 182 | Location playerToLocation = destination.getLocation(player).getOrNull(); 183 | // Add an offset to ensure the player is 1 higher than where the cart was. 184 | playerToLocation.add(0, 0.5, 0); 185 | 186 | Class vehicleClass = vehicle.getType().getEntityClass(); 187 | if (vehicleClass == null) { 188 | Logging.warning("No vehicle class found for vehicle: " + vehicle); 189 | return false; 190 | } 191 | // Now create a new vehicle: 192 | Vehicle newVehicle = (Vehicle) vehicleToLocation.getWorld().spawn(vehicleToLocation, vehicleClass); 193 | 194 | safetyTeleporter.to(destination).teleport(player) 195 | .onSuccess(() -> { 196 | Bukkit.getScheduler().runTask(plugin, () -> { 197 | // Set the new player 198 | if (newVehicle.addPassenger(player)) { 199 | Logging.finer("Added passenger to new vehicle: " + newVehicle); 200 | } else { 201 | Logging.warning("Failed to add passenger to new vehicle: " + newVehicle); 202 | } 203 | // Set the vehicle's velocity to ours. 204 | this.setVehicleVelocity(vehicle.getVelocity(), destination, newVehicle); 205 | // They did teleport. Let's delete the old vehicle. 206 | vehicle.remove(); 207 | }); 208 | }) 209 | .onFailure(reason -> { 210 | Logging.fine("Failed to teleport player '%s' to destination '%s'. Reason: %s", player.getDisplayName(), destination, reason); 211 | vehicle.addPassenger(player); 212 | }); 213 | 214 | return true; 215 | } 216 | 217 | private void setVehicleVelocity(Vector calculated, DestinationInstance to, Entity newVehicle) { 218 | // If the destination has a non-zero velocity, use that, 219 | // otherwise use the existing velocity, because velocities 220 | // are preserved through portals... duh. 221 | to.getVelocity(newVehicle) 222 | .filter(v -> !v.equals(new Vector(0, 0, 0))) 223 | .peek(newVehicle::setVelocity) 224 | .onEmpty(() -> newVehicle.setVelocity(calculated)); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/PlayerListenerHelper.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.listeners; 2 | 3 | import java.util.Date; 4 | 5 | import com.dumptruckman.minecraft.util.Logging; 6 | import org.mvplugins.multiverse.core.destination.DestinationInstance; 7 | import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; 8 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 9 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 10 | import org.jvnet.hk2.annotations.Service; 11 | import org.mvplugins.multiverse.portals.PortalPlayerSession; 12 | import org.bukkit.Location; 13 | import org.bukkit.entity.Player; 14 | 15 | @Service 16 | public class PlayerListenerHelper { 17 | 18 | private final AsyncSafetyTeleporter safetyTeleporter; 19 | 20 | @Inject 21 | PlayerListenerHelper(@NotNull AsyncSafetyTeleporter safetyTeleporter) { 22 | this.safetyTeleporter = safetyTeleporter; 23 | } 24 | 25 | void stateSuccess(String playerName, String worldName) { 26 | Logging.fine(String.format( 27 | "MV-Portals is allowing Player '%s' to use the portal '%s'.", 28 | playerName, worldName)); 29 | } 30 | 31 | void stateFailure(String playerName, String portalName) { 32 | Logging.fine(String.format( 33 | "MV-Portals is DENYING Player '%s' access to use the portal '%s'.", 34 | playerName, portalName)); 35 | } 36 | 37 | void performTeleport(Player player, Location to, PortalPlayerSession ps, DestinationInstance destination) { 38 | safetyTeleporter.to(destination).teleport(player) 39 | .onSuccess(() -> { 40 | ps.playerDidTeleport(to); 41 | ps.setTeleportTime(new Date()); 42 | this.stateSuccess(player.getDisplayName(), destination.toString()); 43 | }) 44 | .onFailure(reason -> Logging.fine( 45 | "Failed to teleport player '%s' to destination '%s'. Reason: %s", 46 | player.getDisplayName(), destination, reason) 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/listeners/PortalsListener.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.listeners; 2 | 3 | import org.bukkit.event.Listener; 4 | import org.jvnet.hk2.annotations.Contract; 5 | 6 | /** 7 | * Base implementation for all portal listeners 8 | */ 9 | @Contract 10 | public interface PortalsListener extends Listener { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/utils/DisplayUtils.java: -------------------------------------------------------------------------------- 1 | package org.mvplugins.multiverse.portals.utils; 2 | 3 | import org.mvplugins.multiverse.core.economy.MVEconomist; 4 | import org.mvplugins.multiverse.core.world.MultiverseWorld; 5 | import org.mvplugins.multiverse.core.world.WorldManager; 6 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 7 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 8 | import org.jvnet.hk2.annotations.Service; 9 | import org.mvplugins.multiverse.portals.MVPortal; 10 | import org.bukkit.ChatColor; 11 | import org.bukkit.command.CommandSender; 12 | 13 | @Service 14 | public class DisplayUtils { 15 | 16 | private final WorldManager worldManager; 17 | private final MVEconomist economist; 18 | 19 | @Inject 20 | DisplayUtils( 21 | @NotNull WorldManager worldManager, 22 | @NotNull MVEconomist economist) { 23 | this.worldManager = worldManager; 24 | this.economist = economist; 25 | } 26 | 27 | public void showStaticInfo(CommandSender sender, MVPortal portal, String message) { 28 | sender.sendMessage(ChatColor.AQUA + "--- " + message + ChatColor.DARK_AQUA + portal.getName() + ChatColor.AQUA + " ---"); 29 | String[] locParts = portal.getLocation().toString().split(":"); 30 | sender.sendMessage("Coords: " + ChatColor.GOLD + locParts[0] + ChatColor.WHITE + " to " + ChatColor.GOLD + locParts[1] + ChatColor.WHITE + " in " + ChatColor.GOLD + portal.getWorld().getName() ); 31 | if (portal.getDestination() == null) { 32 | sender.sendMessage("Destination: " + ChatColor.RED + ChatColor.ITALIC + "NOT SET!"); 33 | } else { 34 | String destination = portal.getDestination().toString(); 35 | String destType = portal.getDestination().getIdentifier(); 36 | if (destType.equals("w")) { 37 | MultiverseWorld destWorld = worldManager.getWorld(destination).getOrNull(); 38 | if (destWorld != null) { 39 | destination = "(World) " + ChatColor.DARK_AQUA + destination; 40 | } 41 | } 42 | if (destType.equals("p")) { 43 | // todo: I think should use instance check instead of destType prefix 44 | // String targetWorldName = portalManager.getPortal(portal.getDestination().getName()).getWorld().getName(); 45 | // destination = "(Portal) " + ChatColor.DARK_AQUA + portal.getDestination().getName() + ChatColor.GRAY + " (" + targetWorldName + ")"; 46 | } 47 | if (destType.equals("e")) { 48 | String destinationWorld = portal.getDestination().toString().split(":")[1]; 49 | String destPart = portal.getDestination().toString().split(":")[2]; 50 | String[] targetParts = destPart.split(","); 51 | int x, y, z; 52 | try { 53 | x = (int) Double.parseDouble(targetParts[0]); 54 | y = (int) Double.parseDouble(targetParts[1]); 55 | z = (int) Double.parseDouble(targetParts[2]); 56 | } catch (NumberFormatException e) { 57 | e.printStackTrace(); 58 | return; 59 | } 60 | destination = "(Location) " + ChatColor.DARK_AQUA + destinationWorld + ", " + x + ", " + y + ", " + z; 61 | } 62 | if (destType.equals("i")) { 63 | destination = ChatColor.RED + "Invalid Destination!"; 64 | } 65 | sender.sendMessage("Destination: " + ChatColor.GOLD + destination); 66 | } 67 | } 68 | 69 | public void showPortalPriceInfo(MVPortal portal, CommandSender sender) { 70 | if (portal.getPrice() > 0D) { 71 | sender.sendMessage("Price: " + ChatColor.GREEN + economist.formatPrice(portal.getPrice(), portal.getCurrency())); 72 | } else if (portal.getPrice() < 0D) { 73 | sender.sendMessage("Price: " + ChatColor.GREEN + economist.formatPrice(-portal.getPrice(), portal.getCurrency())); 74 | } else { 75 | sender.sendMessage("Price: " + ChatColor.GREEN + "FREE!"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.utils; 9 | 10 | import org.bukkit.Location; 11 | import org.bukkit.util.Vector; 12 | import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; 13 | 14 | /** 15 | * This is a placeholder of good things to come... 16 | * 17 | * @author fernferret 18 | */ 19 | public class MultiverseRegion { 20 | 21 | private Vector min; 22 | private Vector max; 23 | private LoadedMultiverseWorld world; 24 | 25 | public MultiverseRegion(Object pos1, Object pos2, LoadedMultiverseWorld w) { 26 | // Creating soft dependencies on WE 27 | if (pos1 instanceof com.sk89q.worldedit.math.BlockVector3 && pos2 instanceof com.sk89q.worldedit.math.BlockVector3) { 28 | com.sk89q.worldedit.math.BlockVector3 weV1 = (com.sk89q.worldedit.math.BlockVector3) pos1; 29 | com.sk89q.worldedit.math.BlockVector3 weV2 = (com.sk89q.worldedit.math.BlockVector3) pos2; 30 | Vector tmp1 = new Vector(weV1.getX(), weV1.getY(), weV1.getZ()); 31 | Vector tmp2 = new Vector(weV2.getX(), weV2.getY(), weV2.getZ()); 32 | this.min = Vector.getMinimum(tmp1, tmp2); 33 | this.max = Vector.getMaximum(tmp1, tmp2); 34 | this.world = w; 35 | } 36 | } 37 | 38 | public MultiverseRegion(Location loc1, Location loc2, LoadedMultiverseWorld w) { 39 | this(loc1.toVector(), loc2.toVector(), w); 40 | } 41 | 42 | public MultiverseRegion(Vector pos1, Vector pos2, LoadedMultiverseWorld w) { 43 | this.min = Vector.getMinimum(pos1, pos2); 44 | this.max = Vector.getMaximum(pos1, pos2); 45 | this.world = w; 46 | } 47 | 48 | public Vector getMinimumPoint() { 49 | return this.min; 50 | } 51 | 52 | public Vector getMaximumPoint() { 53 | return this.max; 54 | } 55 | 56 | public LoadedMultiverseWorld getWorld() { 57 | return this.world; 58 | } 59 | 60 | public int getWidth() { 61 | return Math.abs((this.max.getBlockX() + 1) - this.min.getBlockX()); 62 | } 63 | 64 | public int getHeight() { 65 | return Math.abs((this.max.getBlockY() + 1) - this.min.getBlockY()); 66 | } 67 | 68 | public int getDepth() { 69 | return Math.abs((this.max.getBlockZ() + 1) - this.min.getBlockZ()); 70 | } 71 | 72 | public int getArea() { 73 | return this.getWidth() * this.getHeight() * this.getDepth(); 74 | } 75 | 76 | public boolean containsVector(Location l) { 77 | if (!this.world.getBukkitWorld().map(w -> w.equals(l.getWorld())).getOrElse(false)) { 78 | return false; 79 | } 80 | if (!(l.getBlockX() >= min.getBlockX() && l.getBlockX() <= max.getBlockX())) { 81 | return false; 82 | } 83 | if (!(l.getBlockZ() >= min.getBlockZ() && l.getBlockZ() <= max.getBlockZ())) { 84 | return false; 85 | } 86 | if (!(l.getBlockY() >= min.getBlockY() && l.getBlockY() <= max.getBlockY())) { 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/utils/PortalFiller.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.utils; 9 | 10 | import com.dumptruckman.minecraft.util.Logging; 11 | import org.bukkit.Axis; 12 | import org.bukkit.ChatColor; 13 | import org.bukkit.Location; 14 | import org.bukkit.Material; 15 | import org.bukkit.block.Block; 16 | import org.bukkit.block.data.Orientable; 17 | import org.bukkit.entity.Player; 18 | 19 | import org.mvplugins.multiverse.core.teleportation.LocationManipulation; 20 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 21 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 22 | import org.jvnet.hk2.annotations.Service; 23 | import org.mvplugins.multiverse.portals.MVPortal; 24 | 25 | @Service 26 | public class PortalFiller { 27 | private final LocationManipulation locationManipulation; 28 | 29 | @Inject 30 | PortalFiller(@NotNull LocationManipulation locationManipulation) { 31 | this.locationManipulation = locationManipulation; 32 | } 33 | 34 | public boolean fillRegion(MultiverseRegion r, Location l, Material type, Player player) { 35 | if (r.getWidth() != 1 && r.getDepth() != 1) { 36 | player.sendMessage("Cannot fill portal, It needs a width or depth of " + ChatColor.GOLD + "1" + ChatColor.WHITE + 37 | ". w:[" + ChatColor.AQUA + r.getWidth() + ChatColor.WHITE + "] d:[" + ChatColor.AQUA + r.getDepth() + ChatColor.WHITE + "]"); 38 | } 39 | return this.fillRegion(r, l, type); 40 | } 41 | 42 | public boolean fillRegion(MultiverseRegion r, Location l, Material type) { 43 | if (r.getWidth() != 1 && r.getDepth() != 1) { 44 | Logging.finer("Cannot fill portal, it is too big... w:[" + r.getWidth() + "] d:[" + r.getDepth() + "]"); 45 | return false; 46 | } 47 | Logging.finer("Neat, Starting Portal fill w:[" + r.getWidth() + "] h:[" + r.getHeight() + "] d:[" + r.getDepth() + "]"); 48 | 49 | 50 | int useX = (r.getWidth() == 1) ? 0 : 1; 51 | int useZ = (r.getDepth() == 1) ? 0 : 1; 52 | Block oldLoc = l.getWorld().getBlockAt(l); 53 | Logging.finer("Filling: " + type); 54 | doFill(oldLoc, useX, useZ, r, type); 55 | return true; 56 | } 57 | 58 | /** 59 | * Recursively fills out from a single point! 60 | * 61 | * @param newLoc 62 | * @param useX 63 | * @param useZ 64 | */ 65 | private void doFill(Block newLoc, int useX, int useZ, MultiverseRegion r, Material type) { 66 | if (isValidPortalRegion(newLoc.getLocation(), type)) { 67 | // we need to check if the fill material is nether_portal so we can rotate it if necessary 68 | if (type == Material.NETHER_PORTAL) { 69 | // we won't use physics with nether_portal blocks because we cancel 70 | // the BlockPhysicsEvent to prevent accidentally breaking the blocks. 71 | // if we were to use physics, errors would be thrown upon breaking the portal blocks. 72 | boolean usePhysics = false; 73 | newLoc.setType(type, usePhysics); 74 | if (useX == 0) { 75 | Orientable b = (Orientable) newLoc.getBlockData(); 76 | b.setAxis(Axis.Z); 77 | // also don't use physics here 78 | newLoc.setBlockData(b, usePhysics); 79 | } 80 | } else { 81 | newLoc.setType(type); 82 | } 83 | } 84 | if (isValidPortalRegion(newLoc.getRelative(useX * 1, 0, useZ * 1).getLocation(), type)) { 85 | Block tmpLoc = newLoc.getRelative(useX * 1, 0, useZ * 1); 86 | if (!r.containsVector(tmpLoc.getLocation())) { 87 | return; 88 | } 89 | Logging.finest("Moving Right/Left: " + this.locationManipulation.strCoordsRaw(tmpLoc.getLocation())); 90 | doFill(tmpLoc, useX, useZ, r, type); 91 | } 92 | if (isValidPortalRegion(newLoc.getRelative(useX * 0, 1, useZ * 0).getLocation(), type)) { 93 | Block tmpLoc = newLoc.getRelative(useX * 0, 1, useZ * 0); 94 | if (!r.containsVector(tmpLoc.getLocation())) { 95 | return; 96 | } 97 | Logging.finest("Moving Up" + this.locationManipulation.strCoordsRaw(tmpLoc.getLocation())); 98 | doFill(tmpLoc, useX, useZ, r, type); 99 | } 100 | if (isValidPortalRegion(newLoc.getRelative(useX * -1, 0, useZ * -1).getLocation(), type)) { 101 | Block tmpLoc = newLoc.getRelative(useX * -1, 0, useZ * -1); 102 | if (!r.containsVector(tmpLoc.getLocation())) { 103 | return; 104 | } 105 | Logging.finest("Moving Left/Right" + this.locationManipulation.strCoordsRaw(tmpLoc.getLocation())); 106 | doFill(tmpLoc, useX, useZ, r, type); 107 | } 108 | if (isValidPortalRegion(newLoc.getRelative(useX * 0, -1, useZ * 0).getLocation(), type)) { 109 | Block tmpLoc = newLoc.getRelative(useX * 0, -1, useZ * 0); 110 | if (!r.containsVector(tmpLoc.getLocation())) { 111 | return; 112 | } 113 | Logging.finest("Moving Down" + this.locationManipulation.strCoordsRaw(tmpLoc.getLocation())); 114 | doFill(tmpLoc, useX, useZ, r, type); 115 | } 116 | } 117 | 118 | /** 119 | * @param l 120 | * @param portalType 121 | * 122 | * @return 123 | */ 124 | private boolean isValidPortalRegion(Location l, Material portalType) { 125 | Material type = l.getBlock().getType(); 126 | if (l.getWorld().getBlockAt(l).getType() == portalType) { 127 | return false; 128 | } 129 | return MVPortal.isPortalInterior(type); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Multiverse 2 Copyright (c) the Multiverse Team 2011. 3 | * Multiverse 2 is licensed under the BSD License. 4 | * For more information please check the README.md file included 5 | * with this project 6 | */ 7 | 8 | package org.mvplugins.multiverse.portals.utils; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import org.bukkit.Location; 17 | import org.bukkit.Material; 18 | import org.bukkit.World; 19 | import org.bukkit.block.Block; 20 | import org.bukkit.command.CommandSender; 21 | import org.bukkit.configuration.file.FileConfiguration; 22 | import org.bukkit.entity.Player; 23 | import org.bukkit.permissions.Permission; 24 | import org.bukkit.util.Vector; 25 | 26 | import org.mvplugins.multiverse.core.world.MultiverseWorld; 27 | import org.mvplugins.multiverse.core.world.WorldManager; 28 | import org.mvplugins.multiverse.external.jakarta.inject.Inject; 29 | import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; 30 | import org.jvnet.hk2.annotations.Service; 31 | import org.mvplugins.multiverse.portals.MVPortal; 32 | import org.mvplugins.multiverse.portals.MultiversePortals; 33 | import org.mvplugins.multiverse.portals.PortalLocation; 34 | 35 | 36 | /** 37 | * Manages all portals for all worlds. 38 | * 39 | * @author fernferret 40 | */ 41 | @Service 42 | public class PortalManager { 43 | private final MultiversePortals plugin; 44 | private final WorldManager worldManager; 45 | private final Map portals; 46 | 47 | // For each world, keep a map of chunk hashes (see hashChunk()) to lists of 48 | // portals in those chunks. 49 | private final Map>> worldChunkPortals; 50 | 51 | // getNearbyPortals() returns this instead of null. =) 52 | private static final Collection emptyPortalSet = new ArrayList(); 53 | 54 | @Inject 55 | PortalManager(@NotNull MultiversePortals plugin, @NotNull WorldManager worldManager) { 56 | this.plugin = plugin; 57 | this.worldManager = worldManager; 58 | this.portals = new HashMap<>(); 59 | this.worldChunkPortals = new HashMap<>(); 60 | } 61 | 62 | /** 63 | * Method that checks to see if a player is inside a portal that they have permission to use. 64 | * 65 | * @param sender The sender to check. 66 | * @param l The location they're standing. 67 | * 68 | * @return A MVPortal if it's valid, null if not. 69 | */ 70 | public MVPortal getPortal(Player sender, Location l) { 71 | return getPortal(sender, l, true); 72 | } 73 | 74 | /** 75 | * Method that checks to see if a player is inside a portal and optionally ensure they have 76 | * permission to use. 77 | * 78 | * @param sender The sender to check. 79 | * @param l The location they're standing. 80 | * @param checkPermission The {@link MVPortal} is returned only if player has permission to access it. 81 | * 82 | * @return A MVPortal if it's valid, null if not. 83 | */ 84 | public MVPortal getPortal(Player sender, Location l, boolean checkPermission) { 85 | if (!this.worldManager.isLoadedWorld(l.getWorld().getName())) { 86 | return null; 87 | } 88 | 89 | MultiverseWorld world = this.worldManager.getLoadedWorld(l.getWorld().getName()).getOrNull(); 90 | for (MVPortal portal : getNearbyPortals(world, l)) { 91 | 92 | // Ignore portals the player can't use. 93 | if (!checkPermission || !MultiversePortals.EnforcePortalAccess || portal.playerCanEnterPortal(sender)) { 94 | PortalLocation portalLoc = portal.getLocation(); 95 | if (portalLoc.isValidLocation() && portalLoc.getRegion().containsVector(l)) { 96 | return portal; 97 | } 98 | } 99 | } 100 | 101 | return null; 102 | } 103 | /** 104 | * Deprecated, use getPortal instead. 105 | * @deprecated 106 | */ 107 | @Deprecated 108 | public MVPortal isPortal(Player sender, Location l) { 109 | return this.getPortal(sender, l); 110 | } 111 | 112 | /** 113 | * Simplified method for seeing if someone is in a portal. We'll check perms later. 114 | * 115 | * @param l The location of the player 116 | * 117 | * @return True if it is a valid portal location. 118 | */ 119 | public boolean isPortal(Location l) { 120 | return this.getPortal(l) != null; 121 | } 122 | 123 | /** 124 | * Return a portal at a location. 125 | * NOTE: If there are more than one portal, order is effectively indeterminate. 126 | * @param l The location to check at 127 | * @return Null if no portal found, otherwise the MVPortal at that location. 128 | */ 129 | public MVPortal getPortal(Location l) { 130 | MultiverseWorld world = this.worldManager.getLoadedWorld(l.getWorld().getName()).getOrNull(); 131 | for (MVPortal portal : getNearbyPortals(world, l)) { 132 | MultiverseRegion r = portal.getLocation().getRegion(); 133 | if (r != null && r.containsVector(l)) { 134 | return portal; 135 | } 136 | } 137 | return null; 138 | } 139 | 140 | public boolean addPortal(MVPortal portal) { 141 | if (!this.portals.containsKey(portal.getName())) { 142 | MultiverseWorld world = this.worldManager.getLoadedWorld(portal.getWorld()).getOrNull(); 143 | addUniquePortal(world, portal.getName(), portal); 144 | return true; 145 | } 146 | return false; 147 | } 148 | 149 | public boolean addPortal(MultiverseWorld world, String name, String owner, PortalLocation location) { 150 | if (!this.portals.containsKey(name)) { 151 | addUniquePortal(world, name, new MVPortal(this.plugin, name, owner, location)); 152 | return true; 153 | } 154 | return false; 155 | } 156 | 157 | // Add a portal whose name is already known to be unique. 158 | private void addUniquePortal(MultiverseWorld world, String name, MVPortal portal) { 159 | this.portals.put(name, portal); 160 | addToWorldChunkPortals(world, portal); 161 | } 162 | 163 | public MVPortal removePortal(String portalName, boolean removeFromConfigs) { 164 | return removePortal(portalName, removeFromConfigs, false); 165 | } 166 | 167 | private MVPortal removePortal(String portalName, boolean removeFromConfigs, boolean delayRecalculation) { 168 | if (!isPortal(portalName)) { 169 | return null; 170 | } 171 | if (removeFromConfigs) { 172 | FileConfiguration config = this.plugin.getPortalsConfig(); 173 | config.set("portals." + portalName, null); 174 | this.plugin.savePortalsConfig(); 175 | } 176 | 177 | MVPortal removed = this.portals.remove(portalName); 178 | MultiverseWorld world = this.worldManager.getLoadedWorld(removed.getWorld()).getOrNull(); 179 | removeFromWorldChunkPortals(world, removed); 180 | 181 | removed.removePermission(); 182 | Permission portalAccess = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.access.*"); 183 | Permission exemptAccess = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.exempt.*"); 184 | Permission portalFill = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.fill.*"); 185 | if (exemptAccess != null) { 186 | exemptAccess.getChildren().remove(removed.getExempt().getName()); 187 | } 188 | if (portalAccess != null) { 189 | portalAccess.getChildren().remove(removed.getPermission().getName()); 190 | } 191 | if (portalFill != null) { 192 | portalFill.getChildren().remove(removed.getFillPermission().getName()); 193 | } 194 | 195 | if (!delayRecalculation) { 196 | recalculatePermissions(); 197 | } 198 | 199 | if (MultiversePortals.ClearOnRemove) { 200 | // Replace portal blocks in the portal with air. This keeps us from 201 | // leaving behind portal blocks (which would take an unsuspecting 202 | // player to the nether instead of their expected destination). 203 | 204 | MultiverseRegion region = removed.getLocation().getRegion(); 205 | replaceInRegion(removed.getWorld(), region, Material.NETHER_PORTAL, Material.AIR); 206 | } 207 | this.plugin.getServer().getPluginManager().removePermission(removed.getPermission()); 208 | this.plugin.getServer().getPluginManager().removePermission(removed.getExempt()); 209 | this.plugin.getServer().getPluginManager().removePermission(removed.getFillPermission()); 210 | return removed; 211 | } 212 | 213 | private void recalculatePermissions() { 214 | String[] permissionsNames = new String[] { "multiverse.portal.access.*", "multiverse.portal.exempt.*", "multiverse.portal.fill.*" }; 215 | for (String permissionName : permissionsNames) { 216 | Permission permission = this.plugin.getServer().getPluginManager().getPermission(permissionName); 217 | this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(permission); 218 | } 219 | } 220 | 221 | public List getAllPortals() { 222 | return new ArrayList(this.portals.values()); 223 | } 224 | 225 | public List getPortals(CommandSender sender) { 226 | if (!(sender instanceof Player)) { 227 | return this.getAllPortals(); 228 | } 229 | List all = this.getAllPortals(); 230 | List validItems = new ArrayList(); 231 | if (MultiversePortals.EnforcePortalAccess) { 232 | for (MVPortal p : all) { 233 | if (p.playerCanEnterPortal((Player) sender)) { 234 | validItems.add(p); 235 | } 236 | } 237 | } else { 238 | validItems = new ArrayList(all); 239 | } 240 | return validItems; 241 | } 242 | 243 | private List getPortals(MultiverseWorld world) { 244 | List all = this.getAllPortals(); 245 | List validItems = new ArrayList(); 246 | for (MVPortal p : all) { 247 | MultiverseWorld portalworld = p.getLocation().getMVWorld(); 248 | if (portalworld != null && portalworld.equals(world)) { 249 | validItems.add(p); 250 | } 251 | } 252 | return validItems; 253 | } 254 | 255 | public List getPortals(CommandSender sender, MultiverseWorld world) { 256 | if (!(sender instanceof Player)) { 257 | return this.getPortals(world); 258 | } 259 | List all = this.getAllPortals(); 260 | List validItems = new ArrayList(); 261 | if (MultiversePortals.EnforcePortalAccess) { 262 | for (MVPortal p : all) { 263 | if (p.getLocation().isValidLocation() && p.getLocation().getMVWorld().equals(world) && 264 | p.playerCanEnterPortal((Player) sender)) { 265 | validItems.add(p); 266 | } 267 | } 268 | } else { 269 | validItems = new ArrayList(all); 270 | } 271 | return validItems; 272 | } 273 | 274 | public MVPortal getPortal(String portalName) { 275 | // Returns null if the portal doesn't exist. 276 | return this.portals.get(portalName); 277 | } 278 | 279 | /** 280 | * Gets a portal with a commandsender and a name. Used as a convenience for portal listing methods 281 | * 282 | * @param portalName 283 | * @param sender 284 | * 285 | * @return 286 | */ 287 | public MVPortal getPortal(String portalName, CommandSender sender) { 288 | if (!sender.hasPermission("multiverse.portal.access." + portalName)) { 289 | return null; 290 | } 291 | return this.getPortal(portalName); 292 | } 293 | 294 | public boolean isPortal(String portalName) { 295 | return this.portals.containsKey(portalName); 296 | } 297 | 298 | public void removeAll(boolean removeFromConfigs) { 299 | List iterList = new ArrayList(this.portals.keySet()); 300 | for (String s : iterList) { 301 | this.removePortal(s, removeFromConfigs, true); 302 | } 303 | recalculatePermissions(); 304 | } 305 | 306 | private void replaceInRegion(World world, MultiverseRegion removedRegion, Material oldMaterial, Material newMaterial) { 307 | // Determine the bounds of the region. 308 | Vector min = removedRegion.getMinimumPoint(); 309 | Vector max = removedRegion.getMaximumPoint(); 310 | int minX = min.getBlockX(), minY = min.getBlockY(), minZ = min.getBlockZ(); 311 | int maxX = max.getBlockX(), maxY = max.getBlockY(), maxZ = max.getBlockZ(); 312 | 313 | for (int x = minX; x <= maxX; x++) { 314 | for (int y = minY; y <= maxY; y++) { 315 | for (int z = minZ; z <= maxZ; z++) { 316 | Block b = world.getBlockAt(x, y, z); 317 | if (b.getType() == oldMaterial) { 318 | b.setType(newMaterial, false); 319 | } 320 | } 321 | } 322 | } 323 | } 324 | 325 | private int blockToChunk(int b) { 326 | // A block at -5 should be in chunk -1 instead of chunk 0. 327 | if (b < 0) { 328 | b -= 16; 329 | } 330 | return b / 16; 331 | } 332 | 333 | private int hashChunk(int cx, int cz) { 334 | return (cx << 16) | (cz & 0xFFFF); 335 | } 336 | 337 | private void addToWorldChunkPortals(MultiverseWorld world, MVPortal portal) { 338 | 339 | Map> chunksToPortals = this.worldChunkPortals.get(world); 340 | if (chunksToPortals == null) { 341 | chunksToPortals = new HashMap>(); 342 | this.worldChunkPortals.put(world, chunksToPortals); 343 | } 344 | 345 | // If this portal spans multiple chunks, we'll add it to each chunk that 346 | // contains part of it. 347 | PortalLocation location = portal.getLocation(); 348 | Vector min = location.getMinimum(); 349 | Vector max = location.getMaximum(); 350 | int c1x = blockToChunk(min.getBlockX()), c1z = blockToChunk(min.getBlockZ()); 351 | int c2x = blockToChunk(max.getBlockX()), c2z = blockToChunk(max.getBlockZ()); 352 | for (int cx = c1x; cx <= c2x; cx++) { 353 | for (int cz = c1z; cz <= c2z; cz++) { 354 | Integer hashCode = hashChunk(cx, cz); 355 | Collection portals = chunksToPortals.get(hashCode); 356 | if (portals == null) { 357 | // For this collection, iteration will be -much- more common 358 | // than addition or removal. ArrayList has better iteration 359 | // performance than HashSet. 360 | portals = new ArrayList(); 361 | chunksToPortals.put(hashCode, portals); 362 | } 363 | portals.add(portal); 364 | } 365 | } 366 | } 367 | 368 | private void removeFromWorldChunkPortals(MultiverseWorld world, MVPortal portal) { 369 | Map> chunksToPortals = this.worldChunkPortals.get(world); 370 | 371 | if (chunksToPortals == null) { 372 | // 'world' might be a new instance of an adventure world that's 373 | // being reloaded. If that's the case, the world object won't be 374 | // found in worldChunkPortals. 375 | return; 376 | } 377 | 378 | PortalLocation location = portal.getLocation(); 379 | Vector min = location.getMinimum(); 380 | Vector max = location.getMaximum(); 381 | int c1x = blockToChunk(min.getBlockX()), c1z = blockToChunk(min.getBlockZ()); 382 | int c2x = blockToChunk(max.getBlockX()), c2z = blockToChunk(max.getBlockZ()); 383 | 384 | for (int cx = c1x; cx <= c2x; cx++) { 385 | for (int cz = c1z; cz <= c2z; cz++) { 386 | Integer hashCode = hashChunk(cx, cz); 387 | chunksToPortals.get(hashCode).remove(portal); 388 | } 389 | } 390 | } 391 | 392 | /** 393 | * Returns portals in the same chunk as the given location. 394 | * 395 | * @param location the location 396 | * @return a collection of nearby portals; may be empty, but will not be null 397 | */ 398 | private Collection getNearbyPortals(MultiverseWorld world, Location location) { 399 | 400 | Collection nearbyPortals = null; 401 | 402 | Map> chunkMap = this.worldChunkPortals.get(world); 403 | if (chunkMap != null) { 404 | int cx = blockToChunk(location.getBlockX()); 405 | int cz = blockToChunk(location.getBlockZ()); 406 | Integer hash = hashChunk(cx, cz); 407 | 408 | nearbyPortals = chunkMap.get(hash); 409 | } 410 | 411 | // Never return null. (This just keeps the caller from having to do a 412 | // null check.) 413 | return nearbyPortals != null ? nearbyPortals : emptyPortalSet; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/main/resources/defaults/config.yml: -------------------------------------------------------------------------------- 1 | # This is the MV-Portals Config. If you mess it up, copy the values out 2 | # delete it, and it will be regenerated. Then use the ingame interface 3 | # to add your values back via the "/mvp conf" command. 4 | # When in-game, simply type: "/mvp conf ?" for help. 5 | # A config with explanations can be found here: 6 | # https://github.com/Multiverse/Multiverse-Core/wiki/config.yml-(Portals) 7 | 8 | wand: 271 9 | useonmove: true 10 | bucketfilling: true 11 | portalsdefaulttonether: false 12 | enforceportalaccess: true 13 | portalcooldown: 1000 14 | clearonremove: false 15 | teleportvehicles: true 16 | netheranimation: true 17 | framematerials: [] 18 | version: 2.7 19 | -------------------------------------------------------------------------------- /src/main/resources/defaults/portals.yml: -------------------------------------------------------------------------------- 1 | # This is the new portals file. 2 | # You apparently haven't created or imported any portals. 3 | # Otherwise, they'd be in here. 4 | portals: {} -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Multiverse-Portals 2 | main: org.mvplugins.multiverse.portals.MultiversePortals 3 | authors: ['Rigby', 'fernferret', 'benwoo1110'] 4 | version: ${version} 5 | api-version: 1.13 6 | depend: ['Multiverse-Core'] 7 | softdepend: ['WorldEdit'] 8 | --------------------------------------------------------------------------------