Compare commits

..
Author SHA1 Message Date
Ben Woo 1211dfb057 Implement suggested improvements.
* Use singleton pattern for DefaultContentFilter with getInstance method.
* Have a default SendHandler.
* Don't need streams for small dataset.
* Private WorldListContentParser class to improve readability.
2021-08-17 16:29:31 +08:00
Ben Woo 562eed8255 Revamp content display API. 2021-08-17 00:26:57 +08:00
Ben Woo 1a8a97daf1 Merge pull request #2675 from Multiverse/replace_travis_badge
Replace Travis Badge with Github Actions.
2021-07-22 10:12:35 +08:00
nicegamer7 d9dffa8e6d replace travis badge 2021-07-21 22:11:09 -04:00
nicegamer7 973588defb Merge pull request #2175 from JLLeitschuh/fix/JLL/use_https_to_resolve_dependencies
[SECURITY] Use HTTPS to resolve dependencies in Maven Build
2021-07-19 20:14:17 -04:00
nicegamer7 c704c1ca3a Merge branch 'main' into fix/JLL/use_https_to_resolve_dependencies 2021-07-19 20:12:25 -04:00
Ben Woo c33a268b91 Merge pull request #2672 from Multiverse/fix_java16_tests
Fix Tests on Java 16 and code cleanup.
2021-07-19 10:31:31 +08:00
Kermina Awad 9821f1d9c8 clean up test files 2021-07-18 13:51:53 -04:00
Kermina Awad be6e0b67be don't run tests in parallel 2021-07-18 13:22:04 -04:00
Kermina Awad b9b1fa6164 remove PowerMockito 2021-07-18 13:17:15 -04:00
Kermina Awad 28585dd6f9 remove unnecessary stub 2021-07-18 13:12:09 -04:00
Ben Woo 47ca7b821b Move to GitHub actions (#2671)
* Remove use of travis.

* Add basic github action maven build.
2021-07-17 09:31:20 +08:00
nicegamer7 9ce2dfd100 Merge pull request #2445 from benwoo1110/fix-help
Fix issue where special chars cause PatternSyntaxException
2021-07-16 09:51:21 -04:00
Ben Woo 473ccd09d7 Don't need to toLowerCase filter. 2021-07-16 21:38:57 +08:00
Ben WooandJeremy Wood cc2e1d44b2 Implement new content display system. (#2584)
* Implement new content display system.

* Add @FunctionalInterface annotation to displaytools interfaces.

* Use static factory method to create Builder with contents.

* Use T for generic type since it's static method.

* Rename Builder#display to show and require CommandSender.

* Rename package from displaytools to display.

* Move DisplayHandler impls into their own package.

* Overload ContentDisplay#forContent with defaults for list and map.

* Pass CommandSender to send command.

This system is much more versatile when a single ContentDisplay instance
can be used for multiple players.

* Rename ContentDisplay#send to #show.

* Split DisplaySettings into separate classes.

Co-authored-by: Jeremy Wood <farachan@gmail.com>
2021-07-07 10:25:07 +08:00
Jeremy Wood 2e8f159d38 Merge pull request #2666 from Multiverse/https
Ensure https is used for repository.
2021-07-06 14:12:46 -04:00
Jeremy Wood 6581d16125 Merge pull request #2519 from Multiverse/readme-update
Update readme with more project details
2021-07-06 13:59:35 -04:00
Jeremy Wood 562bc56e83 Merge pull request #2664 from ccuser44/main
Create LICENSE.md
2021-07-06 13:56:16 -04:00
Ben Woo 4493cd7916 Ensure https is used for repository. 2021-07-06 23:27:53 +08:00
nicegamer7 1cd9e45915 Merge pull request #2663 from Multiverse/fix-portal-search
Set defaultportalsearch to true by default.
2021-07-05 15:16:01 -04:00
ccuser44 acb6bc6dd6 Create LICENSE.md 2021-07-05 11:39:50 +03:00
Ben Woo b7b17c5d21 Set defaultportalsearch to true by default. 2021-07-05 11:28:52 +08:00
Jeremy Wood bbd9211845 Bump version to 4.3.1-SNAPSHOT for continued development. 2021-06-23 15:47:52 -04:00
Jeremy Wood fc6796e20f Release version 4.3.0. 2021-06-23 08:56:24 -04:00
Jeremy Wood 6040a27126 Merge pull request #2635 from Multiverse/fix/version_cmd_perms
Change default permission for version command. Seriously wth.
2021-06-04 13:00:39 -04:00
Jeremy Wood 1c483148dc Change default permission for version command. Seriously wth. 2021-06-04 13:00:04 -04:00
nicegamer7 128442c062 Merge pull request #2622 from Multiverse/queue-system-cleanup
Queue system cleanup
2021-05-16 11:29:23 -04:00
Kermina Awad 141854f076 correct some javadoc typos 2021-05-16 11:21:20 -04:00
xSavior_of_God f72cc6764b Fix creation and import Exploit (#2353)
Through this fix you can avoid deleting of important folders.
2021-05-16 12:56:14 +08:00
benwoo1110 d95ecfaa8e Update some CommandQueueManager docs to be clearer. 2021-04-28 14:01:30 +08:00
benwoo1110 6c96f4e974 Ensure DummyCommandBlockSender isnt used for Op. 2021-04-28 13:58:43 +08:00
Ben Woo 28cee291c6 Merge pull request #2581 from Multiverse/regen-keep-gamerules
Add ability to keep gamerules setting on world regen.
2021-04-28 10:43:09 +08:00
Ben Woo 155f32c00f Add ability to keep gamerules on world regen. 2021-04-27 23:44:24 +08:00
Ben Woo 071ba05d81 Merge pull request #2585 from Multiverse/Revamp-queue-command-
Implement new queue command system.
2021-04-27 23:10:59 +08:00
Ben Woo 2ee5aea06b Merge pull request #2580 from Multiverse/per-search-and-reflect
Improve portal search radius
2021-04-27 10:57:46 +08:00
Ben Woo 3bb8de6d33 Improve portal search radius.
* Fix portal search radius not respected after travel agent removed.
* Set portal search radius for entity entering portals.
2021-04-26 22:52:24 +08:00
Ben Woo 79ac01da34 Merge pull request #2619 from Multiverse/general_cleanup
General Cleanup
2021-04-25 11:55:56 +08:00
Kermina Awad 6e4fbb0ece correct a copy-paste error introduced in #2588 2021-04-24 14:51:24 -04:00
Kermina Awad 167ed5e0d3 update VaultAPI repo 2021-04-24 14:49:28 -04:00
Kermina Awad 91f3304f78 add to Buscript failed to load message 2021-04-24 14:13:40 -04:00
Kermina Awad 7928e73a32 enable minimizeJar for maven-shade-plugin 2021-04-24 14:08:25 -04:00
Kermina Awad d61d186544 update bStats Metrics class 2021-04-24 14:08:09 -04:00
nicegamer7 dc356e88d0 Merge pull request #2582 from Multiverse/unload-message
Update unload message logging.
2021-04-24 13:49:12 -04:00
Kermina Awad d0d5574580 update unload message logging another time 2021-04-24 13:48:09 -04:00
Ben Woo 29617059b3 Merge pull request #2611 from Turbotailz/patch-1
Make prefixchat config option default to 'false'
2021-04-06 13:10:02 +08:00
Sam Goodger bf05fc81dd Make prefixchat config option default to 'false'
This may seem like a really negligible change but having this option default to `true` actually causes a lot of pain for support in other plugins that deal with prefixes (LuckPerms and Essentials being the main victims). Users always come in guns blazing, screaming at us because they don't know how to remove the world name from their chat. It's such a simple solution, yet no one thinks that of all plugins, it would be Multiverse that is tainting their beloved chat format.

Please consider updating this default setting. It will save a lot of us trouble in the future 🙏
2021-04-06 14:19:38 +10:00
nicegamer7 a2c5bf70f0 Merge pull request #2597 from Multiverse/cleanup
Cleanup
2021-03-18 10:52:28 -04:00
benwoo1110 ea19006465 Fix selector log level. 2021-03-18 16:40:47 +08:00
benwoo1110 89f685ff9a Add missing javadocs in DestinationFactory. 2021-03-10 17:45:39 +08:00
benwoo1110 ecc76033dc Remove redundant line that was moved to WorldNameChecker. 2021-03-10 17:41:56 +08:00
Ben Woo e843b0711d Merge pull request #2590 from Multiverse/small-wm-updates
Small WorldManager updates
2021-03-10 06:41:18 +08:00
Ben Woo eddfaf7df0 Merge pull request #2593 from Multiverse/destination-id
Add method to get registered destination identifiers.
2021-03-10 06:39:31 +08:00
nicegamer7 d1150ab5f8 Merge pull request #2591 from Multiverse/uuid-regex
Make UUID regex less restrictive.
2021-03-09 09:46:33 -05:00
benwoo1110 cc3996ca76 Implement new queue command system. 2021-03-09 20:54:42 +08:00
benwoo1110 1965aecfba Add method to get registered destination identifiers. 2021-03-09 20:05:23 +08:00
benwoo1110 ce34ae44d8 Make UUID regex less restrictive. 2021-03-09 11:48:26 +08:00
benwoo1110 f5a2d7bc47 Add getPotentialWorlds method to WorldManager. 2021-03-09 10:40:46 +08:00
benwoo1110 24295025ef Add option to lookup mvworlds without checking for aliases. 2021-03-09 10:35:42 +08:00
benwoo1110 b23c1ae606 Update unload message logging. 2021-03-04 16:00:53 +08:00
benwoo1110 9741b1794c Update readme with more project details 2020-12-31 00:42:22 +08:00
benwoo1110 5d3d732c25 Fix issue where special chars cause PatternSyntaxException
Escape regex special chars for help command
2020-10-09 12:19:32 +08:00
Jonathan Leitschuh 59b6522d59 Use HTTPS instead of HTTP to resolve dependencies
This fixes a security vulnerability in this project where the `pom.xml`
files were configuring Maven to resolve dependencies over HTTP instead of
HTTPS.

Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
2020-02-11 09:04:52 -05:00
61 changed files with 1913 additions and 1205 deletions

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
name: Maven CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn -B package --file pom.xml
-6
View File
@@ -1,6 +0,0 @@
language: java
jdk:
- oraclejdk8
notifications:
email: false
dist: trusty
+6
View File
@@ -0,0 +1,6 @@
Copyright (c) 2011, The Multiverse Team All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
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.
+35 -4
View File
@@ -1,11 +1,43 @@
![Multiverse 2](config/multiverse2-long.png)
[![Maven CI/CD](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml/badge.svg)](https://github.com/Multiverse/Multiverse-Core/actions/workflows/build.yml)
[![Release](https://img.shields.io/nexus/r/com.onarandombox.multiversecore/Multiverse-Core?label=release&server=https%3A%2F%2Frepo.onarandombox.com%2F)](https://dev.bukkit.org/projects/multiverse-core)
[![Dev builds](https://img.shields.io/nexus/s/com.onarandombox.multiversecore/Multiverse-Core?label=dev%20builds&server=http%3A%2F%2Frepo.onarandombox.com%2F)](https://ci.onarandombox.com/job/Multiverse-Core/)
[![Discord](https://img.shields.io/discord/325459248047980545?label=discord&logo=discord)](https://discord.gg/NZtfKky)
[![Support me 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)
About
========
[Multiverse](https://dev.bukkit.org/projects/multiverse-core) was created at the dawn of Bukkit multiworld support. It has since then grown into a **complete world management solution!** Multiverse provides the easiest to use world management solution for your Minecraft server, big or small, and with great addons like [Portals](https://dev.bukkit.org/projects/multiverse-portals) and [NetherPortals](https://dev.bukkit.org/projects/multiverse-netherportals/), what's not to love!
<br><br>
Now it's time to create your very own Multiverse server, do check out our [Wiki](https://github.com/Multiverse/Multiverse-Core/wiki) and [Usage Guide](https://github.com/Multiverse/Multiverse-Core/wiki/Basics) to get started. Feel free to hop onto our [Discord](https://discord.gg/NZtfKky) if you have any question or just want to have a chat with us!
### Amazing sub-modules available:
* [Multiverse-NetherPortals](https://github.com/Multiverse/Multiverse-NetherPortals) -> Have separate nether and end worlds for each of your overworlds!
* [Multiverse-Portals](https://github.com/Multiverse/Multiverse-Portals) -> Make custom portals to go to any destination!
* [Multiverse-Inventories](https://github.com/Multiverse/Multiverse-Inventories) -> Have separated players stats and inventories per world or per group of worlds.
* [Multiverse-SignPortals](https://github.com/Multiverse/Multiverse-SignPortals) -> Signs as teleprompters!
Building
========
Simply build the source with maven:
$ mvn install
```
$ mvn install
```
More details are available on the [build instructions wiki page](https://github.com/Multiverse/Multiverse-Core/wiki/Building).
Contributing
=======
**Want to help improve Multiverse?** There are several ways you can support and contribute to the project.
* Take a look at our "Bug: Unconfirmed" issues, where you can find issues that need extra testing and investigation.
* Want others to love Multiverse too? You can join the [Multiverse Discord community](https://discord.gg/NZtfKky) and help others with issues and setup!
* A Multiverse guru? You can update our [Wiki](https://github.com/Multiverse/Multiverse-Core/wiki) with your latest tip, tricks and guides! The wiki open for all to edit and improve.
* Love coding? You could look at ["State: Open to PR"](https://github.com/Multiverse/Multiverse-Core/labels/State%3A%20Open%20to%20PR) and ["Resolution: Accepted"](https://github.com/Multiverse/Multiverse-Core/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/).
* 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)!
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!
License
=======
Copyright (c) 2011, The Multiverse Team All rights reserved.
@@ -13,4 +45,3 @@ Copyright (c) 2011, The Multiverse Team All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
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.
+14 -46
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.onarandombox.multiversecore</groupId>
<artifactId>Multiverse-Core</artifactId>
<version>4.2.3-SNAPSHOT</version>
<version>4.3.1-SNAPSHOT</version>
<name>Multiverse-Core</name>
<description>World Management Plugin</description>
<properties>
@@ -20,31 +20,27 @@
</repository>
<repository>
<id>onarandombox</id>
<url>http://repo.onarandombox.com/content/groups/public</url>
<url>https://repo.onarandombox.com/content/groups/public</url>
</repository>
<repository>
<id>spigot</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>minebench-repo</id>
<url>https://repo.minebench.de/</url>
</repository>
<repository>
<id>CodeMC</id>
<url>https://repo.codemc.org/repository/maven-public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>doodleproject-repo</id>
<name>DoodleProject Maven 2 Repository</name>
<url>http://doodleproject.sourceforge.net/maven2/release</url>
<url>https://doodleproject.sourceforge.net/maven2/release</url>
<releases>
<enabled>true</enabled>
</releases>
@@ -53,7 +49,7 @@
<ciManagement>
<system>jenkins</system>
<url>http://ci.onarandombox.com</url>
<url>https://ci.onarandombox.com</url>
</ciManagement>
<!-- Profiles are used to detect whether this is a local or Jenkins build
@@ -158,8 +154,6 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<parallel>methods</parallel>
<threadCount>10</threadCount>
<excludes>
<exclude>**/TestCommandSender.java</exclude>
<exclude>**/TestInstanceCreator.java</exclude>
@@ -223,6 +217,7 @@
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>me.main__.util</pattern>
@@ -305,7 +300,7 @@
<!-- End of SerializationConfig Dependency -->
<!-- Start of Economy Dependency -->
<dependency>
<groupId>net.milkbowl.vault</groupId>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
@@ -325,13 +320,7 @@
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>1.8</version>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.dumptruckman.minecraft</groupId>
@@ -357,30 +346,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-easymock</artifactId>
<version>2.0.9</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.0.2</version>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -393,11 +361,11 @@
<distributionManagement>
<repository>
<id>OnARandomBox</id>
<url>http://repo.onarandombox.com/content/repositories/multiverse</url>
<url>https://repo.onarandombox.com/content/repositories/multiverse</url>
</repository>
<snapshotRepository>
<id>OnARandomBox</id>
<url>http://repo.onarandombox.com/content/repositories/multiverse-snapshots/</url>
<url>https://repo.onarandombox.com/content/repositories/multiverse-snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
@@ -68,6 +68,7 @@ import com.onarandombox.MultiverseCore.commands.TeleportCommand;
import com.onarandombox.MultiverseCore.commands.UnloadCommand;
import com.onarandombox.MultiverseCore.commands.VersionCommand;
import com.onarandombox.MultiverseCore.commands.WhoCommand;
import com.onarandombox.MultiverseCore.commandtools.queue.CommandQueueManager;
import com.onarandombox.MultiverseCore.destination.AnchorDestination;
import com.onarandombox.MultiverseCore.destination.BedDestination;
import com.onarandombox.MultiverseCore.destination.CannonDestination;
@@ -77,8 +78,6 @@ import com.onarandombox.MultiverseCore.destination.PlayerDestination;
import com.onarandombox.MultiverseCore.destination.WorldDestination;
import com.onarandombox.MultiverseCore.event.MVDebugModeEvent;
import com.onarandombox.MultiverseCore.event.MVVersionEvent;
import com.onarandombox.MultiverseCore.generators.GeneratorManager;
import com.onarandombox.MultiverseCore.api.MVGeneratorManager;
import com.onarandombox.MultiverseCore.listeners.MVAsyncPlayerChatListener;
import com.onarandombox.MultiverseCore.listeners.MVChatListener;
import com.onarandombox.MultiverseCore.listeners.MVEntityListener;
@@ -206,6 +205,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
// Setup our Map for our Commands using the CommandHandler.
private CommandHandler commandHandler;
private CommandQueueManager commandQueueManager;
private static final String LOG_TAG = "[Multiverse-Core]";
@@ -215,7 +215,6 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
// Configurations
private FileConfiguration multiverseConfig = null;
private MVGeneratorManager generatorManager;
private final MVWorldManager worldManager = new WorldManager(this);
// Setup the block/player/entity listener.
@@ -283,7 +282,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
this.messaging = new MVMessaging();
this.economist = new MVEconomist(this);
// Load the defaultWorldGenerators
this.generatorManager = new GeneratorManager(this);
this.worldManager.getDefaultWorldGenerators();
this.registerEvents();
// Setup Permissions, we'll do an initial check for the Permissions plugin then fall back on isOP().
@@ -291,6 +290,7 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
// Setup the command manager
this.commandHandler = new CommandHandler(this, this.ph);
this.commandQueueManager = new CommandQueueManager(this);
// Call the Function to assign all the Commands to their Class.
this.registerCommands();
@@ -367,7 +367,9 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
// Add global variable "multiverse" to javascript environment
buscript.setScriptVariable("multiverse", this);
} catch (NullPointerException e) {
Logging.warning("Buscript failed to load! The script command will be disabled!");
Logging.warning("Buscript failed to load! The script command will be disabled! " +
"If you would like not to see this message, " +
"use `/mv conf enablebuscript false` to disable Buscript from loading.");
}
}
}
@@ -920,6 +922,15 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
return this.commandHandler;
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
public CommandQueueManager getCommandQueueManager() {
return commandQueueManager;
}
/**
* Gets the log-tag.
*
@@ -1017,14 +1028,6 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
this.serverFolder = newServerFolder;
}
/**
* {@inheritDoc}
*/
@Override
public MVGeneratorManager getMVGeneratorManager() {
return generatorManager;
}
/**
* {@inheritDoc}
*/
@@ -1128,12 +1131,22 @@ public class MultiverseCore extends JavaPlugin implements MVPlugin, Core {
/**
* {@inheritDoc}
* @deprecated This is deprecated!
* @deprecated This is deprecated! Do not use!
*/
@Override
@Deprecated
public Boolean regenWorld(String name, Boolean useNewSeed, Boolean randomSeed, String seed) {
return this.worldManager.regenWorld(name, useNewSeed, randomSeed, seed);
return this.worldManager.regenWorld(name, useNewSeed, randomSeed, seed, false);
}
/**
* {@inheritDoc}
* @deprecated This is deprecated! Do not use!
*/
@Override
@Deprecated
public Boolean regenWorld(String name, Boolean useNewSeed, Boolean randomSeed, String seed, Boolean keepGameRules) {
return this.worldManager.regenWorld(name, useNewSeed, randomSeed, seed, keepGameRules);
}
/**
@@ -96,7 +96,7 @@ public class MultiverseCoreConfiguration extends SerializationConfig implements
// BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck
enforceaccess = false;
useasyncchat = true;
prefixchat = true;
prefixchat = false;
prefixchatformat = "[%world%]%chat%";
teleportintercept = true;
firstspawnoverride = true;
@@ -107,7 +107,7 @@ public class MultiverseCoreConfiguration extends SerializationConfig implements
teleportcooldown = 1000;
this.version = 2.9;
silentstart = false;
defaultportalsearch = false;
defaultportalsearch = true;
portalsearchradius = 128;
autopurge = true;
idonotwanttodonate = false;
@@ -8,6 +8,7 @@
package com.onarandombox.MultiverseCore.api;
import buscript.Buscript;
import com.onarandombox.MultiverseCore.commandtools.queue.CommandQueueManager;
import com.onarandombox.MultiverseCore.destination.DestinationFactory;
import com.onarandombox.MultiverseCore.utils.AnchorManager;
import com.onarandombox.MultiverseCore.utils.MVEconomist;
@@ -86,6 +87,15 @@ public interface Core {
*/
CommandHandler getCommandHandler();
/**
* Manager for command that requires /mv confirm before execution.
*
* @return A non-null {@link CommandQueueManager}.
* @deprecated To be moved to new command manager in 5.0.0
*/
@Deprecated
CommandQueueManager getCommandQueueManager();
/**
* Gets the factory class responsible for loading many different destinations
* on demand.
@@ -94,13 +104,6 @@ public interface Core {
*/
DestinationFactory getDestFactory();
/**
* Gets the class responsible for managing world generators plugins installed on the server.
*
* @return A valid {@link MVGeneratorManager}.
*/
MVGeneratorManager getMVGeneratorManager();
/**
* Gets the primary class responsible for managing Multiverse Worlds.
*
@@ -123,20 +126,38 @@ public interface Core {
AnchorManager getAnchorManager();
/**
* Used by queued commands to regenerate a world on a delay.
* Previously used by queued commands to regenerate a world on a delay.
* Do not use api method for any other purpose.
*
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed IF the new seed should be random
* @param seed The seed of the world.
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed If the new seed should be random
* @param seed The seed of the world.
*
* @return True if success, false if fail.
*
* @deprecated Use {@link MVWorldManager#regenWorld(String, boolean, boolean, String)} instead.
* @deprecated Use {@link MVWorldManager#regenWorld(String, boolean, boolean, String, boolean)} instead.
*/
@Deprecated
Boolean regenWorld(String name, Boolean useNewSeed, Boolean randomSeed, String seed);
/**
* Used by queued commands to regenerate a world on a delay.
* Do not use api method for any other purpose.
*
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed If the new seed should be random
* @param seed The seed of the world.
* @param keepGameRules If GameRules should be kept on world regen.
*
* @return True if success, false if fail.
*
* @deprecated Use {@link MVWorldManager#regenWorld(String, boolean, boolean, String, boolean)} instead.
*/
@Deprecated
Boolean regenWorld(String name, Boolean useNewSeed, Boolean randomSeed, String seed, Boolean keepGameRules);
/**
* Decrements the number of plugins that have specifically hooked into core.
*/
@@ -1,101 +0,0 @@
package com.onarandombox.MultiverseCore.api;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* <p>A generator API for Multiverse.</p>
*
* <p>Any generator plugin can register themselves to Multiverse. This will provide Multiverse with addition
* information about your generator plugin such as generator id suggestions, example usages and link to more
* info on your generator plugin.</p>
*
* <p>To register your plugin, implement this class and see {@link MVGeneratorManager#register(GeneratorPlugin)}.</p>
*/
public interface GeneratorPlugin {
/**
* <p>Suggest possible generator ids. To be used in command tab-completion.</p>
*
* <p>These suggestions can be static without relying on currentIdInput, or dynamically changed based
* on the currentIdInput.</p>
*
* @param currentIdInput The current state user input. This may be null or empty if user has not started
* any input for generator id.
* @return Collection of suggested generator ids.
*/
@NotNull
Collection<String> suggestIds(@Nullable String currentIdInput);
/**
* <p>Gets a default or any valid {@link ChunkGenerator}.</p>
*
* <p>This is used on {@link MVGeneratorManager#register(GeneratorPlugin)} to prove to Multiverse that
* you are a valid generator plugin. If this returns null or throw an exception, your generator plugin
* will not be registered.</p>
*
* @return A {@link ChunkGenerator} if possible, else null.
* @throws Exception When an error occurs in getting {@link ChunkGenerator}.
*/
@Nullable
ChunkGenerator getDefaultChunkGenerator() throws Exception;
/**
* <p>Gets a chunk generator based on a world name and id specified.</p>
*
* <p>If this returns null or throw an exception, it will indicate to Multiverse that there is either an issue
* with your plugin, or the generator id given is invalid.</p>
*
* @param id Target generator id.
* @param worldName Target world name to use generator on.
* @return A {@link ChunkGenerator} if possible, else null.
* @throws Exception When an error occurs in getting a {@link ChunkGenerator}.
*/
@Nullable
ChunkGenerator getChunkGenerator(String id, String worldName) throws Exception;
/**
* <p>Gets command usages that users can try to generate a world with your generator plugin. Returning null means
* you do not wish to show any usage examples for your generator plugin.</p>
*
* <p>An example command: '/mv create myworld normal -g CoolGen:FunWorld'</p>
*
* <p>Notes on usage of this method:</p>
* <ul>
* <li>Feel free to have colors in your command usage, but not Multiverse won't parse color codes for you.</li>
* <li>Please include the starting slash '/' in your usage examples.</li>
* <li>We suggest keeping the usage to at most 5 examples.</li>
* <li>This should not be a full explanation on all your generator plugin, just basics usages to get people
* started. For full guide, you can use {@link #getInfoLink()} to direct users.</li>
* </ul>
*
* @return A collection of command usage examples.
*/
@Nullable
Collection<String> getExampleUsages();
/**
* <p>Gets a link with more information on your generator plugin. Returning null means you do not wish to link
* users to any website related to your generator plugin.</p>
*
* <p>An example info: 'Click on https://www.amazinggenerator.io ;)'</p>
*
* <p>Some suggested places you can link to are: spigot resource page, github repo or your own plugin site.</p>
*
* @return Link to more info on your generator plugin.
*/
@Nullable
String getInfoLink();
/**
* Gets the java plugin for this generator. In short, return your own generator plugin instance.
*
* @return The associated plugin for this generator.
*/
@NotNull
Plugin getPlugin();
}
@@ -1,134 +0,0 @@
package com.onarandombox.MultiverseCore.api;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
/**
* API for handling generator plugins installed on the user's server.
*/
public interface MVGeneratorManager {
/**
* Register a generator plugin.
*
* @param generatorPlugin The {@link GeneratorPlugin} to register.
* @return True if successfully registered, else false.
*/
boolean register(@NotNull GeneratorPlugin generatorPlugin);
/**
* Check if a given generator string is valid.
*
* @param genString The generator string to check on.
* @param worldName The target world name to generate on.
* @return True if valid, else false.
*/
boolean isValidGenerator(@NotNull String genString, @NotNull String worldName);
/**
* Check if a given generator string is valid with reason of failure with {@link TestResult}.
*
* @param genString The generator string to check on.
* @param worldName The target world name to generate on.
* @return Result of the validation.
*/
@NotNull
TestResult validateGenerator(@NotNull String genString, @NotNull String worldName);
/**
* Checks if a plugin is a registered generator plugin.
*
* @param genPlugin The plugin to check on.
* @return True if is valid, else false.
*/
boolean isGeneratorPlugin(@NotNull Plugin genPlugin);
/**
* Checks if a plugin is a registered generator plugin.
*
* @param pluginName The plugin name to check on.
* @return True if is valid, else false.
*/
boolean isGeneratorPlugin(@NotNull String pluginName);
/**
* Gets the {@link GeneratorPlugin} for a given plugin if present.
*
* @param genPlugin The plugin that is associated to a {@link GeneratorPlugin}.
* @return The {@link GeneratorPlugin} if present, else null.
*/
@Nullable
GeneratorPlugin getGeneratorPlugin(@NotNull Plugin genPlugin);
/**
* Gets the {@link GeneratorPlugin} for a given plugin if present.
*
* @param pluginName The plugin name that is associated to a {@link GeneratorPlugin}.
* @return The {@link GeneratorPlugin} if present, else null.
*/
@Nullable
GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName);
/**
* Gets all registered {@link GeneratorPlugin}.
*
* @return A collection of registered generator plugins.
*/
@NotNull
Collection<GeneratorPlugin> getGeneratorPlugins();
/**
* Gets all registered plugin names associate with {@link GeneratorPlugin}.
*
* @return A collection of registered generator plugin names.
*/
@NotNull
Collection<String> getGeneratorPluginNames();
/**
* Gets the default world generator string defined in 'bukkit.yml' for a given world.
*
* @param worldName The world to get default generator of.
* @return The generator string if present, else null.
*/
@Nullable
String getDefaultWorldGen(String worldName);
/**
* Gets all the world generator strings defined in 'bukkit.yml'.
*
* @return Map if world names and its respective default world generator strings.
*/
@NotNull
Map<String, String> getDefaultWorldGens();
/**
* Results from validating a generator.
*/
enum TestResult {
/**
* Successfully got {@link ChunkGenerator} from the world generator plugin.
*/
VALID,
/**
* Exceptions thrown while trying to get {@link ChunkGenerator} from the world generator plugin.
*/
ERRORS,
/**
* Plugin is present on the server, but doesnt look like a world generator.
*/
INVALID_GENERATOR,
/**
* Plugin not installed on the server.
*/
PLUGIN_DOES_NOT_EXIST
}
}
@@ -7,7 +7,6 @@
package com.onarandombox.MultiverseCore.api;
import com.onarandombox.MultiverseCore.generators.GeneratorManager;
import com.onarandombox.MultiverseCore.utils.PurgeWorlds;
import com.onarandombox.MultiverseCore.utils.SimpleWorldPurger;
import org.bukkit.World;
@@ -155,9 +154,7 @@ public interface MVWorldManager {
* @param generatorID The generator id.
* @param worldName The worldName to use as the default.
* @return A {@link ChunkGenerator} or null
* @deprecated Use {@link MVGeneratorManager#validateGenerator(String, String)}
*/
@Deprecated
ChunkGenerator getChunkGenerator(String generator, String generatorID, String worldName);
/**
@@ -177,6 +174,16 @@ public interface MVWorldManager {
*/
MultiverseWorld getMVWorld(String name);
/**
* Returns a {@link MultiverseWorld} if the world with name given exists, and null if it does not.
* This will search optionally for alias names.
*
* @param name The name or optionally the alias of the world to get.
* @param checkAliases Indicates whether to check for world alias name.
* @return A {@link MultiverseWorld} or null.
*/
MultiverseWorld getMVWorld(String name, boolean checkAliases);
/**
* Returns a {@link MultiverseWorld} if it exists, and null if it does not.
*
@@ -186,13 +193,24 @@ public interface MVWorldManager {
MultiverseWorld getMVWorld(World world);
/**
* Checks to see if the given name is a valid {@link MultiverseWorld}.
* Checks to see if the given name is a valid {@link MultiverseWorld}
* Searches based on world name AND alias.
*
* @param name The name or alias of the world to check.
* @return True if the world exists, false if not.
*/
boolean isMVWorld(String name);
/**
* Checks to see if the given name is a valid {@link MultiverseWorld}.
* Optionally searches by alias is specified.
*
* @param name The name or alias of the world to check.
* @param checkAliases Indicates whether to check for world alias name.
* @return True if the world exists, false if not.
*/
boolean isMVWorld(String name, boolean checkAliases);
/**
* Checks to see if the given world is a valid {@link MultiverseWorld}.
*
@@ -252,9 +270,7 @@ public interface MVWorldManager {
/**
* This method populates an internal list and needs to be called after multiverse initialization.
* @deprecated This is now done by {@link GeneratorManager}.
*/
@Deprecated
void getDefaultWorldGenerators();
/**
@@ -297,15 +313,28 @@ public interface MVWorldManager {
/**
* Regenerates a world.
*
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed IF the new seed should be random
* @param seed The seed of the world.
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed If the new seed should be random
* @param seed The seed of the world.
*
* @return True if success, false if fail.
*/
boolean regenWorld(String name, boolean useNewSeed, boolean randomSeed, String seed);
/**
* Regenerates a world.
*
* @param name Name of the world to regenerate
* @param useNewSeed If a new seed should be used
* @param randomSeed If the new seed should be random
* @param seed The seed of the world.
* @param keepGameRules If GameRules should be kept on world regen.
*
* @return True if success, false if fail.
*/
boolean regenWorld(String name, boolean useNewSeed, boolean randomSeed, String seed, boolean keepGameRules);
boolean isKeepingSpawnInMemory(World world);
/**
@@ -319,4 +348,11 @@ public interface MVWorldManager {
* does not exist. {@code includeLoaded} if the world exists and is loaded.
*/
boolean hasUnloadedWorld(String name, boolean includeLoaded);
/**
* Get all the possible worlds that Multiverse has detected to be importable.
*
* @return A collection of world names that are deemed importable.
*/
Collection<String> getPotentialWorlds();
}
@@ -33,7 +33,7 @@ public class ConfirmCommand extends MultiverseCommand {
@Override
public void runCommand(CommandSender sender, List<String> args) {
this.plugin.getCommandHandler().confirmQueuedCommand(sender);
this.plugin.getCommandQueueManager().runQueuedCommand(sender);
}
}
@@ -9,7 +9,6 @@ package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MVGeneratorManager;
import com.pneumaticraft.commandhandler.CommandHandler;
import org.bukkit.ChatColor;
import org.bukkit.World.Environment;
@@ -19,6 +18,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissionDefault;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -45,10 +46,15 @@ public class CreateCommand extends MultiverseCommand {
this.addCommandExample("/mv create " + ChatColor.GOLD + "moonworld" + ChatColor.GREEN + " normal" + ChatColor.DARK_AQUA + " -g BukkitFullOfMoon");
this.worldManager = this.plugin.getMVWorldManager();
}
private String trimWorldName(String userInput) {
// Removes relative paths.
return userInput.replaceAll("^[./\\\\]+", "");
}
@Override
public void runCommand(CommandSender sender, List<String> args) {
String worldName = args.get(0);
String worldName = trimWorldName(args.get(0));
File worldFile = new File(this.plugin.getServer().getWorldContainer(), worldName);
String env = args.get(1);
String seed = CommandHandler.getFlag("-s", args);
@@ -65,7 +71,13 @@ public class CreateCommand extends MultiverseCommand {
useSpawnAdjust = false;
}
}
// Make sure the world name doesn't contain the words 'plugins' and '.dat'
if(worldName.contains("plugins")||worldName.contains(".dat")){
sender.sendMessage(ChatColor.RED + "Multiverse cannot create a world that contains 'plugins' or '.dat'");
return;
}
if (this.worldManager.isMVWorld(worldName)) {
sender.sendMessage(ChatColor.RED + "Multiverse cannot create " + ChatColor.GOLD + ChatColor.UNDERLINE
+ "another" + ChatColor.RESET + ChatColor.RED + " world named " + worldName);
@@ -97,26 +109,14 @@ public class CreateCommand extends MultiverseCommand {
}
// Determine if the generator is valid. #918
if (generator != null) {
MVGeneratorManager.TestResult result = this.plugin.getMVGeneratorManager().validateGenerator(generator, worldName);
if (result != MVGeneratorManager.TestResult.VALID) {
List<String> genarray = new ArrayList<String>(Arrays.asList(generator.split(":")));
if (genarray.size() < 2) {
// If there was only one arg specified, pad with another empty one.
genarray.add("");
}
if (this.worldManager.getChunkGenerator(genarray.get(0), genarray.get(1), "test") == null) {
// We have an invalid generator.
switch (result) {
case ERRORS:
sender.sendMessage("An issue occurred while validating generator string '" + generator +
"'! This may indicate an issue with your generator plugin or an invalid generator id. Check console for errors!");
break;
case INVALID_GENERATOR:
sender.sendMessage("Invalid generator string '" + generator +
"'! The generator id you use is probably not supported by your generator plugin.");
break;
case PLUGIN_DOES_NOT_EXIST:
sender.sendMessage("That generator plugin is not installed on your server!");
break;
default:
sender.sendMessage("Invalid generator string '" + generator + "'!");
break;
}
sender.sendMessage(ChatColor.RED + "Aborting world creation...");
sender.sendMessage("Invalid generator! '" + generator + "'. " + ChatColor.RED + "Aborting world creation.");
return;
}
}
@@ -128,4 +128,4 @@ public class CreateCommand extends MultiverseCommand {
Command.broadcastCommandMessage(sender, "FAILED.");
}
}
}
}
@@ -8,9 +8,11 @@
package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -35,11 +37,24 @@ public class DeleteCommand extends MultiverseCommand {
public void runCommand(CommandSender sender, List<String> args) {
String worldName = args.get(0);
Class<?>[] paramTypes = {String.class};
List<Object> objectArgs = new ArrayList<Object>(args);
this.plugin.getCommandHandler()
.queueCommand(sender, "mvdelete", "deleteWorld", objectArgs,
paramTypes, ChatColor.GREEN + "World '" + worldName + "' Deleted!",
ChatColor.RED + "World '" + worldName + "' could NOT be deleted!");
this.plugin.getCommandQueueManager().addToQueue(new QueuedCommand(
sender,
deleteRunnable(sender, worldName),
String.format("Are you sure you want to delete world '%s'? You cannot undo this action.", worldName)
));
}
private Runnable deleteRunnable(@NotNull CommandSender sender,
@NotNull String worldName) {
return () -> {
sender.sendMessage(String.format("Deleting world '%s'...", worldName));
if (this.plugin.getMVWorldManager().deleteWorld(worldName)) {
sender.sendMessage(String.format("%sWorld %s was deleted!", ChatColor.GREEN, worldName));
return;
}
sender.sendMessage(String.format("%sThere was an issue deleting '%s'! Please check console for errors.",
ChatColor.RED, worldName));
};
}
}
@@ -8,6 +8,12 @@
package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.InlineSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.MapContentParser;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
@@ -16,7 +22,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Allows management of Anchor Destinations.
@@ -26,8 +34,8 @@ public class GamerulesCommand extends MultiverseCommand {
public GamerulesCommand(MultiverseCore plugin) {
super(plugin);
this.setName("List the Minecraft Game Rules for a World.");
this.setCommandUsage("/mv gamerules" + ChatColor.GOLD + " [WORLD]");
this.setArgRange(0, 1);
this.setCommandUsage("/mv gamerules" + ChatColor.GOLD + " [WORLD] [FILTER]");
this.setArgRange(0, 2);
this.addKey("mv gamerules");
this.addKey("mv rules");
this.addKey("mvgamerules");
@@ -37,11 +45,13 @@ public class GamerulesCommand extends MultiverseCommand {
this.setPermission("multiverse.core.gamerule.list", "Allows a player to list gamerules.", PermissionDefault.OP);
}
@Override
public void runCommand(CommandSender sender, List<String> args) {
// We NEED a world from the command line
final Player p;
Player p;
World world;
ContentFilter filter = DefaultContentFilter.getInstance();
if (sender instanceof Player) {
p = (Player) sender;
} else {
@@ -56,9 +66,20 @@ public class GamerulesCommand extends MultiverseCommand {
return;
}
final World world;
// Not the best way, need to fix with ACF soon...
if (args.size() == 0) {
world = p.getWorld();
} else if (args.size() == 1) {
world = Bukkit.getWorld(args.get(0));
if (world == null) {
if (p == null) {
sender.sendMessage(ChatColor.RED + "Failure!" + ChatColor.WHITE + " World " + ChatColor.AQUA + args.get(0)
+ ChatColor.WHITE + " does not exist.");
return;
}
world = p.getWorld();
filter = RegexContentFilter.fromString(args.get(0));
}
} else {
world = Bukkit.getWorld(args.get(0));
if (world == null) {
@@ -66,17 +87,25 @@ public class GamerulesCommand extends MultiverseCommand {
+ ChatColor.WHITE + " does not exist.");
return;
}
filter = RegexContentFilter.fromString(args.get(1));
}
final StringBuilder gameRules = new StringBuilder();
for (final String gameRule : world.getGameRules()) {
if (gameRules.length() != 0) {
gameRules.append(ChatColor.WHITE).append(", ");
}
gameRules.append(ChatColor.AQUA).append(gameRule).append(ChatColor.WHITE).append(": ");
gameRules.append(ChatColor.GREEN).append(world.getGameRuleValue(GameRule.getByName(gameRule)));
ContentDisplay.create()
.addContentParser(MapContentParser.forContent(getGameRuleMap(world))
.withKeyColor(ChatColor.GREEN)
.withValueColor(ChatColor.YELLOW))
.withSendHandler(InlineSendHandler.create()
.withHeader("====[ Gamerules for %s%s%s ]====", ChatColor.AQUA, world.getName(), ChatColor.WHITE)
.withFilter(filter))
.send(sender);
}
private Map<String, Object> getGameRuleMap(World world) {
Map<String, Object> gameRuleMap = new HashMap<>();
for (GameRule<?> rule : GameRule.values()) {
Object value = world.getGameRuleValue(rule);
gameRuleMap.put(rule.getName(), value);
}
sender.sendMessage("=== Gamerules for " + ChatColor.AQUA + world.getName() + ChatColor.WHITE + " ===");
sender.sendMessage(gameRules.toString());
return gameRuleMap;
}
}
@@ -9,19 +9,12 @@ package com.onarandombox.MultiverseCore.commands;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.GeneratorPlugin;
import com.onarandombox.MultiverseCore.generators.SimpleGeneratorPlugin;
import net.milkbowl.vault.chat.Chat;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
@@ -32,70 +25,36 @@ public class GeneratorCommand extends MultiverseCommand {
public GeneratorCommand(MultiverseCore plugin) {
super(plugin);
this.setName("World Information");
this.setCommandUsage("/mv generators [plugin]");
this.setArgRange(0, 1);
this.setCommandUsage("/mv generators");
this.setArgRange(0, 0);
this.addKey("mv generators");
this.addKey("mvgenerators");
this.addKey("mv gens");
this.addKey("mvgens");
this.addCommandExample("/mv generators");
this.addCommandExample("/mv generators VoidGenerator");
this.setPermission("multiverse.core.generator", "Returns info of Loaded Generator Plugins.", PermissionDefault.OP);
this.setPermission("multiverse.core.generator", "Returns a list of Loaded Generator Plugins.", PermissionDefault.OP);
}
@Override
public void runCommand(CommandSender sender, List<String> args) {
if (args.size() == 0) {
listKnownGenerators(sender);
return;
Logging.info("PLEASE IGNORE the 'Plugin X does not contain any generators' message below!");
Plugin[] plugins = this.plugin.getServer().getPluginManager().getPlugins();
List<String> generators = new ArrayList<String>();
for (Plugin p : plugins) {
if (p.isEnabled() && p.getDefaultWorldGenerator("world", "") != null) {
generators.add(p.getDescription().getName());
}
}
showGeneratorDetails(sender, args.get(0));
}
private void listKnownGenerators(CommandSender sender) {
sender.sendMessage(ChatColor.AQUA + "--- Loaded Generator Plugins ---");
StringBuilder loadedGens = new StringBuilder();
String loadedGens = "";
boolean altColor = false;
for (String s : this.plugin.getMVGeneratorManager().getGeneratorPluginNames()) {
loadedGens.append(altColor ? ChatColor.YELLOW : ChatColor.WHITE).append(s).append(' ');
for (String s : generators) {
loadedGens += (altColor ? ChatColor.YELLOW : ChatColor.WHITE) + s + " ";
altColor = !altColor;
}
if (loadedGens.length() == 0) {
loadedGens.append(ChatColor.RED).append("No Generator Plugins found.");
}
sender.sendMessage(loadedGens.toString());
}
private void showGeneratorDetails(CommandSender sender, String pluginName) {
Plugin genPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
if (genPlugin == null) {
sender.sendMessage(ChatColor.RED + "You do not have a plugin named '"+ pluginName + "' on your server.");
return;
}
GeneratorPlugin generatorPlugin = this.plugin.getMVGeneratorManager().getGeneratorPlugin(genPlugin);
if (generatorPlugin == null) {
sender.sendMessage(ChatColor.RED + "'" + pluginName + "' does not look like a generator plugin.");
return;
}
sender.sendMessage(ChatColor.AQUA + "--- Info on " + generatorPlugin.getPlugin().getName() + " ---");
boolean hasAddInfo = false;
Collection<String> exampleUsages = generatorPlugin.getExampleUsages();
if (exampleUsages != null && exampleUsages.size() > 0) {
hasAddInfo = true;
sender.sendMessage("Usages:");
exampleUsages.forEach(sender::sendMessage);
}
String infoLink = generatorPlugin.getInfoLink();
if (infoLink != null) {
hasAddInfo = true;
sender.sendMessage("More Info: " + infoLink);
}
if (!hasAddInfo) {
sender.sendMessage(ChatColor.RED + "'" + pluginName + "' did not provide additional info to Multiverse.");
loadedGens = ChatColor.RED + "No Generator Plugins found.";
}
sender.sendMessage(loadedGens);
}
}
@@ -40,24 +40,16 @@ public class HelpCommand extends PaginatedCoreCommand<Command> {
@Override
protected List<Command> getFilteredItems(List<Command> availableItems, String filter) {
String expression = "(?i).*" + cleanFilter(filter) + ".*";
List<Command> filtered = new ArrayList<Command>();
for (Command c : availableItems) {
if (stitchThisString(c.getKeyStrings()).matches("(?i).*" + filter + ".*")) {
if (stitchThisString(c.getKeyStrings()).matches(expression)
|| c.getCommandName().matches(expression)
|| c.getCommandDesc().matches(expression)
|| c.getCommandUsage().matches(expression)
|| c.getCommandExamples().stream().anyMatch(eg -> eg.matches(expression))) {
filtered.add(c);
} else if (c.getCommandName().matches("(?i).*" + filter + ".*")) {
filtered.add(c);
} else if (c.getCommandDesc().matches("(?i).*" + filter + ".*")) {
filtered.add(c);
} else if (c.getCommandUsage().matches("(?i).*" + filter + ".*")) {
filtered.add(c);
} else {
for (String example : c.getCommandExamples()) {
if (example.matches("(?i).*" + filter + ".*")) {
filtered.add(c);
break;
}
}
}
}
return filtered;
@@ -9,7 +9,6 @@ package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.utils.WorldNameChecker;
import com.pneumaticraft.commandhandler.CommandHandler;
import org.bukkit.ChatColor;
@@ -19,8 +18,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissionDefault;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -46,33 +43,17 @@ public class ImportCommand extends MultiverseCommand {
this.worldManager = this.plugin.getMVWorldManager();
}
private String getPotentialWorlds() {
File worldFolder = this.plugin.getServer().getWorldContainer();
if (worldFolder == null) {
return "";
}
File[] files = worldFolder.listFiles();
String worldList = "";
Collection<MultiverseWorld> worlds = this.worldManager.getMVWorlds();
List<String> worldStrings = new ArrayList<String>();
for (MultiverseWorld world : worlds) {
worldStrings.add(world.getName());
}
for (String world : this.worldManager.getUnloadedWorlds()) {
worldStrings.add(world);
}
private String getPotentialWorldStrings() {
final Collection<String> potentialWorlds = this.worldManager.getPotentialWorlds();
StringBuilder worldList = new StringBuilder();
ChatColor currColor = ChatColor.WHITE;
for (File file : files) {
if (file.isDirectory() && WorldNameChecker.isValidWorldFolder(file) && !worldStrings.contains(file.getName())) {
worldList += currColor + file.getName() + " ";
if (currColor == ChatColor.WHITE) {
currColor = ChatColor.YELLOW;
} else {
currColor = ChatColor.WHITE;
}
}
for (String world : potentialWorlds) {
worldList.append(currColor).append(world).append(' ');
currColor = currColor == ChatColor.WHITE ? ChatColor.YELLOW : ChatColor.WHITE;
}
return worldList;
return worldList.toString();
}
private String trimWorldName(String userInput) {
@@ -85,7 +66,7 @@ public class ImportCommand extends MultiverseCommand {
String worldName = trimWorldName(args.get(0));
if (worldName.toLowerCase().equals("--list") || worldName.toLowerCase().equals("-l")) {
String worldList = this.getPotentialWorlds();
String worldList = this.getPotentialWorldStrings();
if (worldList.length() > 2) {
sender.sendMessage(ChatColor.AQUA + "====[ These look like worlds ]====");
sender.sendMessage(worldList);
@@ -100,6 +81,12 @@ public class ImportCommand extends MultiverseCommand {
this.showHelp(sender);
return;
}
// Make sure the world name doesn't contain the words 'plugins' and '.dat'
if(worldName.contains("plugins")||worldName.contains(".dat")){
sender.sendMessage(ChatColor.RED + "Multiverse cannot create a world that contains 'plugins' or '.dat'");
return;
}
// Make sure we don't already know about this world.
if (this.worldManager.isMVWorld(worldName)) {
@@ -128,7 +115,7 @@ public class ImportCommand extends MultiverseCommand {
if (!worldFile.exists()) {
sender.sendMessage(ChatColor.RED + "FAILED.");
String worldList = this.getPotentialWorlds();
String worldList = this.getPotentialWorldStrings();
sender.sendMessage("That world folder does not exist. These look like worlds to me:");
sender.sendMessage(worldList);
} else if (!WorldNameChecker.isValidWorldFolder(worldFile)) {
@@ -147,4 +134,4 @@ public class ImportCommand extends MultiverseCommand {
Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed!");
}
}
}
}
@@ -9,122 +9,112 @@ package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.PagedSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ContentParser;
import org.bukkit.ChatColor;
import org.bukkit.World.Environment;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Displays a listing of all worlds that a player can enter.
*/
public class ListCommand extends PaginatedCoreCommand<String> {
public class ListCommand extends MultiverseCommand {
public ListCommand(MultiverseCore plugin) {
super(plugin);
this.setName("World Listing");
this.setCommandUsage("/mv list [page]");
this.setCommandUsage("/mv list [filter] [page]");
this.setArgRange(0, 2);
this.addKey("mvlist");
this.addKey("mvl");
this.addKey("mv list");
this.setPermission("multiverse.core.list.worlds", "Displays a listing of all worlds that you can enter.", PermissionDefault.OP);
this.setItemsPerPage(8); // SUPPRESS CHECKSTYLE: MagicNumberCheck
}
private List<String> getFancyWorldList(Player p) {
List<String> worldList = new ArrayList<String>();
for (MultiverseWorld world : this.plugin.getMVWorldManager().getMVWorlds()) {
if (p != null && (!this.plugin.getMVPerms().canEnterWorld(p, world))) {
continue;
}
ChatColor color = ChatColor.GOLD;
Environment env = world.getEnvironment();
if (env == Environment.NETHER) {
color = ChatColor.RED;
} else if (env == Environment.NORMAL) {
color = ChatColor.GREEN;
} else if (env == Environment.THE_END) {
color = ChatColor.AQUA;
}
StringBuilder builder = new StringBuilder();
builder.append(world.getColoredWorldString()).append(ChatColor.WHITE);
builder.append(" - ").append(color).append(world.getEnvironment());
if (world.isHidden()) {
if (p == null || this.plugin.getMVPerms().hasPermission(p, "multiverse.core.modify", true)) {
// Prefix hidden worlds with an "[H]"
worldList.add(ChatColor.GRAY + "[H]" + builder.toString());
}
} else {
worldList.add(builder.toString());
}
}
for (String name : this.plugin.getMVWorldManager().getUnloadedWorlds()) {
if (p == null || this.plugin.getMVPerms().hasPermission(p, "multiverse.access." + name, true)) {
worldList.add(ChatColor.GRAY + name + " - UNLOADED");
}
}
return worldList;
}
@Override
protected List<String> getFilteredItems(List<String> availableItems, String filter) {
List<String> filtered = new ArrayList<String>();
for (String s : availableItems) {
if (s.matches("(?i).*" + filter + ".*")) {
filtered.add(s);
}
}
return filtered;
}
@Override
protected String getItemText(String item) {
return item;
}
@Override
public void runCommand(CommandSender sender, List<String> args) {
sender.sendMessage(ChatColor.LIGHT_PURPLE + "====[ Multiverse World List ]====");
Player p = null;
if (sender instanceof Player) {
p = (Player) sender;
}
ContentFilter filter = DefaultContentFilter.getInstance();
int page = 1;
FilterObject filterObject = this.getPageAndFilter(args);
List<String> availableWorlds = new ArrayList<String>(this.getFancyWorldList(p));
if (filterObject.getFilter().length() > 0) {
availableWorlds = this.getFilteredItems(availableWorlds, filterObject.getFilter());
if (availableWorlds.size() == 0) {
sender.sendMessage(ChatColor.RED + "Sorry... " + ChatColor.WHITE
+ "No worlds matched your filter: " + ChatColor.AQUA + filterObject.getFilter());
return;
// Either page or filter.
if (args.size() == 1) {
try {
page = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignore) {
filter = RegexContentFilter.fromString(args.get(0));
}
}
if (!(sender instanceof Player)) {
for (String c : availableWorlds) {
sender.sendMessage(c);
// Filter then page.
if (args.size() == 2) {
filter = RegexContentFilter.fromString(args.get(0));
try {
page = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignore) {
sender.sendMessage(ChatColor.RED + args.get(1) + " is not valid number!");
}
return;
}
int totalPages = (int) Math.ceil(availableWorlds.size() / (this.itemsPerPage + 0.0));
ContentDisplay.create()
.addContentParser(new WorldListContentParser())
.withSendHandler(PagedSendHandler.create()
.withHeader("%s====[ Multiverse World List ]====", ChatColor.GOLD)
.withFilter(filter)
.withTargetPage(page))
.send(sender);
}
if (filterObject.getPage() > totalPages) {
filterObject.setPage(totalPages);
private class WorldListContentParser implements ContentParser {
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
Player player = (sender instanceof Player) ? (Player) sender : null;
plugin.getMVWorldManager().getMVWorlds().stream()
.filter(world -> player == null || plugin.getMVPerms().canEnterWorld(player, world))
.filter(world -> canSeeWorld(player, world))
.map(world -> hiddenText(world) + world.getColoredWorldString() + " - " + parseColouredEnvironment(world.getEnvironment()))
.forEach(content::add);
plugin.getMVWorldManager().getUnloadedWorlds().stream()
.filter(world -> plugin.getMVPerms().hasPermission(sender, "multiverse.access." + world, true))
.map(world -> ChatColor.GRAY + world + " - UNLOADED")
.forEach(content::add);
}
sender.sendMessage(ChatColor.AQUA + " Page " + filterObject.getPage() + " of " + totalPages);
private boolean canSeeWorld(Player player, MultiverseWorld world) {
return !world.isHidden()
|| player == null
|| plugin.getMVPerms().hasPermission(player, "multiverse.core.modify", true);
}
this.showPage(filterObject.getPage(), sender, availableWorlds);
private String hiddenText(MultiverseWorld world) {
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
}
private String parseColouredEnvironment(World.Environment env) {
ChatColor color = ChatColor.GOLD;
switch (env) {
case NETHER:
color = ChatColor.RED;
break;
case NORMAL:
color = ChatColor.GREEN;
break;
case THE_END:
color = ChatColor.AQUA;
break;
}
return color + env.toString();
}
}
}
@@ -13,12 +13,14 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
import java.util.regex.Pattern;
/**
* A generic paginated command.
* @param <T> The type of items on the page.
*/
public abstract class PaginatedCommand<T> extends Command {
private final Pattern REGEX_SPECIAL_CHARS = Pattern.compile("[.+*?\\[^\\]$(){}=!<>|:-\\\\]");
private static final int DEFAULT_ITEMS_PER_PAGE = 9;
/**
* The number of items per page.
@@ -40,12 +42,23 @@ public abstract class PaginatedCommand<T> extends Command {
/**
* Gets filtered items.
*
* @param availableItems All available items.
* @param filter The filter-{@link String}.
* @return A list of items that match the filter.
*/
protected abstract List<T> getFilteredItems(List<T> availableItems, String filter);
/**
* Escape regex special characters from filter
*
* @param filter The filter-{@link String}.
* @return String with regex characters escaped
*/
protected String cleanFilter(String filter) {
return REGEX_SPECIAL_CHARS.matcher(filter).replaceAll("\\\\$0");
}
/**
* Constructs a single string from a list of strings.
*
@@ -8,11 +8,13 @@
package com.onarandombox.MultiverseCore.commands;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import com.pneumaticraft.commandhandler.CommandHandler;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
@@ -23,8 +25,8 @@ public class RegenCommand extends MultiverseCommand {
public RegenCommand(MultiverseCore plugin) {
super(plugin);
this.setName("Regenerates a World");
this.setCommandUsage("/mv regen" + ChatColor.GREEN + " {WORLD}" + ChatColor.GOLD + " [-s [SEED]]");
this.setArgRange(1, 3);
this.setCommandUsage("/mv regen" + ChatColor.GREEN + " {WORLD}" + ChatColor.GOLD + " [-s [SEED]] [--keep-gamerules]");
this.setArgRange(1, 4);
this.addKey("mvregen");
this.addKey("mv regen");
this.addCommandExample("You can use the -s with no args to get a new seed:");
@@ -37,17 +39,31 @@ public class RegenCommand extends MultiverseCommand {
@Override
public void runCommand(CommandSender sender, List<String> args) {
Boolean useseed = (!(args.size() == 1));
Boolean randomseed = (args.size() == 2 && args.get(1).equalsIgnoreCase("-s"));
String worldName = args.get(0);
boolean useseed = (!(args.size() == 1));
boolean randomseed = (args.size() == 2 && args.get(1).equalsIgnoreCase("-s"));
String seed = (args.size() == 3) ? args.get(2) : "";
boolean keepGamerules = CommandHandler.hasFlag("--keep-gamerules", args);
this.plugin.getCommandQueueManager().addToQueue(new QueuedCommand(
sender,
doWorldRegen(sender, worldName, useseed, randomseed, seed, keepGamerules),
String.format("Are you sure you want to regen '%s'? You cannot undo this action.", worldName)
));
}
Class<?>[] paramTypes = {String.class, Boolean.class, Boolean.class, String.class};
List<Object> objectArgs = new ArrayList<Object>();
objectArgs.add(args.get(0));
objectArgs.add(useseed);
objectArgs.add(randomseed);
objectArgs.add(seed);
this.plugin.getCommandHandler().queueCommand(sender, "mvregen", "regenWorld", objectArgs,
paramTypes, ChatColor.GREEN + "World Regenerated!", ChatColor.RED + "World could NOT be regenerated!");
private Runnable doWorldRegen(@NotNull CommandSender sender,
@NotNull String worldName,
boolean useSeed,
boolean randomSeed,
@NotNull String seed,
boolean keepGamerules) {
return () -> {
if (this.plugin.getMVWorldManager().regenWorld(worldName, useSeed, randomSeed, seed, keepGamerules)) {
sender.sendMessage(ChatColor.GREEN + "World Regenerated!");
return;
}
sender.sendMessage(ChatColor.RED + "World could NOT be regenerated!");
};
}
}
@@ -11,6 +11,7 @@ import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.Teleporter;
import com.onarandombox.MultiverseCore.api.MVDestination;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import com.onarandombox.MultiverseCore.destination.CustomTeleporterDestination;
import com.onarandombox.MultiverseCore.destination.DestinationFactory;
import com.onarandombox.MultiverseCore.destination.InvalidDestination;
@@ -152,24 +153,27 @@ public class TeleportCommand extends MultiverseCommand {
if (result == TeleportResult.FAIL_UNSAFE) {
Logging.fine("Could not teleport " + teleportee.getName()
+ " to " + plugin.getLocationManipulation().strCoordsRaw(d.getLocation(teleportee)));
Logging.fine("Queueing Command");
Class<?>[] paramTypes = { CommandSender.class, Player.class, Location.class };
List<Object> items = new ArrayList<Object>();
items.add(teleporter);
items.add(teleportee);
items.add(d.getLocation(teleportee));
String player = "you";
if (!teleportee.equals(teleporter)) {
player = teleportee.getName();
}
String message = String.format("%sMultiverse %sdid not teleport %s%s %sto %s%s %sbecause it was unsafe.",
ChatColor.GREEN, ChatColor.WHITE, ChatColor.AQUA, player, ChatColor.WHITE, ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE);
this.plugin.getCommandHandler().queueCommand(sender, "mvteleport", "teleportPlayer", items,
paramTypes, message, "Would you like to try anyway?", "", "", UNSAFE_TELEPORT_EXPIRE_DELAY);
this.plugin.getCommandQueueManager().addToQueue(new QueuedCommand(
sender,
doUnsafeTeleport(teleporter, teleportee, d.getLocation(teleportee)),
String.format("%sMultiverse %sdid not teleport %s%s %sto %s%s %sbecause it was unsafe. Would you like to try anyway?",
ChatColor.GREEN, ChatColor.WHITE, ChatColor.AQUA, player, ChatColor.WHITE, ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE),
UNSAFE_TELEPORT_EXPIRE_DELAY
));
}
// else: Player was teleported successfully (or the tp event was fired I should say)
}
private Runnable doUnsafeTeleport(CommandSender teleporter, Player player, Location location) {
return () -> this.plugin.getSafeTTeleporter().safelyTeleport(teleporter, player, location, false);
}
private boolean checkSendPermissions(CommandSender teleporter, Player teleportee, MVDestination destination) {
if (teleporter.equals(teleportee)) {
if (!this.plugin.getMVPerms().hasPermission(teleporter, "multiverse.teleport.self." + destination.getIdentifier(), true)) {
@@ -45,8 +45,11 @@ public class VersionCommand extends MultiverseCommand {
this.addKey("mvver");
this.addKey("mvv");
this.addKey("mvversion");
this.setPermission("multiverse.core.version",
"Dumps version info to the console, optionally to pastebin.com with -b, to hastebin.com using -h, or to paste.gg with -p.", PermissionDefault.TRUE);
this.setPermission(
"multiverse.core.version",
"Dumps version info to the console, optionally to pastebin.com with -b, to hastebin.com using -h, or to paste.gg with -p.",
PermissionDefault.OP)
;
}
private String getLegacyString() {
@@ -0,0 +1,161 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2020. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package com.onarandombox.MultiverseCore.commandtools.queue;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.block.data.type.CommandBlock;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.WeakHashMap;
/**
* <p>Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.</p>
*
* <p>Each sender can only have one command in queue at any given time. When a queued command is added
* for a sender that already has a command in queue, it will replace the old queued command.</p>
*/
public class CommandQueueManager {
private static final long TICKS_PER_SECOND = 20;
private static final DummyCommandBlockSender COMMAND_BLOCK = new DummyCommandBlockSender();
private final MultiverseCore plugin;
private final Map<CommandSender, QueuedCommand> queuedCommandMap;
public CommandQueueManager(@NotNull MultiverseCore plugin) {
this.plugin = plugin;
this.queuedCommandMap = new WeakHashMap<>();
}
/**
* Adds a {@link QueuedCommand} into queue.
*
* @param queuedCommand The queued command to add.
*/
public void addToQueue(QueuedCommand queuedCommand) {
CommandSender targetSender = parseSender(queuedCommand.getSender());
// Since only one command is stored in queue per sender, we remove the old one.
this.removeFromQueue(targetSender);
Logging.finer("Add new command to queue for sender %s.", targetSender);
this.queuedCommandMap.put(targetSender, queuedCommand);
queuedCommand.setExpireTask(runExpireLater(queuedCommand));
queuedCommand.getSender().sendMessage(queuedCommand.getPrompt());
queuedCommand.getSender().sendMessage(String.format("Run %s/mv confirm %sto continue. This will expire in %s seconds.",
ChatColor.GREEN, ChatColor.WHITE, queuedCommand.getValidDuration()));
}
/**
* Expire task that removes a {@link QueuedCommand} from queue after valid duration defined.
*
* @param queuedCommand Command to run the expire task on.
* @return The expire {@link BukkitTask}.
*/
@NotNull
private BukkitTask runExpireLater(@NotNull QueuedCommand queuedCommand) {
return Bukkit.getScheduler().runTaskLater(
this.plugin,
expireRunnable(queuedCommand),
queuedCommand.getValidDuration() * TICKS_PER_SECOND
);
}
/**
* Runnable responsible for expiring the queued command.
*
* @param queuedCommand Command to create the expire task on.
* @return The expire runnable.
*/
@NotNull
private Runnable expireRunnable(@NotNull QueuedCommand queuedCommand) {
return () -> {
CommandSender targetSender = parseSender(queuedCommand.getSender());
QueuedCommand matchingQueuedCommand = this.queuedCommandMap.get(targetSender);
if (!queuedCommand.equals(matchingQueuedCommand) || queuedCommand.getExpireTask().isCancelled()) {
// To be safe, but this shouldn't happen since we cancel old commands before add new once.
Logging.finer("This is an old queue command already.");
return;
}
queuedCommand.getSender().sendMessage("Your queued command has expired.");
this.queuedCommandMap.remove(queuedCommand.getSender());
};
}
/**
* Runs the command in queue for the given sender, if any.
*
* @param sender {@link CommandSender} that confirmed the command.
* @return True if queued command ran successfully, else false.
*/
public boolean runQueuedCommand(@NotNull CommandSender sender) {
CommandSender targetSender = parseSender(sender);
QueuedCommand queuedCommand = this.queuedCommandMap.get(targetSender);
if (queuedCommand == null) {
sender.sendMessage(ChatColor.RED + "You do not have any commands in queue.");
return false;
}
Logging.finer("Running queued command...");
queuedCommand.getAction().run();
return removeFromQueue(targetSender);
}
/**
* Since only one command is stored in queue per sender, we remove the old one.
*
* @param sender The {@link CommandSender} that executed the command.
* @return True if queue command is removed from sender successfully, else false.
*/
public boolean removeFromQueue(@NotNull CommandSender sender) {
CommandSender targetSender = parseSender(sender);
QueuedCommand previousCommand = this.queuedCommandMap.remove(targetSender);
if (previousCommand == null) {
Logging.finer("No queue command to remove for sender %s.", targetSender.getName());
return false;
}
previousCommand.getExpireTask().cancel();
Logging.finer("Removed queue command for sender %s.", targetSender.getName());
return true;
}
/**
* To allow all CommandBlocks to be a common sender with use of {@link DummyCommandBlockSender}.
* So confirm command can be used for a queued command on another command block.
*
* @param sender The sender to parse.
* @return The sender, or if its a command block, a {@link DummyCommandBlockSender}.
*/
@NotNull
private CommandSender parseSender(@NotNull CommandSender sender) {
Logging.fine(sender.getClass().getName());
if (isCommandBlock(sender)) {
Logging.finer("Is command block.");
return COMMAND_BLOCK;
}
return sender;
}
/**
* Checks if the sender is a command block.
*
* @param sender The sender to check.
* @return True if sender is a command block, else false.
*/
private boolean isCommandBlock(@NotNull CommandSender sender) {
return sender instanceof BlockCommandSender
&& ((BlockCommandSender) sender).getBlock().getBlockData() instanceof CommandBlock;
}
}
@@ -0,0 +1,104 @@
package com.onarandombox.MultiverseCore.commandtools.queue;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
/**
* Used by {@link CommandQueueManager}, so different commands block can be recognised as one.
*/
class DummyCommandBlockSender implements CommandSender {
@Override
public void sendMessage(@NotNull String message) {
throw new UnsupportedOperationException();
}
@Override
public void sendMessage(@NotNull String[] messages) {
throw new UnsupportedOperationException();
}
@Override
public @NotNull Server getServer() {
return Bukkit.getServer();
}
@Override
public @NotNull String getName() {
return "DummyCommandBlockSender";
}
@Override
public boolean isPermissionSet(@NotNull String name) {
throw new UnsupportedOperationException();
}
@Override
public boolean isPermissionSet(@NotNull Permission perm) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPermission(@NotNull String name) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPermission(@NotNull Permission perm) {
throw new UnsupportedOperationException();
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) {
throw new UnsupportedOperationException();
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull Plugin plugin) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) {
throw new UnsupportedOperationException();
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) {
throw new UnsupportedOperationException();
}
@Override
public void removeAttachment(@NotNull PermissionAttachment attachment) {
throw new UnsupportedOperationException();
}
@Override
public void recalculatePermissions() {
throw new UnsupportedOperationException();
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions() {
throw new UnsupportedOperationException();
}
@Override
public boolean isOp() {
throw new UnsupportedOperationException();
}
@Override
public void setOp(boolean value) {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,86 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2020. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package com.onarandombox.MultiverseCore.commandtools.queue;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;
/**
* Represents a single command used in {@link CommandQueueManager} for confirming before running potentially
* dangerous action.
*/
public class QueuedCommand {
private static final String DEFAULT_PROMPT_MESSAGE = "The command you are trying to run is deemed dangerous.";
private static final int DEFAULT_VALID_TIME = 10;
private final CommandSender sender;
private final Runnable action;
private final String prompt;
private final int validDuration;
private BukkitTask expireTask;
public QueuedCommand(CommandSender sender, Runnable action) {
this(sender, action, DEFAULT_PROMPT_MESSAGE, DEFAULT_VALID_TIME);
}
public QueuedCommand(CommandSender sender, Runnable action, String prompt) {
this(sender, action, prompt, DEFAULT_VALID_TIME);
}
public QueuedCommand(CommandSender sender, Runnable action, int validDuration) {
this(sender, action, DEFAULT_PROMPT_MESSAGE, validDuration);
}
/**
* Creates a new queue command, to be registered at {@link CommandQueueManager#addToQueue(QueuedCommand)}.
*
* @param sender The sender that ran the command needed for confirmation.
* @param action The logic to execute upon confirming.
* @param prompt Question to ask sender to confirm.
* @param validDuration Duration in which the command is valid for confirm in seconds.
*/
public QueuedCommand(CommandSender sender, Runnable action, String prompt, int validDuration) {
this.sender = sender;
this.action = action;
this.prompt = prompt;
this.validDuration = validDuration;
}
@NotNull
CommandSender getSender() {
return sender;
}
@NotNull
String getPrompt() {
return prompt;
}
int getValidDuration() {
return validDuration;
}
@NotNull
Runnable getAction() {
return action;
}
@NotNull
BukkitTask getExpireTask() {
return expireTask;
}
void setExpireTask(@NotNull BukkitTask expireTask) {
if (this.expireTask != null) {
throw new IllegalStateException("This queue command already has an expire task. You can't register twice!");
}
this.expireTask = expireTask;
}
}
@@ -0,0 +1,4 @@
/**
* Manager queuing of dangerous commands in need of confirmation.
*/
package com.onarandombox.MultiverseCore.commandtools.queue;
@@ -21,12 +21,15 @@ import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/** A factory class that will create destinations from specific strings. */
/**
* A factory class that will create destinations from specific strings.
*/
public class DestinationFactory {
private static final Pattern CANNON_PATTERN = Pattern.compile("(?i)cannon-[\\d]+(\\.[\\d]+)?");
@@ -164,4 +167,13 @@ public class DestinationFactory {
this.teleportCommand.addAdditonalPermission(other);
return true;
}
/**
* Gets all the {@link MVDestination} identifiers registered.
*
* @return A collection of destination identifiers.
*/
public Collection<String> getRegisteredIdentifiers() {
return this.destList.keySet();
}
}
@@ -0,0 +1,69 @@
package com.onarandombox.MultiverseCore.display;
import com.onarandombox.MultiverseCore.display.handlers.DefaultSendHandler;
import com.onarandombox.MultiverseCore.display.handlers.SendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ContentParser;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Helps to display contents such as list and maps in a nicely formatted fashion.
*/
public class ContentDisplay {
/**
* Makes a new {@link ContentDisplay} instance to use.
*
* @return New {@link ContentDisplay} instance.
*/
@NotNull
public static ContentDisplay create() {
return new ContentDisplay();
}
private final List<ContentParser> contentParsers = new ArrayList<>();
private SendHandler sendHandler = DefaultSendHandler.getInstance();
public ContentDisplay() {
}
/**
* Adds content to be displayed.
*
* @param parser The content parser to add.
* @return Same {@link ContentDisplay} for method chaining.
*/
@NotNull
public ContentDisplay addContentParser(@NotNull ContentParser parser) {
contentParsers.add(parser);
return this;
}
/**
* Sets the handler for displaying the message to command sender.
*
* @param handler The send handler to use.
* @return Same {@link ContentDisplay} for method chaining.
*/
@NotNull
public ContentDisplay withSendHandler(@NotNull SendHandler handler) {
sendHandler = handler;
return this;
}
/**
* Format and display the message to command sender.
*
* @param sender The target command sender to show the display to.
*/
public void send(@NotNull CommandSender sender) {
Objects.requireNonNull(sendHandler, "No send handler set for content display");
List<String> parsedContent = new ArrayList<>();
contentParsers.forEach(parser -> parser.parse(sender, parsedContent));
sendHandler.send(sender, parsedContent);
}
}
@@ -0,0 +1,21 @@
package com.onarandombox.MultiverseCore.display.filters;
/**
* Filter display content for it show only certain string.
*/
public interface ContentFilter {
/**
* Checks if a particular string should be displayed.
*
* @param value String to check on.
* @return True if should be display, false otherwise.
*/
boolean checkMatch(String value);
/**
* Gets whether content needs to be filtered by this filter.
*
* @return True if content should be filtered, false otherwise.
*/
boolean needToFilter();
}
@@ -0,0 +1,35 @@
package com.onarandombox.MultiverseCore.display.filters;
/**
* Default implementation of {@link ContentFilter} that doesn't filter anything.
*/
public class DefaultContentFilter implements ContentFilter {
public static DefaultContentFilter instance;
public static DefaultContentFilter getInstance() {
if (instance == null) {
instance = new DefaultContentFilter();
}
return instance;
}
private DefaultContentFilter() {
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkMatch(String value) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean needToFilter() {
return false;
}
}
@@ -0,0 +1,99 @@
package com.onarandombox.MultiverseCore.display.filters;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.base.Strings;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Filter content and text based on regex matching.
*/
public class RegexContentFilter implements ContentFilter {
private static final Pattern REGEX_SPECIAL_CHARS = Pattern.compile("[.+*?\\[^\\]$(){}=!<>|:-\\\\]");
/**
* Compile regex pattern to create a regex filter.
*
* When prefixed with 'r=', filter string is used as the full regex pattern.
* Else, set to regex that contains the filterString.
*
* @param filterString The target string to create filter.
* @return A new instance of {@link RegexContentFilter} with filter applied.
*/
@NotNull
public static RegexContentFilter fromString(@Nullable String filterString) {
if (filterString == null) {
return new RegexContentFilter(null);
}
if (filterString.startsWith("r=")) {
return new RegexContentFilter(filterString.substring(2));
}
String cleanedFilter = REGEX_SPECIAL_CHARS.matcher(filterString.toLowerCase()).replaceAll("\\\\$0");
return new RegexContentFilter("(?i).*" + cleanedFilter + ".*");
}
private final String regexString;
private Pattern regexPattern;
public RegexContentFilter(@Nullable String regexString) {
this.regexString = regexString;
convertToPattern();
}
/**
* Try to compile and store the regex into a {@link Pattern}.
*/
private void convertToPattern() {
if (Strings.isNullOrEmpty(regexString)) {
return;
}
try {
regexPattern = Pattern.compile(regexString);
} catch (PatternSyntaxException ignored) {
regexPattern = null;
Logging.fine("Error parsing regex: %s", regexString);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkMatch(String value) {
if (!hasValidRegex()) {
return false;
}
String text = ChatColor.stripColor(String.valueOf(value));
return regexPattern.matcher(text).find();
}
/**
* {@inheritDoc}
*/
@Override
public boolean needToFilter() {
return hasValidRegex();
}
public boolean hasValidRegex() {
return regexPattern != null;
}
public String getRegexString() {
return regexString;
}
public Pattern getRegexPattern() {
return regexPattern;
}
@Override
public String toString() {
return regexString;
}
}
@@ -0,0 +1,99 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
/**
* Base implementation of {@link SendHandler} with some common parameters.
*
* @param <T> The subclass that inherited this baseclass.
*/
public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements SendHandler {
protected String header = "";
protected ContentFilter filter = DefaultContentFilter.getInstance();
/**
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sendHeader(sender);
List<String> filteredContent = filterContent(content);
if (filteredContent.isEmpty()) {
sender.sendMessage(String.format("%sThere is no content to display.", ChatColor.RED));
return;
}
sendContent(sender, filteredContent);
}
/**
* Sends the header if header is present.
*
* @param sender The target which the header will be displayed to.
*/
protected void sendHeader(CommandSender sender) {
if (!Strings.isNullOrEmpty(header)) {
sender.sendMessage(header);
}
}
/**
* Filter to keep only contents that matches the filter.
*
* @param content The content to filter on.
* @return The filtered list of content.
*/
protected List<String> filterContent(@NotNull List<String> content) {
if (filter.needToFilter()) {
return content.stream().filter(filter::checkMatch).collect(Collectors.toList());
}
return content;
}
/**
* Display the contents.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
protected abstract void sendContent(@NotNull CommandSender sender, @NotNull List<String> content);
/**
* Sets header to be displayed.
*
* @param header The header text.
* @param replacements String formatting replacements.
* @return Same {@link T} for method chaining.
*/
public T withHeader(@NotNull String header, @NotNull Object...replacements) {
this.header = String.format(header, replacements);
return (T) this;
}
/**
* Sets content filter used to match specific content to be displayed.
*
* @param filter The filter to use.
* @return Same {@link T} for method chaining.
*/
public T withFilter(@NotNull ContentFilter filter) {
this.filter = filter;
return (T) this;
}
public String getHeader() {
return header;
}
public ContentFilter getFilter() {
return filter;
}
}
@@ -0,0 +1,29 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class DefaultSendHandler implements SendHandler {
private static DefaultSendHandler instance;
public static DefaultSendHandler getInstance() {
if (instance == null) {
instance = new DefaultSendHandler();
}
return instance;
}
private DefaultSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sender.sendMessage(content.toArray(new String[0]));
}
}
@@ -0,0 +1,53 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Display the contents in a single line.
*/
public class InlineSendHandler extends BaseSendHandler<InlineSendHandler> {
/**
* Makes a new {@link InlineSendHandler} instance to use.
*
* @return New {@link InlineSendHandler} instance.
*/
public static InlineSendHandler create() {
return new InlineSendHandler();
}
private String delimiter = ChatColor.WHITE + ", ";
public InlineSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender, @NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(String.join(delimiter, content));
}
/**
* Sets the delimiter. A sequence of characters that is used to separate each of the elements in content.
*
* @param delimiter The delimiter to use.
* @return Same {@link InlineSendHandler} for method chaining.
*/
public InlineSendHandler withDelimiter(String delimiter) {
this.delimiter = delimiter;
return this;
}
public String getDelimiter() {
return delimiter;
}
}
@@ -0,0 +1,170 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Display content as a list with optional pagination.
*/
public class PagedSendHandler extends BaseSendHandler<PagedSendHandler> {
/**
* Makes a new {@link PagedSendHandler} instance to use.
*
* @return New {@link PagedSendHandler} instance.
*/
public static PagedSendHandler create() {
return new PagedSendHandler();
}
private boolean paginate = true;
private boolean paginateInConsole = false;
private boolean padEnd = true;
private int linesPerPage = 8;
private int targetPage = 1;
public PagedSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender,
@NotNull List<String> content) {
if (!paginate || (sender instanceof ConsoleCommandSender && !paginateInConsole)) {
sendNormal(sender, content);
return;
}
sendPaged(sender, content);
}
/**
* Send content list without pagination.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
private void sendNormal(@NotNull CommandSender sender,
@NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(content.toArray(new String[0]));
}
/**
* Send content list with pagination.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
private void sendPaged(@NotNull CommandSender sender,
@NotNull List<String> content) {
int totalPages = (content.size() + linesPerPage - 1) / linesPerPage; // Basically just divide round up
if (targetPage < 1 || targetPage > totalPages) {
sender.sendMessage(String.format("%sInvalid page number. Please enter a page number between 1 and %s", ChatColor.RED, totalPages));
return;
}
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Page %s of %s] [Filter '%s']", ChatColor.GRAY, targetPage, totalPages, filter));
} else {
sender.sendMessage(String.format("%s[Page %s of %s]", ChatColor.GRAY, targetPage, totalPages));
}
int startIndex = (targetPage - 1) * linesPerPage;
int pageEndIndex = startIndex + linesPerPage;
int endIndex = Math.min(pageEndIndex, content.size());
List<String> pageContent = content.subList(startIndex, endIndex);
if (padEnd) {
for (int i = 0; i < (pageEndIndex - endIndex); i++) {
pageContent.add("");
}
}
sender.sendMessage(pageContent.toArray(new String[0]));
}
/**
* Sets whether display output should be paginated.
*
* @param paginate State of doing pagination.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doPagination(boolean paginate) {
this.paginate = paginate;
return this;
}
/**
* Sets whether display output should be paginated if is for console output.
* This option will be useless of {@link PagedSendHandler#paginate} is set to false.
*
* @param paginateInConsole State of doing pagination in console.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doPaginationInConsole(boolean paginateInConsole) {
this.paginateInConsole = paginateInConsole;
return this;
}
/**
* Sets whether empty lines should be added if content lines shown is less that {@link PagedSendHandler#linesPerPage}.
*
* @param padEnd State of doing end padding.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doEndPadding(boolean padEnd) {
this.padEnd = padEnd;
return this;
}
/**
* Sets the max number of lines per page. This excludes header.
*
* @param linesPerPage The number of lines per page.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler withLinesPerPage(int linesPerPage) {
this.linesPerPage = linesPerPage;
return this;
}
/**
* Sets the page number to display.
*
* @param targetPage The target page number to display.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler withTargetPage(int targetPage) {
this.targetPage = targetPage;
return this;
}
public boolean isPaginate() {
return paginate;
}
public boolean isPaginateInConsole() {
return paginateInConsole;
}
public boolean isPadEnd() {
return padEnd;
}
public int getLinesPerPage() {
return linesPerPage;
}
public int getTargetPage() {
return targetPage;
}
}
@@ -0,0 +1,20 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Handles the sending of all content to the command sender.
*/
@FunctionalInterface
public interface SendHandler {
/**
* Sends all the content to the given command sender.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
void send(@NotNull CommandSender sender, @NotNull List<String> content);
}
@@ -0,0 +1,20 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Parse objects into string or list of strings.
*/
@FunctionalInterface
public interface ContentParser {
/**
* Parse the object to string(s) and add it to the content.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
void parse(@NotNull CommandSender sender, @NotNull List<String> content);
}
@@ -0,0 +1,60 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Simple parser for list object.
*
* @param <T> List element type.
*/
public class ListContentParser<T> implements ContentParser {
/**
* New list content parser for the given list.
*
* @param list The list object to parse.
* @param <T> List element type.
* @return New {@link MapContentParser} instance.
*/
public static <T> ListContentParser<T> forContent(List<T> list) {
return new ListContentParser<>(list);
}
private final List<T> list;
private String format = "%s";
public ListContentParser(List<T> list) {
this.list = list;
}
/**
* {@inheritDoc}
*/
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
list.forEach(element -> content.add(String.format(format, element)));
}
/**
* Sets the format that will be used to parse each list entry. Uses java string format pattern.
*
* @param format The format to use.
* @return Same {@link ListContentParser} for method chaining.
*/
public ListContentParser<T> withFormat(String format) {
this.format = format;
return this;
}
public List<T> getList() {
return list;
}
public String getFormat() {
return format;
}
}
@@ -0,0 +1,112 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
/**
* Simple parser for map object.
*
* @param <K> Key type.
* @param <V> Value type.
*/
public class MapContentParser<K, V> implements ContentParser {
/**
* New map content parser for the given map.
*
* @param map The map object to parse.
* @param <K> Key type.
* @param <V> Value type.
* @return New {@link MapContentParser} instance.
*/
public static <K, V> MapContentParser<K, V> forContent(Map<K, V> map) {
return new MapContentParser<>(map);
}
private final Map<K, V> map;
private String format = "%s%s%s%s%s";
private ChatColor keyColor = ChatColor.WHITE;
private ChatColor valueColor = ChatColor.WHITE;
private String separator = ": ";
public MapContentParser(Map<K, V> map) {
this.map = map;
}
/**
* {@inheritDoc}
*/
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
map.forEach((k, v) -> content.add(String.format(format, keyColor, k, separator, valueColor, v)));
}
/**
* Sets the format that will be used to parse each map entry. Uses java string format pattern.
*
* @param format The format to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withFormat(String format) {
this.format = format;
return this;
}
/**
* Sets the color for the key text.
*
* @param keyColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withKeyColor(ChatColor keyColor) {
this.keyColor = keyColor;
return this;
}
/**
* Sets the color for the value text.
*
* @param valueColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withValueColor(ChatColor valueColor) {
this.valueColor = valueColor;
return this;
}
/**
* Sets the separator between each key value pairing.
*
* @param separator The separator to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withSeparator(String separator) {
this.separator = separator;
return this;
}
public Map<K, V> getMap() {
return map;
}
public String getFormat() {
return format;
}
public ChatColor getKeyColor() {
return keyColor;
}
public ChatColor getValueColor() {
return valueColor;
}
public String getSeparator() {
return separator;
}
}
@@ -1,311 +0,0 @@
package com.onarandombox.MultiverseCore.generators;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.GeneratorPlugin;
import com.onarandombox.MultiverseCore.api.MVGeneratorManager;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* Default implementation of {@link MVGeneratorManager} to facilitate the detection and management of generator
* plugins installed on user's server.
*/
public class GeneratorManager implements MVGeneratorManager {
private final MultiverseCore plugin;
private final Map<String, String> defaultGens;
private Map<String, GeneratorPlugin> generatorPluginMap;
public GeneratorManager(MultiverseCore plugin) {
this.plugin = plugin;
this.defaultGens = new HashMap<>();
loadDefaultWorldGenerators();
findAndRegisterAllGeneratorPlugins();
Bukkit.getPluginManager().registerEvents(new GeneratorListener(), this.plugin);
}
/**
* Loads default generator strings defined in 'bukkit.yml'.
*/
private void loadDefaultWorldGenerators() {
this.generatorPluginMap = new HashMap<>();
File[] files = this.plugin.getServerFolder().listFiles((file, s) -> s.equalsIgnoreCase("bukkit.yml"));
if (files == null || files.length != 1) {
Logging.fine("Could not read 'bukkit.yml'. Any Default worldgenerators will not be loaded!");
return;
}
FileConfiguration bukkitConfig = YamlConfiguration.loadConfiguration(files[0]);
if (!bukkitConfig.isConfigurationSection("worlds")) {
Logging.fine("'bukkit.yml' missing worlds config section. No default generators found!");
return;
}
Set<String> keys = bukkitConfig.getConfigurationSection("worlds").getKeys(false);
for (String key : keys) {
this.defaultGens.put(key, bukkitConfig.getString("worlds." + key + ".generator", ""));
}
this.generatorPluginMap = Collections.unmodifiableMap(this.generatorPluginMap);
}
/**
* Register all generator plugins detected.
*/
private void findAndRegisterAllGeneratorPlugins() {
Arrays.stream(Bukkit.getPluginManager().getPlugins()).forEach(this::register);
}
/**
* Attempts to register a plugin as {@link SimpleGeneratorPlugin}.
*
* @param genPlugin The plugin to register.
* @return True if registered successfully, else false.
*/
private boolean register(@NotNull Plugin genPlugin) {
return this.register(genPlugin, SimpleGeneratorPlugin.DEFAULT_TEST_ID);
}
/**
* Attempts to register a plugin as {@link SimpleGeneratorPlugin}.
*
* @param genPlugin The plugin to register.
* @param id Generator Id to test for valid chunk generator.
* @return True if registered successfully, else false.
*/
private boolean register(@NotNull Plugin genPlugin, String id) {
Logging.finer("Attempting to register %s as a generator...", genPlugin.getName());
if (this.isGeneratorPlugin(genPlugin)) {
return false;
}
if (!simpleTestGen(genPlugin, id)) {
Logging.finer("%s is probably not a generator plugin!", genPlugin.getName());
return false;
}
this.generatorPluginMap.put(genPlugin.getName(), new SimpleGeneratorPlugin(genPlugin));
Logging.finer("Registered %s as a simple generator plugin.", genPlugin.getName());
return true;
}
/**
* Basic test to see if plugin is a valid generator.
*
* @param generator The potential generator plugin.
* @param id Generator Id to test for valid chunk generator.
* @return True if plugin is a valid generator, else false.
*/
private boolean simpleTestGen(@Nullable Plugin generator, String id) {
if (generator == null) {
return false;
}
// Since we are unsure if the plugin is even suppose to be a generator, we assume any error means its
// not a generator plugin.
try {
return generator.getDefaultWorldGenerator(SimpleGeneratorPlugin.TEST_WORLDNAME, id) != null;
} catch (Exception ignore) {
return false;
}
}
/**
* Unregisters a plugin.
*
* @param genPlugin The plugin to unregister.
* @return True if the plugin was present and now unregistered, else false.
*/
private boolean unregister(@NotNull Plugin genPlugin) {
return this.generatorPluginMap.remove(genPlugin.getName()) != null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean register(@NotNull GeneratorPlugin generatorPlugin) {
Plugin genPlugin = generatorPlugin.getPlugin();
// Make sure not registered before.
GeneratorPlugin registeredGeneratorPlugin = this.getGeneratorPlugin(genPlugin);
if (registeredGeneratorPlugin != null) {
if (!(registeredGeneratorPlugin instanceof SimpleGeneratorPlugin)) {
throw new IllegalStateException("You cannot register plugin for '" + genPlugin.getName() + "' twice!");
}
// If it's auto registration by MV, we can remove in favour of the custom one.
this.unregister(genPlugin);
}
if (this.safelyTestGenerator(genPlugin, generatorPlugin::getDefaultChunkGenerator) != TestResult.VALID) {
return false;
}
this.generatorPluginMap.put(genPlugin.getName(), generatorPlugin);
Logging.fine("Registered %s as a simple generator plugin.", genPlugin.getName());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValidGenerator(@NotNull String genString, @NotNull String worldName) {
return validateGenerator(genString, worldName) == TestResult.VALID;
}
/**
* {@inheritDoc}
*/
@Override
@NotNull
public TestResult validateGenerator(@NotNull String genString, @NotNull String worldName) {
String[] genArray = genString.split(":", 2);
String pluginName = genArray[0];
String id = (genArray.length == 1) ? "" : genArray[1];
Plugin genPlugin = Bukkit.getPluginManager().getPlugin(pluginName);
if (genPlugin == null) {
return TestResult.PLUGIN_DOES_NOT_EXIST;
}
if (!this.isGeneratorPlugin(genPlugin)) {
// Tries to register again with specific Id defined.
if (!this.register(genPlugin, id)) {
return TestResult.INVALID_GENERATOR;
}
}
GeneratorPlugin generatorPlugin = this.getGeneratorPlugin(genPlugin);
if (generatorPlugin == null) {
return TestResult.INVALID_GENERATOR;
}
TestResult testResult = safelyTestGenerator(generatorPlugin.getPlugin(), () -> generatorPlugin.getChunkGenerator(id, worldName));
if (testResult == TestResult.VALID && generatorPlugin instanceof SimpleGeneratorPlugin) {
((SimpleGeneratorPlugin) generatorPlugin).addKnownWorkingId(id);
}
return testResult;
}
/**
* Tries to get chunk generator with wrapper to catch any exceptions that may occur.
*
* @param genPlugin The generator plugin.
* @param genGetter Logic to get the chunk generator.
* @return VALID if successfully got a not-null chunk generator, INVALID_GENERATOR if null,
* ERRORS if exception is thrown.
*/
@NotNull
private TestResult safelyTestGenerator(Plugin genPlugin, Callable<ChunkGenerator> genGetter) {
TestResult result = this.plugin.getUnsafeCallWrapper().wrap(
() -> genGetter.call() == null ? TestResult.INVALID_GENERATOR : TestResult.VALID,
genPlugin.getName(),
"Failed to get the chunk generator: %s"
);
return (result == null) ? TestResult.ERRORS : result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isGeneratorPlugin(@NotNull Plugin genPlugin) {
return this.isGeneratorPlugin(genPlugin.getName());
}
/**
* {@inheritDoc}
*/
@Override
public boolean isGeneratorPlugin(@NotNull String pluginName) {
return this.generatorPluginMap.containsKey(pluginName);
}
/**
* {@inheritDoc}
*/
@Override
@Nullable
public GeneratorPlugin getGeneratorPlugin(@NotNull Plugin genPlugin) {
return this.getGeneratorPlugin(genPlugin.getName());
}
/**
* {@inheritDoc}
*/
@Override
@Nullable
public GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName) {
return this.generatorPluginMap.get(pluginName);
}
/**
* {@inheritDoc}
*/
@Override
@NotNull
public Collection<GeneratorPlugin> getGeneratorPlugins() {
return this.generatorPluginMap.values();
}
/**
* {@inheritDoc}
*/
@Override
@NotNull
public Collection<String> getGeneratorPluginNames() {
return this.generatorPluginMap.keySet();
}
/**
* {@inheritDoc}
*/
@Override
@Nullable
public String getDefaultWorldGen(String worldName) {
return this.defaultGens.get(worldName);
}
/**
* {@inheritDoc}
*/
@Override
@NotNull
public Map<String, String> getDefaultWorldGens() {
return this.defaultGens;
}
/**
* Listen to when a plugin enables/disable to register and unregister accordingly.
*/
private class GeneratorListener implements Listener {
@EventHandler
public void onEnable(PluginEnableEvent event) {
register(event.getPlugin());
}
@EventHandler
public void onDisable(PluginDisableEvent event) {
unregister(event.getPlugin());
}
}
}
@@ -1,94 +0,0 @@
package com.onarandombox.MultiverseCore.generators;
import com.onarandombox.MultiverseCore.api.GeneratorPlugin;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* A default implementation of {@link GeneratorPlugin} for those generator plugins that do not provide their own
* custom {@link GeneratorPlugin} implementation to Multiverse.
*/
public class SimpleGeneratorPlugin implements GeneratorPlugin {
public static String TEST_WORLDNAME = "test";
public static String DEFAULT_TEST_ID = "";
private final Plugin plugin;
private final List<String> workingIds;
SimpleGeneratorPlugin(Plugin plugin) {
this(plugin, DEFAULT_TEST_ID);
}
SimpleGeneratorPlugin(Plugin plugin, String testedId) {
this.plugin = plugin;
this.workingIds = new ArrayList<>();
this.workingIds.add(testedId);
}
/**
* Adds a known generator id that was tested to be working.
*
* @param id The known working generator id.
*/
void addKnownWorkingId(String id) {
if (!this.workingIds.contains(id)) {
this.workingIds.add(id);
}
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Collection<String> suggestIds(@Nullable String currentIdInput) {
return this.workingIds;
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable ChunkGenerator getDefaultChunkGenerator() throws Exception {
return this.plugin.getDefaultWorldGenerator(TEST_WORLDNAME, this.workingIds.get(0));
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable ChunkGenerator getChunkGenerator(String id, String worldName) throws Exception {
return this.plugin.getDefaultWorldGenerator(worldName, id);
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Collection<String> getExampleUsages() {
return null;
}
/**
* {@inheritDoc}
* @return
*/
@Override
public @Nullable String getInfoLink() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Plugin getPlugin() {
return this.plugin;
}
}
@@ -11,6 +11,7 @@ import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.utils.CompatibilityLayer;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@@ -18,6 +19,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.FoodLevelChangeEvent;
@@ -107,4 +109,17 @@ public class MVEntityListener implements Listener {
event.setCancelled(this.plugin.getMVWorldManager().getTheWorldPurger().shouldWeKillThisCreature(mvworld, event.getEntity()));
}
/**
* Handles portal search radius adjustment.
* @param event The Event that was fired.
*/
@EventHandler
public void entityPortal(EntityPortalEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
if (!this.plugin.getMVConfig().isUsingDefaultPortalSearch()) {
CompatibilityLayer.setPortalSearchRadius(event, this.plugin.getMVConfig().getPortalSearchRadius());
}
}
}
@@ -7,10 +7,6 @@
package com.onarandombox.MultiverseCore.listeners;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
@@ -35,6 +31,9 @@ import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Multiverse's {@link Listener} for players.
*/
@@ -313,16 +312,8 @@ public class MVPlayerListener implements Listener {
+ "' was allowed to go to '" + event.getTo().getWorld().getName()
+ "' because enforceaccess is off.");
}
if (!plugin.getMVConfig().isUsingDefaultPortalSearch()) {
try {
Class.forName("org.bukkit.TravelAgent");
if (event.getPortalTravelAgent() != null) {
event.getPortalTravelAgent().setSearchRadius(plugin.getMVConfig().getPortalSearchRadius());
}
} catch (ClassNotFoundException ignore) {
Logging.fine("TravelAgent not available for PlayerPortalEvent for " + event.getPlayer().getName());
}
if (!this.plugin.getMVConfig().isUsingDefaultPortalSearch()) {
CompatibilityLayer.setPortalSearchRadius(event, this.plugin.getMVConfig().getPortalSearchRadius());
}
}
@@ -1,32 +1,35 @@
package com.onarandombox.MultiverseCore.utils;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.Bukkit;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import java.lang.reflect.Method;
/**
* A utility class to enable version specific minecraft features.
* Utility class to enable version specific minecraft features.
*/
public class CompatibilityLayer {
private static Method checkAnchorSpawn;
private static boolean useTravelAgent;
private static Method playerPortalSearchRadius;
private static Method entityPortalSearchRadius;
/**
* Initialise the reflection methods.
* Initialise the reflection class, methods and fields.
*/
public static void init() {
try {
checkAnchorSpawn = PlayerRespawnEvent.class.getDeclaredMethod("isAnchorSpawn");
} catch (NoSuchMethodException e) {
Logging.fine("%s does not support respawn anchors.", Bukkit.getVersion());
}
checkAnchorSpawn = ReflectHelper.getMethod(PlayerRespawnEvent.class, "isAnchorSpawn");
useTravelAgent = ReflectHelper.hasClass("org.bukkit.TravelAgent");
playerPortalSearchRadius = ReflectHelper.getMethod(PlayerPortalEvent.class, "setSearchRadius", int.class);
entityPortalSearchRadius = ReflectHelper.getMethod(EntityPortalEvent.class, "setSearchRadius", int.class);
}
/**
* Check if the respawn point is of respawn anchor type.
* Introduced in minecraft 1.16
* <p>Check if the respawn point is of respawn anchor type.</p>
* <p>Introduced in minecraft 1.16</p>
*
* @param event A player respawn event.
* @return If the respawn location is an anchor point.
@@ -35,12 +38,69 @@ public class CompatibilityLayer {
if (checkAnchorSpawn == null) {
return false;
}
try {
return (boolean) checkAnchorSpawn.invoke(event);
} catch (Exception e) {
Logging.warning("Error checking for: %s", checkAnchorSpawn);
e.printStackTrace();
Boolean result = ReflectHelper.invokeMethod(event, checkAnchorSpawn);
if (result == null) {
Logging.warning("Unable to check if spawning at respawn anchor!");
return false;
}
return false;
return result;
}
}
/**
* <p>Gets if Travel Agent is supported on the server's minecraft version.</p>
* <p>Removed in minecraft 1.14</p>
*
* @return True if Travel Agent is supported, else false.
*/
public static boolean isUseTravelAgent() {
return useTravelAgent;
}
/**
* <p>Sets search radius for a PlayerPortalEvent.</p>
*
* <p>Use travel agent if available, else using new PlayerPortalEvent.setSearchRadius(int) method
* introduced in minecraft 1.15</p>
*
* @param event A Player Portal Event.
* @param searchRadius Target search radius to set to.
*/
public static void setPortalSearchRadius(PlayerPortalEvent event, int searchRadius) {
if (useTravelAgent) {
event.getPortalTravelAgent().setSearchRadius(searchRadius);
event.useTravelAgent(true);
Logging.finer("Used travel agent to set player portal search radius.");
return;
}
if (playerPortalSearchRadius == null) {
Logging.warning("Unable to set player portal search radius!");
return;
}
ReflectHelper.invokeMethod(event, playerPortalSearchRadius, searchRadius);
Logging.finer("Used new method to set player portal search radius.");
}
/**
* <p>Sets search radius for a EntityPortalEvent.</p>
*
* <p>Use travel agent if available, else using new EntityPortalEvent.setSearchRadius(int) method
* introduced in minecraft 1.15</p>
*
* @param event A Entity Portal Event.
* @param searchRadius Target search radius to set to.
*/
public static void setPortalSearchRadius(EntityPortalEvent event, int searchRadius) {
if (useTravelAgent) {
event.getPortalTravelAgent().setSearchRadius(searchRadius);
event.useTravelAgent(true);
Logging.finer("Used travel agent to set entity portal search radius.");
return;
}
if (entityPortalSearchRadius == null) {
Logging.warning("Unable to set entity portal search radius!");
return;
}
ReflectHelper.invokeMethod(event, entityPortalSearchRadius, searchRadius);
Logging.finer("Used new method to set entity portal search radius.");
}
}
@@ -18,7 +18,7 @@ import java.util.stream.Collectors;
*/
public class PlayerFinder {
private static final Pattern UUID_REGEX = Pattern.compile("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}");
private static final Pattern UUID_REGEX = Pattern.compile("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");
private static final Pattern COMMA_SPLIT = Pattern.compile(",");
/**
@@ -135,7 +135,7 @@ public class PlayerFinder {
List<Player> matchedPlayers = getMultiBySelector(playerSelector, sender);
if (matchedPlayers == null || matchedPlayers.isEmpty()) {
Logging.warning("No player found with selector '%s' for %s.", playerSelector, sender.getName());
Logging.fine("No player found with selector '%s' for %s.", playerSelector, sender.getName());
return null;
}
if (matchedPlayers.size() > 1) {
@@ -18,6 +18,7 @@ import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.api.WorldPurger;
import com.onarandombox.MultiverseCore.event.MVWorldDeleteEvent;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.World.Environment;
@@ -36,26 +37,25 @@ import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Public facing API to add/remove Multiverse worlds.
*/
public class WorldManager implements MVWorldManager {
private final MultiverseCore plugin;
private final Pattern worldNamePattern = Pattern.compile("[a-zA-Z0-9/._-]+");
private final WorldPurger worldPurger;
private final Map<String, MultiverseWorld> worlds;
private Map<String, WorldProperties> worldsFromTheConfig;
@@ -74,7 +74,6 @@ public class WorldManager implements MVWorldManager {
* {@inheritDoc}
*/
@Override
@Deprecated
public void getDefaultWorldGenerators() {
this.defaultGens = new HashMap<String, String>();
File[] files = this.plugin.getServerFolder().listFiles(new FilenameFilter() {
@@ -131,7 +130,7 @@ public class WorldManager implements MVWorldManager {
}
// Check for valid world name
if (!(WorldNameChecker.isValidWorldName(oldName) && WorldNameChecker.isValidWorldName(oldName))) {
if (!(WorldNameChecker.isValidWorldName(oldName) && WorldNameChecker.isValidWorldName(newName))) {
return false;
}
@@ -302,7 +301,6 @@ public class WorldManager implements MVWorldManager {
* {@inheritDoc}
*/
@Override
@Deprecated
public ChunkGenerator getChunkGenerator(String generator, final String generatorID, final String worldName) {
if (generator == null) {
return null;
@@ -389,14 +387,14 @@ public class WorldManager implements MVWorldManager {
this.worldsFromTheConfig.get(name).cacheVirtualProperties();
if (unloadBukkit && this.unloadWorldFromBukkit(name, true)) {
this.worlds.remove(name);
Logging.info("World '%s' was unloaded from memory.", name);
Logging.info("World '%s' was unloaded from Bukkit.", name);
return true;
} else if (!unloadBukkit){
this.worlds.remove(name);
Logging.info("World '%s' was unloaded from memory.", name);
Logging.info("World '%s' was unloaded from Multiverse.", name);
return true;
} else {
Logging.warning("World '%s' could not be unloaded. Is it a default world?", name);
Logging.warning("World '%s' could not be unloaded from Bukkit. Is it a default world?", name);
}
} else if (this.plugin.getServer().getWorld(name) != null) {
Logging.warning("Hmm Multiverse does not know about this world but it's loaded in memory.");
@@ -619,6 +617,14 @@ public class WorldManager implements MVWorldManager {
*/
@Override
public MultiverseWorld getMVWorld(String name) {
return this.getMVWorld(name, true);
}
/**
* {@inheritDoc}
*/
@Override
public MultiverseWorld getMVWorld(String name, boolean checkAliases) {
if (name == null) {
return null;
}
@@ -626,7 +632,7 @@ public class WorldManager implements MVWorldManager {
if (world != null) {
return world;
}
return this.getMVWorldByAlias(name);
return (checkAliases) ? this.getMVWorldByAlias(name) : null;
}
/**
@@ -635,7 +641,7 @@ public class WorldManager implements MVWorldManager {
@Override
public MultiverseWorld getMVWorld(World world) {
if (world != null) {
return this.getMVWorld(world.getName());
return this.getMVWorld(world.getName(), false);
}
return null;
}
@@ -660,7 +666,15 @@ public class WorldManager implements MVWorldManager {
*/
@Override
public boolean isMVWorld(final String name) {
return (this.worlds.containsKey(name) || isMVWorldAlias(name));
return this.isMVWorld(name, true);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMVWorld(final String name, boolean checkAliases) {
return this.worlds.containsKey(name) || (checkAliases && this.isMVWorldAlias(name));
}
/**
@@ -696,7 +710,10 @@ public class WorldManager implements MVWorldManager {
for (World w : myWorlds) {
String name = w.getName();
if (!worldsFromTheConfig.containsKey(name)) {
String generator = this.plugin.getMVGeneratorManager().getDefaultWorldGen(name);
String generator = null;
if (this.defaultGens.containsKey(name)) {
generator = this.defaultGens.get(name);
}
this.addWorld(name, w.getEnvironment(), String.valueOf(w.getSeed()), w.getWorldType(), w.canGenerateStructures(), generator);
}
}
@@ -880,12 +897,23 @@ public class WorldManager implements MVWorldManager {
*/
@Override
public boolean regenWorld(String name, boolean useNewSeed, boolean randomSeed, String seed) {
return regenWorld(name, useNewSeed, randomSeed, seed, false);
}
/**
* {@inheritDoc}
*/
@Override
public boolean regenWorld(String name, boolean useNewSeed, boolean randomSeed, String seed, boolean keepGameRules) {
MultiverseWorld world = this.getMVWorld(name);
if (world == null)
if (world == null) {
Logging.warning("Unable to regen a world that does not exist!");
return false;
}
List<Player> ps = world.getCBWorld().getPlayers();
// Apply new seed if needed.
if (useNewSeed) {
long theSeed;
@@ -901,19 +929,64 @@ public class WorldManager implements MVWorldManager {
world.setSeed(theSeed);
}
WorldType type = world.getWorldType();
if (this.deleteWorld(name, false, false)) {
this.doLoad(name, true, type);
SafeTTeleporter teleporter = this.plugin.getSafeTTeleporter();
Location newSpawn = world.getSpawnLocation();
// Send all players that were in the old world, BACK to it!
for (Player p : ps) {
teleporter.safelyTeleport(null, p, newSpawn, true);
// Save current GameRules if needed.
Map<GameRule<?>, Object> gameRuleMap = null;
if (keepGameRules) {
gameRuleMap = new HashMap<>(GameRule.values().length);
World CBWorld = world.getCBWorld();
for (GameRule<?> gameRule : GameRule.values()) {
// Only save if not default value.
Object value = CBWorld.getGameRuleValue(gameRule);
if (value != CBWorld.getGameRuleDefault(gameRule)) {
gameRuleMap.put(gameRule, value);
}
}
return true;
}
return false;
// Do the regen.
if (!this.deleteWorld(name, false, false)) {
Logging.severe("Unable to regen world as world cannot be deleted.");
return false;
}
if (!this.doLoad(name, true, type)) {
Logging.severe("Unable to regen world as world cannot be loaded.");
return false;
}
// Get new MultiverseWorld reference.
world = this.getMVWorld(name);
// Load back GameRules if needed.
if (keepGameRules) {
Logging.fine("Restoring previous world's GameRules...");
World CBWorld = world.getCBWorld();
for (Map.Entry<GameRule<?>, Object> gameRuleEntry : gameRuleMap.entrySet()) {
if (!setGameRuleValue(CBWorld, gameRuleEntry.getKey(), gameRuleEntry.getValue())) {
Logging.warning("Unable to set GameRule '%s' to '%s' on regen world.",
gameRuleEntry.getKey().getName(), gameRuleEntry.getValue());
}
}
}
// Send all players that were in the old world, BACK to it!
SafeTTeleporter teleporter = this.plugin.getSafeTTeleporter();
Location newSpawn = world.getSpawnLocation();
for (Player p : ps) {
teleporter.safelyTeleport(null, p, newSpawn, true);
}
return true;
}
private <T> boolean setGameRuleValue(World world, GameRule<T> gameRule, Object value) {
try {
return world.setGameRule(gameRule, (T) value);
} catch (Exception e) {
return false;
}
}
/**
@@ -939,4 +1012,21 @@ public class WorldManager implements MVWorldManager {
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getPotentialWorlds() {
File worldContainer = this.plugin.getServer().getWorldContainer();
if (worldContainer == null) {
return Collections.emptyList();
}
return Arrays.stream(worldContainer.listFiles())
.filter(File::isDirectory)
.filter(folder -> !this.isMVWorld(folder.getName(), false))
.filter(WorldNameChecker::isValidWorldFolder)
.map(File::getName)
.collect(Collectors.toList());
}
}
@@ -1,11 +1,12 @@
package com.onarandombox.MultiverseCore.utils.metrics;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.MultiLineChart;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.bstats.bukkit.Metrics;
enum MetricsHelper {
;
@@ -17,16 +18,16 @@ enum MetricsHelper {
map.put(key, count + 1);
}
static Metrics.AdvancedPie createAdvancedPieChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
static AdvancedPie createAdvancedPieChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
Map<String, Integer> map = new HashMap<>();
metricsFunc.accept(map);
return new Metrics.AdvancedPie(chartId, () -> map);
return new AdvancedPie(chartId, () -> map);
}
static Metrics.MultiLineChart createMultiLineChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
static MultiLineChart createMultiLineChart(String chartId, Consumer<Map<String, Integer>> metricsFunc) {
Map<String, Integer> map = new HashMap<>();
metricsFunc.accept(map);
return new Metrics.MultiLineChart(chartId, () -> map);
return new MultiLineChart(chartId, () -> map);
}
}
@@ -14,15 +14,9 @@ import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
@@ -31,9 +25,6 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MultiverseCore.class, PluginDescriptionFile.class, JavaPluginLoader.class})
@PowerMockIgnore("javax.script.*")
public class TestDebugMode {
TestInstanceCreator creator;
Server mockServer;
@@ -7,35 +7,29 @@ import com.onarandombox.MultiverseCore.utils.MockWorldFactory;
import com.onarandombox.MultiverseCore.utils.TestInstanceCreator;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Sheep;
import org.bukkit.entity.Zombie;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MultiverseCore.class, PluginDescriptionFile.class, JavaPluginLoader.class})
@PowerMockIgnore("javax.script.*")
public class TestEntitySpawnRules {
TestInstanceCreator creator;
MultiverseCore core;
@@ -157,6 +151,6 @@ public class TestEntitySpawnRules {
when(zombie.getType()).thenReturn(EntityType.ZOMBIE);
when(zombie.getWorld()).thenReturn(this.cbworld);
when(cbworld.getEntities()).thenReturn(Arrays.asList((Entity) sheep, (Entity) zombie));
when(cbworld.getEntities()).thenReturn(Arrays.asList(sheep, zombie));
}
}
@@ -2,24 +2,17 @@ package com.onarandombox.MultiverseCore;
import com.onarandombox.MultiverseCore.utils.TestInstanceCreator;
import org.bukkit.Material;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MultiverseCore.class, PluginDescriptionFile.class, JavaPluginLoader.class})
@PowerMockIgnore("javax.script.*")
public class TestEntryFeeConversion {
private TestInstanceCreator creator;
@@ -6,24 +6,16 @@ import org.bukkit.Server;
import org.bukkit.World.Environment;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MultiverseCore.class, PluginDescriptionFile.class, JavaPluginLoader.class })
@PowerMockIgnore("javax.script.*")
public class TestModifyCommand {
TestInstanceCreator creator;
Server mockServer;
@@ -13,8 +13,6 @@ import com.onarandombox.MultiverseCore.configuration.SpawnLocation;
import com.onarandombox.MultiverseCore.listeners.MVAsyncPlayerChatListener;
import com.onarandombox.MultiverseCore.utils.MockWorldFactory;
import com.onarandombox.MultiverseCore.utils.TestInstanceCreator;
import com.onarandombox.MultiverseCore.utils.WorldManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
@@ -36,33 +34,26 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.weather.ThunderChangeEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.internal.verification.VerificationModeFactory;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ PluginManager.class, MultiverseCore.class, Permission.class, Bukkit.class,
WeatherChangeEvent.class, ThunderChangeEvent.class, AsyncPlayerChatEvent.class,
PlayerJoinEvent.class, PlayerRespawnEvent.class, EntityRegainHealthEvent.class,
FoodLevelChangeEvent.class, WorldManager.class, PluginDescriptionFile.class, JavaPluginLoader.class })
@PowerMockIgnore("javax.script.*")
public class TestWorldProperties {
private TestInstanceCreator creator;
private MultiverseCore core;
@@ -122,7 +113,7 @@ public class TestWorldProperties {
String[] netherArgs = new String[] { "import", "world_nether", "nether" };
core.onCommand(mockCommandSender, mockCommand, "", netherArgs);
verify(mockCommandSender).sendMessage("Starting import of world 'world_nether'...");
verify(mockCommandSender, VerificationModeFactory.times(2)).sendMessage(
verify(mockCommandSender, times(2)).sendMessage(
ChatColor.GREEN + "Complete!");
assertEquals(core.getServer().getWorlds().size(), 2);
@@ -222,7 +213,7 @@ public class TestWorldProperties {
assertTrue(mvWorld.setColor("BLACK"));
assertFalse(mvWorld.setColor("INVALID COLOR"));
assertEquals(ChatColor.BLACK, mvWorld.getColor());
assertEquals(ChatColor.BLACK.toString() + "alias" + ChatColor.WHITE.toString(), mvWorld.getColoredWorldString());
assertEquals(ChatColor.BLACK + "alias" + ChatColor.WHITE, mvWorld.getColoredWorldString());
mvWorld.setPVPMode(false);
assertEquals(false, mvWorld.isPVPEnabled());
assertTrue(mvWorld.setScaling(2D));
@@ -329,7 +320,7 @@ public class TestWorldProperties {
assertEquals(true, mvWorld.isHidden());
assertEquals("alias", mvWorld.getAlias());
assertEquals(ChatColor.GREEN, mvWorld.getColor());
assertEquals(ChatColor.GREEN.toString() + "alias" + ChatColor.WHITE.toString(), mvWorld.getColoredWorldString());
assertEquals(ChatColor.GREEN + "alias" + ChatColor.WHITE, mvWorld.getColoredWorldString());
assertEquals(false, mvWorld.isPVPEnabled());
assertEquals(2D, mvWorld.getScaling(), 0);
assertSame(worldManager.getMVWorld("world_nether").getCBWorld(),
@@ -366,36 +357,36 @@ public class TestWorldProperties {
when(mockPlayer.hasPlayedBefore()).thenReturn(true);
when(mockPlayer.hasPermission("multiverse.access.world")).thenReturn(true);
when(mockPlayer.getName()).thenReturn("MultiverseMan");
playerChatEvent = PowerMockito.mock(AsyncPlayerChatEvent.class);
playerChatEvent = mock(AsyncPlayerChatEvent.class);
when(playerChatEvent.getPlayer()).thenReturn(mockPlayer);
when(playerChatEvent.getFormat()).thenReturn("format");
// player join
mockNewPlayer = mock(Player.class);
when(mockNewPlayer.hasPlayedBefore()).thenReturn(false);
playerJoinEvent = PowerMockito.mock(PlayerJoinEvent.class);
playerJoinEvent = mock(PlayerJoinEvent.class);
when(playerJoinEvent.getPlayer()).thenReturn(mockPlayer);
playerNewJoinEvent = PowerMockito.mock(PlayerJoinEvent.class);
playerNewJoinEvent = mock(PlayerJoinEvent.class);
when(playerNewJoinEvent.getPlayer()).thenReturn(mockNewPlayer);
// player respawn
playerRespawnBed = PowerMockito.mock(PlayerRespawnEvent.class);
playerRespawnBed = mock(PlayerRespawnEvent.class);
when(playerRespawnBed.getPlayer()).thenReturn(mockPlayer);
when(playerRespawnBed.isBedSpawn()).thenReturn(true);
playerRespawnNormal = PowerMockito.mock(PlayerRespawnEvent.class);
playerRespawnNormal = mock(PlayerRespawnEvent.class);
when(playerRespawnNormal.getPlayer()).thenReturn(mockPlayer);
when(playerRespawnNormal.isBedSpawn()).thenReturn(false);
//// Entity events
mockHumanEntity = mock(HumanEntity.class);
// entity regain health
entityRegainHealthEvent = PowerMockito.mock(EntityRegainHealthEvent.class);
entityRegainHealthEvent = mock(EntityRegainHealthEvent.class);
when(entityRegainHealthEvent.getRegainReason()).thenReturn(RegainReason.REGEN);
when(mockHumanEntity.getLocation()).thenReturn(new Location(world, 0, 0, 0));
when(entityRegainHealthEvent.getEntity()).thenReturn(mockHumanEntity);
// entity food level change event
entityFoodLevelChangeEvent = PowerMockito.mock(FoodLevelChangeEvent.class);
entityFoodLevelChangeEvent = mock(FoodLevelChangeEvent.class);
// this won't do anything since we're not mocking a player,
// but the plugin should be able to handle this!
when(entityFoodLevelChangeEvent.getEntity()).thenReturn(mockHumanEntity);
entityFoodLevelRiseEvent = PowerMockito.mock(FoodLevelChangeEvent.class);
entityFoodLevelRiseEvent = mock(FoodLevelChangeEvent.class);
when(mockPlayer.getFoodLevel()).thenReturn(2);
when(entityFoodLevelRiseEvent.getEntity()).thenReturn(mockPlayer);
when(entityFoodLevelRiseEvent.getFoodLevel()).thenReturn(3);
@@ -4,28 +4,21 @@ import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.api.WorldPurger;
import com.onarandombox.MultiverseCore.utils.TestInstanceCreator;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Sheep;
import org.bukkit.entity.Zombie;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.Arrays;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MultiverseCore.class, PluginDescriptionFile.class, JavaPluginLoader.class })
@PowerMockIgnore("javax.script.*")
public class TestWorldPurger {
TestInstanceCreator creator;
MultiverseCore core;
@@ -121,6 +114,6 @@ public class TestWorldPurger {
zombie = mock(Zombie.class);
when(zombie.getType()).thenReturn(EntityType.ZOMBIE);
when(zombie.getWorld()).thenReturn(world);
when(cbworld.getEntities()).thenReturn(Arrays.asList((Entity) sheep, (Entity) zombie));
when(cbworld.getEntities()).thenReturn(Arrays.asList(sheep, zombie));
}
}
@@ -12,39 +12,30 @@ import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException;
import com.onarandombox.MultiverseCore.utils.MockWorldFactory;
import com.onarandombox.MultiverseCore.utils.TestInstanceCreator;
import com.onarandombox.MultiverseCore.utils.WorldCreatorMatcher;
import com.onarandombox.MultiverseCore.utils.WorldManager;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Server;
import org.bukkit.WorldCreator;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.Matchers;
import org.mockito.internal.verification.VerificationModeFactory;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
import static junit.framework.Assert.*;
import static org.mockito.Mockito.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ PluginManager.class, MultiverseCore.class, Permission.class, Bukkit.class, WorldManager.class,
PluginDescriptionFile.class, JavaPluginLoader.class })
@PowerMockIgnore("javax.script.*")
public class TestWorldStuff {
private TestInstanceCreator creator;
@@ -207,7 +198,7 @@ public class TestWorldStuff {
assertEquals(0, creator.getCore().getMVWorldManager().getMVWorlds().size());
// Verify
verify(mockCommandSender).sendMessage("That generator plugin is not installed on your server!");
verify(mockCommandSender).sendMessage("Invalid generator! 'BogusGen'. " + ChatColor.RED + "Aborting world creation.");
}
@Test
@@ -1,7 +1,8 @@
package com.onarandombox.MultiverseCore.utils;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
@@ -10,10 +11,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.dumptruckman.minecraft.util.Logging;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class FileUtilsTest {
@@ -137,7 +136,7 @@ public class FileUtilsTest {
assertTrue(Files.isRegularFile(targetChildDirFile));
}
@Test()
@Test
public void copyFolder_intoExistingFolder_whereFileExists() throws Exception {
Path targetDir = Files.createDirectory(tempDir.resolve("target"));
Path targetFile = Files.createFile(targetDir.resolve("parentDirFile.txt"));
@@ -21,13 +21,17 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.LinkedHashMap;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MockWorldFactory {
@@ -27,13 +27,9 @@ import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler;
import org.mockito.ArgumentMatchers;
import org.mockito.internal.util.reflection.ReflectionMemberAccessor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.easymock.PowerMock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.MockGateway;
import org.powermock.reflect.Whitebox;
import java.io.File;
import java.lang.reflect.Field;
@@ -44,15 +40,16 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
public class TestInstanceCreator {
private MultiverseCore core;
@@ -69,28 +66,20 @@ public class TestInstanceCreator {
pluginDirectory.mkdirs();
assertTrue(pluginDirectory.exists());
MockGateway.MOCK_STANDARD_METHODS = false;
// Initialize the Mock server.
mockServer = mock(Server.class);
JavaPluginLoader mockPluginLoader = PowerMock.createMock(JavaPluginLoader.class);
Whitebox.setInternalState(mockPluginLoader, "server", mockServer);
JavaPluginLoader mockPluginLoader = mock(JavaPluginLoader.class);
new ReflectionMemberAccessor().set(JavaPluginLoader.class.getDeclaredField("server"), mockPluginLoader, mockServer);
when(mockServer.getName()).thenReturn("TestBukkit");
Logger.getLogger("Minecraft").setParent(Util.logger);
when(mockServer.getLogger()).thenReturn(Util.logger);
when(mockServer.getWorldContainer()).thenReturn(worldsDirectory);
// Return a fake PDF file.
PluginDescriptionFile pdf = PowerMockito.spy(new PluginDescriptionFile("Multiverse-Core", "2.2-Test",
PluginDescriptionFile pdf = spy(new PluginDescriptionFile("Multiverse-Core", "2.2-Test",
"com.onarandombox.MultiverseCore.MultiverseCore"));
when(pdf.getAuthors()).thenReturn(new ArrayList<String>());
core = PowerMockito.spy(new MultiverseCore(mockPluginLoader, pdf, pluginDirectory, new File(pluginDirectory, "testPluginFile")));
PowerMockito.doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
return null; // don't run metrics in tests
}
}).when(core, "setupMetrics");
core = spy(new MultiverseCore(mockPluginLoader, pdf, pluginDirectory, new File(pluginDirectory, "testPluginFile")));
// Let's let all MV files go to bin/test
doReturn(pluginDirectory).when(core).getDataFolder();
@@ -103,7 +92,7 @@ public class TestInstanceCreator {
JavaPlugin[] plugins = new JavaPlugin[] { core };
// Mock the Plugin Manager
PluginManager mockPluginManager = PowerMockito.mock(PluginManager.class);
PluginManager mockPluginManager = mock(PluginManager.class);
when(mockPluginManager.getPlugins()).thenReturn(plugins);
when(mockPluginManager.getPlugin("Multiverse-Core")).thenReturn(core);
when(mockPluginManager.getPermission(anyString())).thenReturn(null);
@@ -157,7 +146,7 @@ public class TestInstanceCreator {
when(mockServer.getPluginManager()).thenReturn(mockPluginManager);
when(mockServer.createWorld(ArgumentMatchers.isA(WorldCreator.class))).thenAnswer(
when(mockServer.createWorld(isA(WorldCreator.class))).thenAnswer(
new Answer<World>() {
@Override
public World answer(InvocationOnMock invocation) throws Throwable {
@@ -219,7 +208,7 @@ public class TestInstanceCreator {
buscriptfield.setAccessible(true);
try {
buscript = PowerMockito.spy(new Buscript(core));
buscript = spy(new Buscript(core));
when(buscript.getPlugin()).thenReturn(core);
} catch (NullPointerException e) {
buscript = null;
@@ -228,25 +217,25 @@ public class TestInstanceCreator {
buscriptfield.set(core, buscript);
// Set worldManager
WorldManager wm = PowerMockito.spy(new WorldManager(core));
WorldManager wm = spy(new WorldManager(core));
Field worldmanagerfield = MultiverseCore.class.getDeclaredField("worldManager");
worldmanagerfield.setAccessible(true);
worldmanagerfield.set(core, wm);
// Set playerListener
MVPlayerListener pl = PowerMockito.spy(new MVPlayerListener(core));
MVPlayerListener pl = spy(new MVPlayerListener(core));
Field playerlistenerfield = MultiverseCore.class.getDeclaredField("playerListener");
playerlistenerfield.setAccessible(true);
playerlistenerfield.set(core, pl);
// Set entityListener
MVEntityListener el = PowerMockito.spy(new MVEntityListener(core));
MVEntityListener el = spy(new MVEntityListener(core));
Field entitylistenerfield = MultiverseCore.class.getDeclaredField("entityListener");
entitylistenerfield.setAccessible(true);
entitylistenerfield.set(core, el);
// Set weatherListener
MVWeatherListener wl = PowerMockito.spy(new MVWeatherListener(core));
MVWeatherListener wl = spy(new MVWeatherListener(core));
Field weatherlistenerfield = MultiverseCore.class.getDeclaredField("weatherListener");
weatherlistenerfield.setAccessible(true);
weatherlistenerfield.set(core, wl);
@@ -264,9 +253,9 @@ public class TestInstanceCreator {
when(commandSender.getServer()).thenReturn(mockServer);
when(commandSender.getName()).thenReturn("MockCommandSender");
when(commandSender.isPermissionSet(anyString())).thenReturn(true);
when(commandSender.isPermissionSet(ArgumentMatchers.isA(Permission.class))).thenReturn(true);
when(commandSender.isPermissionSet(isA(Permission.class))).thenReturn(true);
when(commandSender.hasPermission(anyString())).thenReturn(true);
when(commandSender.hasPermission(ArgumentMatchers.isA(Permission.class))).thenReturn(true);
when(commandSender.hasPermission(isA(Permission.class))).thenReturn(true);
when(commandSender.addAttachment(core)).thenReturn(null);
when(commandSender.isOp()).thenReturn(true);
@@ -10,8 +10,8 @@ package com.onarandombox.MultiverseCore.utils;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
public class Util {
private Util() {}
@@ -11,7 +11,7 @@ import org.bukkit.WorldCreator;
import org.mockito.ArgumentMatcher;
public class WorldCreatorMatcher implements ArgumentMatcher<WorldCreator> {
private WorldCreator worldCreator;
private final WorldCreator worldCreator;
private boolean careAboutSeeds = false;
private boolean careAboutGenerators = false;
@@ -0,0 +1 @@
mock-maker-inline