diff --git a/.github/workflows/generic.checkstyle.yml b/.github/workflows/generic.checkstyle.yml index a57e6cd2d..fa83ae0bf 100644 --- a/.github/workflows/generic.checkstyle.yml +++ b/.github/workflows/generic.checkstyle.yml @@ -8,11 +8,15 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + pull-requests: write + continue-on-error: true steps: - uses: actions/checkout@v3 - uses: dbelyaev/action-checkstyle@v0.8.4 + continue-on-error: true with: github_token: ${{ secrets.GITHUB_TOKEN }} checkstyle_version: 10.12.2 checkstyle_config: ./config/mv_checks.xml + level: warning diff --git a/.github/workflows/generic.github_release.yml b/.github/workflows/generic.github_release.yml index a75ea2c03..ba5f817bd 100644 --- a/.github/workflows/generic.github_release.yml +++ b/.github/workflows/generic.github_release.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '21' distribution: 'adopt' cache: gradle diff --git a/.github/workflows/generic.test.yml b/.github/workflows/generic.test.yml index 3713010bf..ce2175105 100644 --- a/.github/workflows/generic.test.yml +++ b/.github/workflows/generic.test.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: '21' distribution: 'adopt' cache: gradle diff --git a/build.gradle b/build.gradle index 4ff62e252..e97d50743 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,33 @@ import org.apache.tools.ant.filters.ReplaceTokens +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id 'java-library' id 'maven-publish' - id 'com.github.johnrengelman.shadow' version '7.1.2' + id 'com.gradleup.shadow' version '8.3.5' + id "org.jetbrains.kotlin.jvm" version "2.0.21" } version = System.getenv('GITHUB_VERSION') ?: 'local' -group = 'com.onarandombox.multiversecore' +group = 'org.mvplugins.multiverse.core' description = 'Multiverse-Core' -java.sourceCompatibility = JavaVersion.VERSION_11 +compileJava { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +compileTestJava { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +compileTestKotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + javaParameters.set(true) + } +} repositories { mavenLocal() @@ -39,37 +56,91 @@ repositories { maven { url = uri('https://repo.maven.apache.org/maven2/') } + + maven { + name = 'aikar repo' + url = uri('https://repo.aikar.co/content/groups/aikar/') + } + + maven { + name = 'glaremasters repo' + url = 'https://repo.glaremasters.me/repository/towny/' + } + + maven { + name = "helpchatRepoReleases" + url = uri("https://repo.helpch.at/releases/") + } + + maven { + name = 'papermc repo' + url = 'https://repo.papermc.io/repository/maven-public/' + } +} + +configurations { + // Add configuration for server API + compileOnly.extendsFrom serverApi + runtimeClasspath.extendsFrom serverApi + + // Add configuration for external plugins + implementation.extendsFrom externalPlugin + + // Add configuration for dependencies that will be included in fat jar + compileClasspath.extendsFrom shadowed + testCompileClasspath.extendsFrom shadowed + testRuntimeClasspath.extendsFrom shadowed } dependencies { - implementation 'org.bukkit:bukkit:1.13.2-R0.1-SNAPSHOT' + serverApi 'org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT' - implementation('com.github.MilkBowl:VaultAPI:1.7') { + // Economy + externalPlugin('com.github.MilkBowl:VaultAPI:1.7.1') { exclude group: 'org.bukkit', module: 'bukkit' } - compileOnly('me.main__.util:SerializationConfig:1.7') { - exclude group: 'org.bukkit', module: 'bukkit' + // PlaceholderAPI + externalPlugin 'me.clip:placeholderapi:2.11.6' + + // Command Framework + shadowed 'co.aikar:acf-paper:0.5.1-SNAPSHOT' + + // Config + shadowed('io.github.townyadvanced.commentedconfiguration:CommentedConfiguration:1.0.1') { + exclude group: 'org.spigotmc', module: 'spigot-api' } - compileOnly('com.pneumaticraft.commandhandler:CommandHandler:11') { - exclude group: 'org.bukkit', module: 'bukkit' - exclude group: 'junit', module: 'junit' + + // Utils + shadowed 'io.vavr:vavr:0.10.4' + shadowed 'org.glassfish.hk2:hk2-locator:3.0.3' + shadowed('org.glassfish.hk2:hk2-inhabitant-generator:3.0.3') { + exclude group: 'org.apache.maven', module: 'maven-core' } - compileOnly 'com.dumptruckman.minecraft:buscript:2.0-SNAPSHOT' - compileOnly 'org.bstats:bstats-bukkit:2.2.1' - compileOnly('com.dumptruckman.minecraft:Logging:1.1.1') { + shadowed('com.dumptruckman.minecraft:Logging:1.1.1') { exclude group: 'junit', module: 'junit' } - compileOnly 'de.themoep.idconverter:mappings:1.2-SNAPSHOT' - compileOnly 'org.jetbrains:annotations:16.0.2' + shadowed 'de.themoep.idconverter:mappings:1.2-SNAPSHOT' + shadowed('org.bstats:bstats-bukkit:3.1.0') { + exclude group: 'org.bukkit', module: 'bukkit' + } + shadowed 'net.minidev:json-smart:2.4.9' + shadowed 'org.jetbrains:annotations:22.0.0' + shadowed 'io.papermc:paperlib:1.0.8' - testImplementation 'org.spigotmc:spigot-api:1.19.3-R0.1-SNAPSHOT' + // Tests + testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21' + testImplementation 'org.mockbukkit.mockbukkit:mockbukkit-v1.21:4.24.1' testImplementation('com.googlecode.json-simple:json-simple:1.1.1') { exclude group: 'junit', module: 'junit' } - testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:3.11.2' - testImplementation 'commons-io:commons-io:2.4' + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'com.natpryce:hamkrest:1.8.0.1' + testImplementation 'org.mockito.kotlin:mockito-kotlin:4.1.0' + + // Annotation Processors + annotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3' + testAnnotationProcessor 'org.glassfish.hk2:hk2-metadata-generator:3.0.3' } @@ -78,11 +149,11 @@ java { withJavadocJar() } -tasks.withType(JavaCompile) { +tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } -tasks.withType(Javadoc) { +tasks.withType(Javadoc).configureEach { options.encoding = 'UTF-8' } @@ -92,28 +163,40 @@ configurations { it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) } it.outgoing.artifact(shadowJar) } - - testCompileOnly.extendsFrom compileOnly - testRuntimeOnly.extendsFrom testCompileOnly - - shadowed.extendsFrom compileOnly { - canBeResolved = true - } } + publishing { publications { maven(MavenPublication) { from components.java pom.withXml { - asNode().dependencies.'*'.findAll() { - it.scope.text() == 'runtime' && project.configurations.implementation.allDependencies.find { dep -> - dep.name == it.artifactId.text() - } + Node pomNode = asNode() + + // Remove Kotlin dependency + pomNode.dependencies.'*'.findAll() { + it.groupId.text() == 'org.jetbrains.kotlin' + }.each() { + it.parent().remove(it) + } + + // Switch runtime deps to provided + pomNode.dependencies.'*'.findAll() { + it.scope.text() == 'runtime' }.each() { it.scope*.value = 'provided' } + + // Add spigot api to pom + project.configurations.serverApi.allDependencies.each { dependency -> + pomNode.dependencies[0].appendNode("dependency").with { + it.appendNode("groupId", dependency.group) + it.appendNode("artifactId", dependency.name) + it.appendNode("version", dependency.version) + it.appendNode("scope", "provided") + } + } } } } @@ -133,6 +216,13 @@ publishing { url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl credentials(PasswordCredentials) } + + maven { + // todo: remove before mv5 release + name = "multiverseBeta" + url = "https://repo.c0ding.party/multiverse-beta" + credentials(PasswordCredentials) + } } } @@ -151,6 +241,19 @@ task prepareSource(type: Sync) { compileJava { source = prepareSource.outputs } +tasks.withType(JavaCompile) { + configure(options) { + options.compilerArgs << '-Aorg.glassfish.hk2.metadata.location=META-INF/hk2-locator/Multiverse-Core' + } +} + +compileKotlin { + // We're not using Kotlin in the plugin itself, just tests! + enabled = false +} +configurations.findAll { !it.name.startsWith('test') }.each { + it.exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' +} processResources { def props = [version: "${project.version}"] @@ -175,20 +278,45 @@ javadoc { project.configurations.api.canBeResolved = true shadowJar { - relocate 'me.main__.util', 'com.onarandombox.serializationconfig' - relocate 'com.pneumaticraft.commandhandler', 'com.onarandombox.commandhandler' - relocate 'buscript', 'com.onarandombox.buscript' - relocate 'org.bstats', 'com.onarandombox.bstats' - relocate 'com.dumptruckman.minecraft.util.Logging', 'com.onarandombox.MultiverseCore.utils.CoreLogging' - relocate 'com.dumptruckman.minecraft.util.DebugLog', 'com.onarandombox.MultiverseCore.utils.DebugFileLogger' - relocate 'org.codehaus.jettison', 'com.onarandombox.jettison' - relocate 'de.themoep.idconverter', 'com.onarandombox.idconverter' + relocate 'co.aikar', 'org.mvplugins.multiverse.external.acf' + relocate 'com.dumptruckman.minecraft.util.Logging', 'org.mvplugins.multiverse.core.utils.CoreLogging' + relocate 'com.dumptruckman.minecraft.util.DebugLog', 'org.mvplugins.multiverse.core.utils.DebugFileLogger' + relocate 'de.themoep.idconverter', 'org.mvplugins.multiverse.external.idconverter' + relocate 'io.github.townyadvanced.commentedconfiguration', 'org.mvplugins.multiverse.external.commentedconfiguration' + relocate 'me.main__.util', 'org.mvplugins.multiverse.external.serializationconfig' + relocate 'org.bstats', 'org.mvplugins.multiverse.external.bstats' + relocate 'com.sun', 'org.mvplugins.multiverse.external.sun' + relocate 'net.minidev', 'org.mvplugins.multiverse.external.minidev' + relocate 'org.objectweb', 'org.mvplugins.multiverse.external.objectweb' + relocate 'io.vavr', 'org.mvplugins.multiverse.external.vavr' + relocate 'jakarta', 'org.mvplugins.multiverse.external.jakarta' + relocate 'javassist', 'org.mvplugins.multiverse.external.javassist' + relocate 'org.aopalliance', 'org.mvplugins.multiverse.external.aopalliance' + relocate 'org.glassfish', 'org.mvplugins.multiverse.external.glassfish' + relocate 'org.jvnet', 'org.mvplugins.multiverse.external.jvnet' + relocate 'org.intellij', 'org.mvplugins.multiverse.external.intellij' + relocate 'org.jetbrains', 'org.mvplugins.multiverse.external.jetbrains' + relocate 'io.papermc.lib', 'org.mvplugins.multiverse.external.paperlib' configurations = [project.configurations.shadowed] - archiveFileName = "$baseName-$version.$extension" - classifier = '' + archiveClassifier.set('') + + dependencies { + exclude(dependency { + it.moduleGroup == 'org.jetbrains.kotlin' + }) + } + //classifier = '' } build.dependsOn shadowJar jar.enabled = false + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = 'full' + } +} diff --git a/config/mv_checks.xml b/config/mv_checks.xml index 10802b971..560747b59 100644 --- a/config/mv_checks.xml +++ b/config/mv_checks.xml @@ -145,7 +145,10 @@ - + + + + @@ -158,6 +161,8 @@ + + @@ -192,7 +197,9 @@ - + + + @@ -215,18 +222,21 @@ - - - - - - - - + + + + + + + + + + @@ -297,11 +307,48 @@ - + + - + + + + + + + @@ -333,15 +380,18 @@ - + - + + + + + - @@ -360,6 +410,7 @@ RECORD_COMPONENT_DEF"/> + @@ -398,7 +449,7 @@ value="Local variable name ''{0}'' must match pattern ''{1}''."/> - + @@ -412,6 +463,7 @@ value="Method type name ''{0}'' must match pattern ''{1}''."/> + @@ -455,7 +507,13 @@ + + + + + + @@ -472,9 +530,30 @@ - + + - + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f398c33c4..5c40527d4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/co/aikar/commands/OpenBukkitCommandIssuer.java b/src/main/java/co/aikar/commands/OpenBukkitCommandIssuer.java new file mode 100644 index 000000000..3d136f3ac --- /dev/null +++ b/src/main/java/co/aikar/commands/OpenBukkitCommandIssuer.java @@ -0,0 +1,13 @@ +package co.aikar.commands; + +import org.bukkit.command.CommandSender; + +/** + * Exists just so we can extend BukkitCommandIssuer since it has a package-private constructor. + */ +public abstract class OpenBukkitCommandIssuer extends BukkitCommandIssuer { + + protected OpenBukkitCommandIssuer(BukkitCommandManager manager, CommandSender sender) { + super(manager, sender); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/MVWorld.java b/src/main/java/com/onarandombox/MultiverseCore/MVWorld.java deleted file mode 100644 index af9f66117..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/MVWorld.java +++ /dev/null @@ -1,1219 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.api.BlockSafety; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; -import com.onarandombox.MultiverseCore.configuration.SpawnLocation; -import com.onarandombox.MultiverseCore.configuration.SpawnSettings; -import com.onarandombox.MultiverseCore.configuration.WorldPropertyValidator; -import com.onarandombox.MultiverseCore.enums.AllowedPortalType; -import com.onarandombox.MultiverseCore.enums.EnglishChatColor; -import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException; -import me.main__.util.SerializationConfig.ChangeDeniedException; -import me.main__.util.SerializationConfig.NoSuchPropertyException; -import me.main__.util.SerializationConfig.VirtualProperty; -import org.bukkit.ChatColor; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.WorldType; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.serialization.SerializableAs; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.util.Vector; -import org.jetbrains.annotations.Nullable; -import org.json.simple.JSONObject; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Level; - -/** - * The implementation of a Multiverse handled world. - */ -public class MVWorld implements MultiverseWorld { - private static final int SPAWN_LOCATION_SEARCH_TOLERANCE = 16; - private static final int SPAWN_LOCATION_SEARCH_RADIUS = 16; - - private final MultiverseCore plugin; // Hold the Plugin Instance. - private final String name; // The Worlds Name, EG its folder name. - private final UUID worldUID; - private final WorldProperties props; - - public MVWorld(MultiverseCore plugin, World world, WorldProperties properties) { - this(plugin, world, properties, true); - } - - /* - * We have to use setCBWorld(), setPlugin() and initPerms() to prepare this object for use. - */ - public MVWorld(MultiverseCore plugin, World world, WorldProperties properties, boolean fixSpawn) { - this.plugin = plugin; - this.name = world.getName(); - this.worldUID = world.getUID(); - this.props = properties; - - setupProperties(); - - if (!fixSpawn) { - props.setAdjustSpawn(false); - } - - // Setup spawn separately so we can use the validator with the world spawn value.. - final SpawnLocationPropertyValidator spawnValidator = new SpawnLocationPropertyValidator(); - this.props.setValidator("spawn", spawnValidator); - this.props.spawnLocation.setWorld(world); - if (this.props.spawnLocation instanceof NullLocation) { - final SpawnLocation newLoc = new SpawnLocation(readSpawnFromWorld(world)); - this.props.spawnLocation = newLoc; - world.setSpawnLocation(newLoc.getBlockX(), newLoc.getBlockY(), newLoc.getBlockZ()); - } - - this.props.environment = world.getEnvironment(); - this.props.seed = world.getSeed(); - - this.initPerms(); - - this.props.flushChanges(); - - validateProperties(); - } - - private void setupProperties() { - this.props.setMVWorld(this); - this.props.pvp = new VirtualProperty() { - @Override - public void set(Boolean newValue) { - final World world = getCBWorld(); - if (world != null) { - world.setPVP(newValue); - } - } - - @Override - public Boolean get() { - final World world = getCBWorld(); - return world != null ? world.getPVP() : null; - } - }; - - this.props.difficulty = new VirtualProperty() { - @Override - public void set(Difficulty newValue) { - final World world = getCBWorld(); - if (world != null) { - world.setDifficulty(newValue); - } - } - - @Override - public Difficulty get() { - final World world = getCBWorld(); - return world != null ? world.getDifficulty() : null; - } - }; - - this.props.keepSpawnInMemory = new VirtualProperty() { - @Override - public void set(Boolean newValue) { - final World world = getCBWorld(); - if (world != null) { - world.setKeepSpawnInMemory(newValue); - } - } - - @Override - public Boolean get() { - final World world = getCBWorld(); - return world != null ? world.getKeepSpawnInMemory() : null; - } - }; - - this.props.spawn = new VirtualProperty() { - @Override - public void set(Location newValue) { - if (getCBWorld() != null) - getCBWorld().setSpawnLocation(newValue.getBlockX(), newValue.getBlockY(), newValue.getBlockZ()); - - props.spawnLocation = new SpawnLocation(newValue); - } - - @Override - public Location get() { - props.spawnLocation.setWorld(getCBWorld()); - // basically, everybody should accept our "SpawnLocation", right? - // so just returning it should be fine - return props.spawnLocation; - } - }; - - this.props.time = new VirtualProperty() { - @Override - public void set(Long newValue) { - final World world = getCBWorld(); - if (world != null) { - world.setTime(newValue); - } - } - - @Override - public Long get() { - final World world = getCBWorld(); - return world != null ? world.getTime() : null; - } - }; - - this.props.setValidator("scale", new ScalePropertyValidator()); - this.props.setValidator("respawnWorld", new RespawnWorldPropertyValidator()); - this.props.setValidator("allowWeather", new AllowWeatherPropertyValidator()); - this.props.setValidator("spawning", new SpawningPropertyValidator()); - this.props.setValidator("gameMode", new GameModePropertyValidator()); - - //this.props.validate(); - } - - /** - * This method is here to provide a stopgap until the add/remove/clear methods are implemented with - * SerializationConfig. - */ - public void validateEntitySpawns() { - setAllowAnimalSpawn(canAnimalsSpawn()); - setAllowMonsterSpawn(canMonstersSpawn()); - } - - private void validateProperties() { - setPVPMode(isPVPEnabled()); - setDifficulty(getDifficulty()); - setKeepSpawnInMemory(isKeepingSpawnInMemory()); - setScaling(getScaling()); - setRespawnToWorld(this.props.getRespawnToWorld()); - validateEntitySpawns(); - setGameMode(getGameMode()); - } - - /** - * Validates the scale-property. - */ - private final class ScalePropertyValidator extends WorldPropertyValidator { - @Override - public Double validateChange(String property, Double newValue, Double oldValue, - MVWorld object) throws ChangeDeniedException { - if (newValue <= 0) { - Logging.fine("Someone tried to set a scale <= 0, aborting!"); - throw new ChangeDeniedException(); - } - return super.validateChange(property, newValue, oldValue, object); - } - } - - /** - * Validates the respawnWorld-property. - */ - private final class RespawnWorldPropertyValidator extends WorldPropertyValidator { - @Override - public String validateChange(String property, String newValue, String oldValue, - MVWorld object) throws ChangeDeniedException { - if (!newValue.isEmpty() && !plugin.getMVWorldManager().isMVWorld(newValue)) - throw new ChangeDeniedException(); - return super.validateChange(property, newValue, oldValue, object); - } - } - - - - /** - * Used to apply the allowWeather-property. - */ - private final class AllowWeatherPropertyValidator extends WorldPropertyValidator { - @Override - public Boolean validateChange(String property, Boolean newValue, Boolean oldValue, - MVWorld object) throws ChangeDeniedException { - if (!newValue) { - final World world = getCBWorld(); - if (world != null) { - world.setStorm(false); - world.setThundering(false); - } - } - return super.validateChange(property, newValue, oldValue, object); - } - } - - /** - * Used to apply the spawning-property. - */ - private final class SpawningPropertyValidator extends WorldPropertyValidator { - @Override - public SpawnSettings validateChange(String property, SpawnSettings newValue, SpawnSettings oldValue, - MVWorld object) throws ChangeDeniedException { - boolean allowMonsters, allowAnimals; - if (getAnimalList().isEmpty()) { - allowAnimals = canAnimalsSpawn(); - } else { - allowAnimals = true; - } - if (getMonsterList().isEmpty()) { - allowMonsters = canMonstersSpawn(); - } else { - allowMonsters = true; - } - final World world = getCBWorld(); - if (world != null) { - if (MVWorld.this.props.getAnimalSpawnRate() != -1) { - world.setTicksPerAnimalSpawns(MVWorld.this.props.getAnimalSpawnRate()); - } - if (MVWorld.this.props.getMonsterSpawnRate() != -1) { - world.setTicksPerMonsterSpawns(MVWorld.this.props.getMonsterSpawnRate()); - } - world.setSpawnFlags(allowMonsters, allowAnimals); - } - if (MultiverseCoreConfiguration.getInstance().isAutoPurgeEnabled()) { - plugin.getMVWorldManager().getTheWorldPurger().purgeWorld(MVWorld.this); - } - return super.validateChange(property, newValue, oldValue, object); - } - } - - /** - * Used to apply the gameMode-property. - */ - private final class GameModePropertyValidator extends WorldPropertyValidator { - @Override - public GameMode validateChange(String property, GameMode newValue, GameMode oldValue, - MVWorld object) throws ChangeDeniedException { - for (Player p : plugin.getServer().getWorld(getName()).getPlayers()) { - Logging.finer(String.format("Setting %s's GameMode to %s", - p.getName(), newValue.toString())); - plugin.getPlayerListener().handleGameModeAndFlight(p, MVWorld.this); - } - return super.validateChange(property, newValue, oldValue, object); - } - } - - /** - * Validator for the spawnLocation-property. - */ - private final class SpawnLocationPropertyValidator extends WorldPropertyValidator { - @Override - public Location validateChange(String property, Location newValue, Location oldValue, - MVWorld object) throws ChangeDeniedException { - if (newValue == null) - throw new ChangeDeniedException(); - if (props.getAdjustSpawn()) { - BlockSafety bs = plugin.getBlockSafety(); - // verify that the location is safe - if (!bs.playerCanSpawnHereSafely(newValue)) { - // it's not ==> find a better one! - Logging.warning(String.format("Somebody tried to set the spawn location for '%s' to an unsafe value! Adjusting...", getAlias())); - Logging.warning("Old Location: " + plugin.getLocationManipulation().strCoordsRaw(oldValue)); - Logging.warning("New (unsafe) Location: " + plugin.getLocationManipulation().strCoordsRaw(newValue)); - SafeTTeleporter teleporter = plugin.getSafeTTeleporter(); - newValue = teleporter.getSafeLocation(newValue, SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS); - if (newValue == null) { - Logging.warning("Couldn't fix the location. I have to abort the spawn location-change :/"); - throw new ChangeDeniedException(); - } - Logging.warning("New (safe) Location: " + plugin.getLocationManipulation().strCoordsRaw(newValue)); - } - } - return super.validateChange(property, newValue, oldValue, object); - } - } - - private Permission permission; - private Permission exempt; - private Permission ignoreperm; - private Permission limitbypassperm; - - /** - * Null-location. - */ - @SerializableAs("MVNullLocation (It's a bug if you see this in your config file)") - public static final class NullLocation extends SpawnLocation { - public NullLocation() { - super(0, -1, 0); - } - - @Override - public Location clone() { - throw new UnsupportedOperationException(); - }; - - @Override - public Map serialize() { - return Collections.emptyMap(); - } - - /** - * Let Bukkit be able to deserialize this. - * @param args The map. - * @return The deserialized object. - */ - public static NullLocation deserialize(Map args) { - return new NullLocation(); - } - - @Override - public Vector toVector() { - throw new UnsupportedOperationException(); - } - - @Override - public int hashCode() { - return -1; - }; - - @Override - public String toString() { - return "NULL LOCATION"; - }; - } - - /** - * Initializes permissions. - */ - private void initPerms() { - this.permission = new Permission("multiverse.access." + this.getName(), "Allows access to " + this.getName(), PermissionDefault.OP); - // This guy is special. He shouldn't be added to any parent perms. - this.ignoreperm = new Permission("mv.bypass.gamemode." + this.getName(), - "Allows players with this permission to ignore gamemode changes.", PermissionDefault.FALSE); - - this.exempt = new Permission("multiverse.exempt." + this.getName(), - "A player who has this does not pay to enter this world, or use any MV portals in it " + this.getName(), PermissionDefault.OP); - - this.limitbypassperm = new Permission("mv.bypass.playerlimit." + this.getName(), - "A player who can enter this world regardless of wether its full", PermissionDefault.OP); - try { - this.plugin.getServer().getPluginManager().addPermission(this.permission); - this.plugin.getServer().getPluginManager().addPermission(this.exempt); - this.plugin.getServer().getPluginManager().addPermission(this.ignoreperm); - this.plugin.getServer().getPluginManager().addPermission(this.limitbypassperm); - // Add the permission and exempt to parents. - this.addToUpperLists(this.permission); - - // Add ignore to it's parent: - this.ignoreperm.addParent("mv.bypass.gamemode.*", true); - // Add limit bypass to it's parent - this.limitbypassperm.addParent("mv.bypass.playerlimit.*", true); - } catch (IllegalArgumentException e) { - Logging.finer("Permissions nodes were already added for " + this.name); - } - } - - private Location readSpawnFromWorld(World w) { - Location location = w.getSpawnLocation(); - // Set the worldspawn to our configspawn - BlockSafety bs = this.plugin.getBlockSafety(); - // Verify that location was safe - if (!bs.playerCanSpawnHereSafely(location)) { - if (!this.getAdjustSpawn()) { - Logging.fine("Spawn location from world.dat file was unsafe!!"); - Logging.fine("NOT adjusting spawn for '" + this.getAlias() + "' because you told me not to."); - Logging.fine("To turn on spawn adjustment for this world simply type:"); - Logging.fine("/mvm set adjustspawn true " + this.getAlias()); - return location; - } - // If it's not, find a better one. - SafeTTeleporter teleporter = this.plugin.getSafeTTeleporter(); - Logging.warning("Spawn location from world.dat file was unsafe. Adjusting..."); - Logging.warning("Original Location: " + plugin.getLocationManipulation().strCoordsRaw(location)); - Location newSpawn = teleporter.getSafeLocation(location, - SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS); - // I think we could also do this, as I think this is what Notch does. - // Not sure how it will work in the nether... - //Location newSpawn = this.spawnLocation.getWorld().getHighestBlockAt(this.spawnLocation).getLocation(); - if (newSpawn != null) { - Logging.info("New Spawn for '%s' is located at: %s", - this.getName(), plugin.getLocationManipulation().locationToString(newSpawn)); - return newSpawn; - } else { - // If it's a standard end world, let's check in a better place: - Location newerSpawn; - newerSpawn = bs.getTopBlock(new Location(w, 0, 0, 0)); - if (newerSpawn != null) { - Logging.info("New Spawn for '%s' is located at: %s", - this.getName(), plugin.getLocationManipulation().locationToString(newerSpawn)); - return newerSpawn; - } else { - Logging.severe("Safe spawn NOT found!!!"); - } - } - } - return location; - } - - private void addToUpperLists(Permission perm) { - Permission all = this.plugin.getServer().getPluginManager().getPermission("multiverse.*"); - Permission allWorlds = this.plugin.getServer().getPluginManager().getPermission("multiverse.access.*"); - Permission allExemption = this.plugin.getServer().getPluginManager().getPermission("multiverse.exempt.*"); - - if (allWorlds == null) { - allWorlds = new Permission("multiverse.access.*"); - this.plugin.getServer().getPluginManager().addPermission(allWorlds); - } - allWorlds.getChildren().put(perm.getName(), true); - if (allExemption == null) { - allExemption = new Permission("multiverse.exempt.*"); - this.plugin.getServer().getPluginManager().addPermission(allExemption); - } - allExemption.getChildren().put(this.exempt.getName(), true); - if (all == null) { - all = new Permission("multiverse.*"); - this.plugin.getServer().getPluginManager().addPermission(all); - } - all.getChildren().put("multiverse.access.*", true); - all.getChildren().put("multiverse.exempt.*", true); - - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(all); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allWorlds); - } - - /** - * Copies all properties from another {@link MVWorld} object. - * @param other The other world object. - */ - public void copyValues(MVWorld other) { - props.copyValues(other.props); - } - - /** - * Copies all properties from a {@link WorldProperties} object. - * @param other The world properties object. - */ - public void copyValues(WorldProperties other) { - props.copyValues(other); - } - - /** - * {@inheritDoc} - */ - @Override - public World getCBWorld() { - final World world = plugin.getServer().getWorld(worldUID); - if (world == null) { - throw new IllegalStateException("Lost reference to bukkit world '" + name + "'"); - } - return world; - } - - /** - * {@inheritDoc} - */ - @Override - public String getColoredWorldString() { - if (props.getAlias().length() == 0) { - props.setAlias(this.getName()); - } - - if ((props.getColor() == null) || (props.getColor().getColor() == null)) { - this.props.setColor(EnglishChatColor.WHITE); - } - - StringBuilder nameBuilder = new StringBuilder().append(props.getColor().getColor()); - if (props.getStyle().getColor() != null) - nameBuilder.append(props.getStyle().getColor()); - nameBuilder.append(props.getAlias()).append(ChatColor.WHITE).toString(); - - return nameBuilder.toString(); - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean clearList(String property) { - return clearVariable(property); - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean clearVariable(String property) { - List list = getOldAndEvilList(property); - if (list == null) - return false; - list.clear(); - validateEntitySpawns(); - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean addToVariable(String property, String value) { - List list = getOldAndEvilList(property); - if (list == null) - return false; - list.add(value); - validateEntitySpawns(); - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean removeFromVariable(String property, String value) { - List list = getOldAndEvilList(property); - if (list == null) - return false; - list.remove(value); - validateEntitySpawns(); - return true; - } - - /** - * @deprecated This is deprecated. - */ - @Deprecated - private List getOldAndEvilList(String property) { - if (property.equalsIgnoreCase("worldblacklist")) - return this.props.getWorldBlacklist(); - else if (property.equalsIgnoreCase("animals")) - return this.props.getAnimalList(); - else if (property.equalsIgnoreCase("monsters")) - return this.props.getMonsterList(); - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPropertyValue(String property) throws PropertyDoesNotExistException { - try { - return this.props.getProperty(property, true); - } catch (NoSuchPropertyException e) { - throw new PropertyDoesNotExistException(property, e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setPropertyValue(String property, String value) throws PropertyDoesNotExistException { - try { - return this.props.setProperty(property, value, true); - } catch (NoSuchPropertyException e) { - throw new PropertyDoesNotExistException(property, e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public String getPropertyHelp(String property) throws PropertyDoesNotExistException { - try { - return this.props.getPropertyDescription(property, true); - } catch (NoSuchPropertyException e) { - throw new PropertyDoesNotExistException(property, e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public WorldType getWorldType() { - // This variable is not settable in-game, therefore does not get a property. - final World world = getCBWorld(); - return world != null ? world.getWorldType() : null; - } - - /** - * {@inheritDoc} - */ - @Override - public Environment getEnvironment() { - return this.props.getEnvironment(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setEnvironment(Environment environment) { - this.props.setEnvironment(environment); - } - - /** - * {@inheritDoc} - */ - @Override - public long getSeed() { - return this.props.getSeed(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setSeed(long seed) { - this.props.setSeed(seed); - } - - /** - * {@inheritDoc} - */ - @Override - public String getGenerator() { - return this.props.getGenerator(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setGenerator(String generator) { - this.props.setGenerator(generator); - } - - /** - * {@inheritDoc} - */ - @Override - public int getPlayerLimit() { - return this.props.getPlayerLimit(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setPlayerLimit(int limit) { - this.props.setPlayerLimit(limit); - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - // This variable is not settable in-game, therefore does not get a property. - return this.name; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPermissibleName() { - return this.name.toLowerCase(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getAlias() { - if (this.props.getAlias() == null || this.props.getAlias().length() == 0) { - return this.name; - } - return this.props.getAlias(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAlias(String alias) { - this.props.setAlias(alias); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canAnimalsSpawn() { - return this.props.canAnimalsSpawn(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAllowAnimalSpawn(boolean animals) { - this.props.setAllowAnimalSpawn(animals); - } - - /** - * {@inheritDoc} - */ - @Override - public List getAnimalList() { - // These don't fire events at the moment. Should they? - return this.props.getAnimalList(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canMonstersSpawn() { - return this.props.canMonstersSpawn(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAllowMonsterSpawn(boolean monsters) { - this.props.setAllowMonsterSpawn(monsters); - } - - /** - * {@inheritDoc} - */ - @Override - public List getMonsterList() { - // These don't fire events at the moment. Should they? - return this.props.getMonsterList(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPVPEnabled() { - return this.props.isPVPEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setPVPMode(boolean pvp) { - this.props.setPVPMode(pvp); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isHidden() { - return this.props.isHidden(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setHidden(boolean hidden) { - this.props.setHidden(hidden); - } - - /** - * {@inheritDoc} - */ - @Override - public List getWorldBlacklist() { - return this.props.getWorldBlacklist(); - } - - /** - * {@inheritDoc} - */ - @Override - public double getScaling() { - return this.props.getScaling(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setScaling(double scaling) { - return this.props.setScaling(scaling); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setColor(String aliasColor) { - return props.setColor(aliasColor); - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean isValidAliasColor(String aliasColor) { - return (EnglishChatColor.fromString(aliasColor) != null); - } - - /** - * {@inheritDoc} - */ - @Override - public ChatColor getColor() { - return this.props.getColor().getColor(); - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean getFakePVP() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public World getRespawnToWorld() { - return this.plugin.getServer().getWorld(props.getRespawnToWorld()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setRespawnToWorld(String respawnToWorld) { - if (!this.plugin.getMVWorldManager().isMVWorld(respawnToWorld)) return false; - return this.props.setRespawnToWorld(respawnToWorld); - } - - /** - * {@inheritDoc} - */ - @Override - public Permission getAccessPermission() { - return this.permission; - } - - /** - * {@inheritDoc} - */ - @Override - public Material getCurrency() { - return this.props.getCurrency(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setCurrency(@Nullable Material currency) { - this.props.setCurrency(currency); - } - - /** - * {@inheritDoc} - */ - @Override - public double getPrice() { - return this.props.getPrice(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setPrice(double price) { - this.props.setPrice(price); - } - - /** - * {@inheritDoc} - */ - @Override - public Permission getExemptPermission() { - return this.exempt; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setGameMode(String mode) { - return this.props.setGameMode(mode); - } - - @Override - public boolean setGameMode(GameMode mode) { - return this.props.setGameMode(mode); - } - - /** - * {@inheritDoc} - */ - @Override - public GameMode getGameMode() { - return this.props.getGameMode(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setEnableWeather(boolean weather) { - this.props.setEnableWeather(weather); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isWeatherEnabled() { - return this.props.isWeatherEnabled(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isKeepingSpawnInMemory() { - return this.props.isKeepingSpawnInMemory(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setKeepSpawnInMemory(boolean value) { - this.props.setKeepSpawnInMemory(value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getHunger() { - return this.props.getHunger(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setHunger(boolean hunger) { - this.props.setHunger(hunger); - } - - /** - * {@inheritDoc} - */ - @Override - public Location getSpawnLocation() { - return this.props.getSpawnLocation(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setSpawnLocation(Location l) { - this.props.setSpawnLocation(l); - } - - /** - * {@inheritDoc} - */ - @Override - public Difficulty getDifficulty() { - return this.props.getDifficulty(); - } - - /** - * {@inheritDoc} - * - * @deprecated This is deprecated. - */ - @Override - @Deprecated - public boolean setDifficulty(String difficulty) { - return this.props.setDifficulty(difficulty); - } - - @Override - public boolean setDifficulty(Difficulty difficulty) { - return this.props.setDifficulty(difficulty); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getAutoHeal() { - return this.props.getAutoHeal(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAutoHeal(boolean heal) { - this.props.setAutoHeal(heal); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAdjustSpawn(boolean adjust) { - this.props.setAdjustSpawn(adjust); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getAdjustSpawn() { - return this.props.getAdjustSpawn(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAutoLoad(boolean load) { - this.props.setAutoLoad(load); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getAutoLoad() { - return this.props.getAutoLoad(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setBedRespawn(boolean respawn) { - this.props.setBedRespawn(respawn); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getBedRespawn() { - return this.props.getBedRespawn(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getAllPropertyNames() { - return this.props.getAllPropertyNames(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getTime() { - return this.props.getTime(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setTime(String timeAsString) { - return this.props.setTime(timeAsString); - } - - /** - * {@inheritDoc} - */ - @Override - public AllowedPortalType getAllowedPortals() { - return props.getAllowedPortals(); - } - - /** - * {@inheritDoc} - */ - @Override - public void allowPortalMaking(AllowedPortalType portalType) { - this.props.allowPortalMaking(portalType); - } - - /** - * {@inheritDoc} - */ - @Override - public ChatColor getStyle() { - return this.props.getStyle().getColor(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setStyle(String style) { - return this.props.setStyle(style); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getAllowFlight() { - return this.props.getAllowFlight(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setAllowFlight(final boolean allowFlight) { - this.props.setAllowFlight(allowFlight); - } - - @Override - public String toString() { - final JSONObject jsonData = new JSONObject(); - jsonData.put("Name", getName()); - jsonData.put("Env", getEnvironment().toString()); - jsonData.put("Type", getWorldType().toString()); - jsonData.put("Gen", getGenerator()); - final JSONObject topLevel = new JSONObject(); - topLevel.put(getClass().getSimpleName() + "@" + hashCode(), jsonData); - return topLevel.toString(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java deleted file mode 100644 index 6fda9ae95..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java +++ /dev/null @@ -1,1238 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; - -import buscript.Buscript; -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MVWorld.NullLocation; -import com.onarandombox.MultiverseCore.api.BlockSafety; -import com.onarandombox.MultiverseCore.api.Core; -import com.onarandombox.MultiverseCore.api.LocationManipulation; -import com.onarandombox.MultiverseCore.api.MVPlugin; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseCoreConfig; -import com.onarandombox.MultiverseCore.api.MultiverseMessaging; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; -import com.onarandombox.MultiverseCore.commands.AnchorCommand; -import com.onarandombox.MultiverseCore.commands.CheckCommand; -import com.onarandombox.MultiverseCore.commands.CloneCommand; -import com.onarandombox.MultiverseCore.commands.ConfigCommand; -import com.onarandombox.MultiverseCore.commands.ConfirmCommand; -import com.onarandombox.MultiverseCore.commands.CoordCommand; -import com.onarandombox.MultiverseCore.commands.CreateCommand; -import com.onarandombox.MultiverseCore.commands.DebugCommand; -import com.onarandombox.MultiverseCore.commands.DeleteCommand; -import com.onarandombox.MultiverseCore.commands.EnvironmentCommand; -import com.onarandombox.MultiverseCore.commands.GameruleCommand; -import com.onarandombox.MultiverseCore.commands.GamerulesCommand; -import com.onarandombox.MultiverseCore.commands.GeneratorCommand; -import com.onarandombox.MultiverseCore.commands.HelpCommand; -import com.onarandombox.MultiverseCore.commands.ImportCommand; -import com.onarandombox.MultiverseCore.commands.InfoCommand; -import com.onarandombox.MultiverseCore.commands.ListCommand; -import com.onarandombox.MultiverseCore.commands.LoadCommand; -import com.onarandombox.MultiverseCore.commands.ModifyAddCommand; -import com.onarandombox.MultiverseCore.commands.ModifyClearCommand; -import com.onarandombox.MultiverseCore.commands.ModifyCommand; -import com.onarandombox.MultiverseCore.commands.ModifyRemoveCommand; -import com.onarandombox.MultiverseCore.commands.ModifySetCommand; -import com.onarandombox.MultiverseCore.commands.PurgeCommand; -import com.onarandombox.MultiverseCore.commands.RegenCommand; -import com.onarandombox.MultiverseCore.commands.ReloadCommand; -import com.onarandombox.MultiverseCore.commands.RemoveCommand; -import com.onarandombox.MultiverseCore.commands.ScriptCommand; -import com.onarandombox.MultiverseCore.commands.SetSpawnCommand; -import com.onarandombox.MultiverseCore.commands.SilentCommand; -import com.onarandombox.MultiverseCore.commands.SpawnCommand; -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; -import com.onarandombox.MultiverseCore.destination.DestinationFactory; -import com.onarandombox.MultiverseCore.destination.ExactDestination; -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.listeners.MVAsyncPlayerChatListener; -import com.onarandombox.MultiverseCore.listeners.MVChatListener; -import com.onarandombox.MultiverseCore.listeners.MVEntityListener; -import com.onarandombox.MultiverseCore.listeners.MVMapListener; -import com.onarandombox.MultiverseCore.listeners.MVPlayerChatListener; -import com.onarandombox.MultiverseCore.listeners.MVPlayerListener; -import com.onarandombox.MultiverseCore.listeners.MVPortalListener; -import com.onarandombox.MultiverseCore.listeners.MVWeatherListener; -import com.onarandombox.MultiverseCore.listeners.MVWorldInitListener; -import com.onarandombox.MultiverseCore.listeners.MVWorldListener; -import com.onarandombox.MultiverseCore.utils.AnchorManager; -import com.onarandombox.MultiverseCore.utils.CompatibilityLayer; -import com.onarandombox.MultiverseCore.utils.MVEconomist; -import com.onarandombox.MultiverseCore.utils.MVMessaging; -import com.onarandombox.MultiverseCore.utils.MVPermissions; -import com.onarandombox.MultiverseCore.utils.MVPlayerSession; -import com.onarandombox.MultiverseCore.utils.MaterialConverter; -import com.onarandombox.MultiverseCore.utils.TestingMode; -import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator; -import com.onarandombox.MultiverseCore.utils.SimpleBlockSafety; -import com.onarandombox.MultiverseCore.utils.SimpleLocationManipulation; -import com.onarandombox.MultiverseCore.utils.SimpleSafeTTeleporter; -import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper; -import com.onarandombox.MultiverseCore.utils.VaultHandler; -import com.onarandombox.MultiverseCore.utils.WorldManager; -import com.pneumaticraft.commandhandler.CommandHandler; -import me.main__.util.SerializationConfig.NoSuchPropertyException; -import me.main__.util.SerializationConfig.SerializationConfig; -import org.bukkit.ChatColor; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.plugin.java.JavaPluginLoader; - -/** - * The implementation of the Multiverse-{@link Core}. - */ -public class MultiverseCore extends JavaPlugin implements MVPlugin, Core { - private static final int PROTOCOL = 24; - // TODO: Investigate if this one is really needed to be static. - // Doubt it. -- FernFerret - private static Map teleportQueue = new HashMap(); - - private AnchorManager anchorManager = new AnchorManager(this); - // TODO please let's make this non-static - private volatile MultiverseCoreConfiguration config; - - public MultiverseCore() { - super(); - } - - /** - * This is for unit testing. - * @param loader The PluginLoader to use. - * @param description The Description file to use. - * @param dataFolder The folder that other datafiles can be found in. - * @param file The location of the plugin. - */ - public MultiverseCore(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { - super(loader, description, dataFolder, file); - } - - /** - * This method is used to find out who is teleporting a player. - * @param playerName The teleported player (the teleportee). - * @return The player that teleported the other one (the teleporter). - */ - public static String getPlayerTeleporter(String playerName) { - if (teleportQueue.containsKey(playerName)) { - String teleportee = teleportQueue.get(playerName); - teleportQueue.remove(playerName); - return teleportee; - } - return null; - } - - /** - * This method is used to add a teleportation to the teleportQueue. - * - * @param teleporter The name of the player that initiated the teleportation. - * @param teleportee The name of the player that was teleported. - */ - public static void addPlayerToTeleportQueue(String teleporter, String teleportee) { - Logging.finest("Adding mapping '%s' => '%s' to teleport queue", teleporter, teleportee); - teleportQueue.put(teleportee, teleporter); - } - - /** - * {@inheritDoc} - * @deprecated This is now deprecated, nobody needs it any longer. - * All version info-dumping is now done with {@link MVVersionEvent}. - */ - @Override - @Deprecated - public String dumpVersionInfo(String buffer) { - return buffer; - } - - @Override - public MultiverseCore getCore() { - return this; - } - - @Override - public void setCore(MultiverseCore core) { - // This method is required by the interface (so core is effectively a plugin of itself) and therefore - // this is never used. - } - - @Override - public int getProtocolVersion() { - return MultiverseCore.PROTOCOL; - } - - // Setup our Map for our Commands using the CommandHandler. - private CommandHandler commandHandler; - private CommandQueueManager commandQueueManager; - - private static final String LOG_TAG = "[Multiverse-Core]"; - - // Multiverse Permissions Handler - private MVPermissions ph; - - // Configurations - private FileConfiguration multiverseConfig = null; - - private final MVWorldManager worldManager = new WorldManager(this); - - // Setup the block/player/entity listener. - private final MVPlayerListener playerListener = new MVPlayerListener(this); - private final MVEntityListener entityListener = new MVEntityListener(this); - private final MVWeatherListener weatherListener = new MVWeatherListener(this); - private final MVPortalListener portalListener = new MVPortalListener(this); - private final MVWorldListener worldListener = new MVWorldListener(this); - private MVChatListener chatListener; - - // HashMap to contain information relating to the Players. - private HashMap playerSessions; - private MVEconomist economist; - private Buscript buscript; - private int pluginCount; - private DestinationFactory destFactory; - private MultiverseMessaging messaging; - private BlockSafety blockSafety; - private LocationManipulation locationManipulation; - private SafeTTeleporter safeTTeleporter; - private UnsafeCallWrapper unsafeCallWrapper; - - private File serverFolder = new File(System.getProperty("user.dir")); - - @Override - public void onLoad() { - // Register our config - SerializationConfig.registerAll(MultiverseCoreConfiguration.class); - // Register our world - SerializationConfig.registerAll(WorldProperties.class); - // Create our DataFolder - getDataFolder().mkdirs(); - // Setup our Debug Log - Logging.init(this); - SerializationConfig.initLogging(Logging.getLogger()); - // Setup our BlockSafety - this.blockSafety = new SimpleBlockSafety(this); - // Setup our LocationManipulation - this.locationManipulation = new SimpleLocationManipulation(); - // Setup our SafeTTeleporter - this.safeTTeleporter = new SimpleSafeTTeleporter(this); - this.unsafeCallWrapper = new UnsafeCallWrapper(this); - // Setup our CompatibilityLayer - CompatibilityLayer.init(); - } - - - @Override - @Deprecated - public VaultHandler getVaultHandler() { - return getEconomist().getVaultHandler(); - } - - public MVEconomist getEconomist() { - return economist; - } - - /** - * {@inheritDoc} - */ - @Override - public void onEnable() { - getServer().getPluginManager().registerEvents(new MVWorldInitListener(this), this); - - this.messaging = new MVMessaging(); - this.economist = new MVEconomist(this); - // Load the defaultWorldGenerators - this.worldManager.getDefaultWorldGenerators(); - - this.registerEvents(); - // Setup Permissions, we'll do an initial check for the Permissions plugin then fall back on isOP(). - this.ph = new MVPermissions(this); - - // 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(); - - // Initialize the Destination factor AFTER the commands - this.initializeDestinationFactory(); - - this.playerSessions = new HashMap(); - - // Start the Update Checker - // updateCheck = new UpdateChecker(this.getDescription().getName(), this.getDescription().getVersion()); - - // Call the Function to load all the Worlds and setup the HashMap - // When called with null, it tries to load ALL - // this function will be called every time a plugin registers a new envtype with MV - // Setup & Load our Configuration files. - loadConfigs(); - if (this.multiverseConfig != null) { - Logging.setShowingConfig(!getMVConfig().getSilentStart()); - this.worldManager.loadDefaultWorlds(); - this.worldManager.loadWorlds(true); - } else { - Logging.severe("Your configs were not loaded. Very little will function in Multiverse."); - } - this.anchorManager.loadAnchors(); - - // Now set the firstspawnworld (after the worlds are loaded): - this.worldManager.setFirstSpawnWorld(getMVConfig().getFirstSpawnWorld()); - try { - getMVConfig().setFirstSpawnWorld(this.worldManager.getFirstSpawnWorld().getName()); - } catch (NullPointerException e) { - // A test that had no worlds loaded was being run. This should never happen in production - } - this.saveMVConfig(); - // Register async or sync player chat according to config - try { - Class.forName("org.bukkit.event.player.AsyncPlayerChatEvent"); - } catch (ClassNotFoundException e) { - getMVConfig().setUseAsyncChat(false); - } - if (getMVConfig().getUseAsyncChat()) { - this.chatListener = new MVAsyncPlayerChatListener(this, this.playerListener); - } else { - this.chatListener = new MVPlayerChatListener(this, this.playerListener); - } - getServer().getPluginManager().registerEvents(this.chatListener, this); - - this.initializeBuscript(); - this.setupMetrics(); - - // Output a little snippet to show it's enabled. - Logging.config("Version %s (API v%s) Enabled - By %s", this.getDescription().getVersion(), PROTOCOL, getAuthors()); - - if (getMVConfig().isShowingDonateMessage()) { - getLogger().config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman"); - getLogger().config("One time donations are also appreciated: https://www.paypal.me/dumptruckman"); - } - } - - private void setupMetrics() { - if (TestingMode.isDisabled()) { - MetricsConfigurator.configureMetrics(this); - } - } - - /** - * Initializes the buscript javascript library. - */ - private void initializeBuscript() { - buscript = null; - - if (this.getMVConfig().getEnableBuscript()) { - try { - buscript = new Buscript(this); - // 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! " + - "If you would like not to see this message, " + - "use `/mv conf enablebuscript false` to disable Buscript from loading."); - } - } - } - - private void initializeDestinationFactory() { - this.destFactory = new DestinationFactory(this); - this.destFactory.registerDestinationType(WorldDestination.class, ""); - this.destFactory.registerDestinationType(WorldDestination.class, "w"); - this.destFactory.registerDestinationType(ExactDestination.class, "e"); - this.destFactory.registerDestinationType(PlayerDestination.class, "pl"); - this.destFactory.registerDestinationType(CannonDestination.class, "ca"); - this.destFactory.registerDestinationType(BedDestination.class, "b"); - this.destFactory.registerDestinationType(AnchorDestination.class, "a"); - } - - /** - * Function to Register all the Events needed. - */ - private void registerEvents() { - PluginManager pm = getServer().getPluginManager(); - pm.registerEvents(this.playerListener, this); - pm.registerEvents(this.entityListener, this); - pm.registerEvents(this.weatherListener, this); - pm.registerEvents(this.portalListener, this); - Logging.info(ChatColor.GREEN + "We are aware of the warning about the deprecated event. There is no alternative that allows us to do what we need to do and performance impact is negligible. It is safe to ignore."); - pm.registerEvents(this.worldListener, this); - pm.registerEvents(new MVMapListener(this), this); - } - - /** - * {@inheritDoc} - */ - @Override - public void loadConfigs() { - // Now grab the Configuration Files. - this.multiverseConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "config.yml")); - InputStream resourceURL = this.getClass().getResourceAsStream("/defaults/config.yml"); - - // Read in our default config with UTF-8 now - Configuration coreDefaults; - try { - coreDefaults = YamlConfiguration.loadConfiguration(new BufferedReader(new InputStreamReader(resourceURL, "UTF-8"))); - this.multiverseConfig.setDefaults(coreDefaults); - } catch (UnsupportedEncodingException e) { - Logging.severe("Couldn't load default config with UTF-8 encoding. Details follow:"); - e.printStackTrace(); - Logging.severe("Default configs NOT loaded."); - } - - this.multiverseConfig.options().copyDefaults(false); - this.multiverseConfig.options().copyHeader(true); - - MultiverseCoreConfiguration wantedConfig = null; - try { - wantedConfig = (MultiverseCoreConfiguration) multiverseConfig.get("multiverse-configuration"); - } catch (Exception ignore) { - } finally { - config = ((wantedConfig == null) ? new MultiverseCoreConfiguration() : wantedConfig); - } - this.migrateWorldConfig(); - this.worldManager.loadWorldConfig(new File(getDataFolder(), "worlds.yml")); - - this.messaging.setCooldown(getMVConfig().getMessageCooldown()); - - // Remove old values. - this.multiverseConfig.set("enforcegamemodes", null); - this.multiverseConfig.set("bedrespawn", null); - this.multiverseConfig.set("opfallback", null); - - // Old Config Format - this.migrate22Values(); - this.saveMVConfigs(); - - int level = Logging.getDebugLevel(); - Logging.setDebugLevel(getMVConfig().getGlobalDebug()); - if (level != Logging.getDebugLevel()) { - getServer().getPluginManager().callEvent(new MVDebugModeEvent(level)); - } - } - - /** - * Thes are the MV config 2.0-2.2 values, - * they should be migrated to the new format. - */ - private void migrate22Values() { - if (this.multiverseConfig.isSet("worldnameprefix")) { - Logging.config("Migrating 'worldnameprefix'..."); - this.getMVConfig().setPrefixChat(this.multiverseConfig.getBoolean("worldnameprefix")); - this.multiverseConfig.set("worldnameprefix", null); - } - if (this.multiverseConfig.isSet("firstspawnworld")) { - Logging.config("Migrating 'firstspawnworld'..."); - this.getMVConfig().setFirstSpawnWorld(this.multiverseConfig.getString("firstspawnworld")); - this.multiverseConfig.set("firstspawnworld", null); - } - if (this.multiverseConfig.isSet("enforceaccess")) { - Logging.config("Migrating 'enforceaccess'..."); - this.getMVConfig().setEnforceAccess(this.multiverseConfig.getBoolean("enforceaccess")); - this.multiverseConfig.set("enforceaccess", null); - } - if (this.multiverseConfig.isSet("displaypermerrors")) { - Logging.config("Migrating 'displaypermerrors'..."); - this.getMVConfig().setDisplayPermErrors(this.multiverseConfig.getBoolean("displaypermerrors")); - this.multiverseConfig.set("displaypermerrors", null); - } - if (this.multiverseConfig.isSet("teleportintercept")) { - Logging.config("Migrating 'teleportintercept'..."); - this.getMVConfig().setTeleportIntercept(this.multiverseConfig.getBoolean("teleportintercept")); - this.multiverseConfig.set("teleportintercept", null); - } - if (this.multiverseConfig.isSet("firstspawnoverride")) { - Logging.config("Migrating 'firstspawnoverride'..."); - this.getMVConfig().setFirstSpawnOverride(this.multiverseConfig.getBoolean("firstspawnoverride")); - this.multiverseConfig.set("firstspawnoverride", null); - } - if (this.multiverseConfig.isSet("messagecooldown")) { - Logging.config("Migrating 'messagecooldown'..."); - this.getMVConfig().setMessageCooldown(this.multiverseConfig.getInt("messagecooldown")); - this.multiverseConfig.set("messagecooldown", null); - } - if (this.multiverseConfig.isSet("debug")) { - Logging.config("Migrating 'debug'..."); - this.getMVConfig().setGlobalDebug(this.multiverseConfig.getInt("debug")); - this.multiverseConfig.set("debug", null); - } - if (this.multiverseConfig.isSet("version")) { - Logging.config("Migrating 'version'..."); - this.multiverseConfig.set("version", null); - } - } - - private static final char PATH_SEPARATOR = '\uF8FF'; - - /** - * Migrate the worlds.yml to SerializationConfig. - */ - private void migrateWorldConfig() { // SUPPRESS CHECKSTYLE: MethodLength - FileConfiguration wconf = new YamlConfiguration(); - wconf.options().pathSeparator(PATH_SEPARATOR); - File worldsFile = new File(getDataFolder(), "worlds.yml"); - try { - wconf.load(worldsFile); - } catch (IOException e) { - Logging.warning("Cannot load worlds.yml"); - } catch (InvalidConfigurationException e) { - Logging.warning("Your worlds.yml is invalid!"); - } - - if (!wconf.isConfigurationSection("worlds")) { // empty config - Logging.fine("No worlds to migrate!"); - return; - } - - Map values = wconf.getConfigurationSection("worlds").getValues(false); - - boolean wasChanged = false; - Map newValues = new LinkedHashMap(values.size()); - for (Map.Entry entry : values.entrySet()) { - if (entry.getValue() instanceof WorldProperties) { - // fine - newValues.put(entry.getKey(), entry.getValue()); - } else if (entry.getValue() instanceof ConfigurationSection) { - Logging.fine("Migrating: " + entry.getKey()); - // we have to migrate this - WorldProperties world = new WorldProperties(Collections.emptyMap()); - ConfigurationSection section = (ConfigurationSection) entry.getValue(); - - // migrate animals and monsters - if (section.isConfigurationSection("animals")) { - ConfigurationSection animalSection = section.getConfigurationSection("animals"); - if (animalSection.contains("spawn")) { - if (animalSection.isBoolean("spawn")) - world.setAllowAnimalSpawn(animalSection.getBoolean("spawn")); - else - world.setAllowAnimalSpawn(Boolean.parseBoolean(animalSection.getString("spawn"))); - } - if (animalSection.isList("exceptions")) { - world.getAnimalList().clear(); - world.getAnimalList().addAll(animalSection.getStringList("exceptions")); - } - } - if (section.isConfigurationSection("monsters")) { - ConfigurationSection monsterSection = section.getConfigurationSection("monsters"); - if (monsterSection.contains("spawn")) { - if (monsterSection.isBoolean("spawn")) - world.setAllowMonsterSpawn(monsterSection.getBoolean("spawn")); - else - world.setAllowMonsterSpawn(Boolean.parseBoolean(monsterSection.getString("spawn"))); - } - if (monsterSection.isList("exceptions")) { - world.getMonsterList().clear(); - world.getMonsterList().addAll(monsterSection.getStringList("exceptions")); - } - } - - // migrate entryfee - if (section.isConfigurationSection("entryfee")) { - ConfigurationSection feeSection = section.getConfigurationSection("entryfee"); - if (feeSection.isInt("currency")) { - world.setCurrency(MaterialConverter.convertConfigType(feeSection, "currency")); - } - - if (feeSection.isDouble("amount")) - world.setPrice(feeSection.getDouble("amount")); - else if (feeSection.isInt("amount")) - world.setPrice(feeSection.getInt("amount")); - } - - // migrate pvp - if (section.isBoolean("pvp")) { - world.setPVPMode(section.getBoolean("pvp")); - } - - // migrate alias - if (section.isConfigurationSection("alias")) { - ConfigurationSection aliasSection = section.getConfigurationSection("alias"); - if (aliasSection.isString("color")) - world.setColor(aliasSection.getString("color")); - if (aliasSection.isString("name")) - world.setAlias(aliasSection.getString("name")); - } - - // migrate worldblacklist - if (section.isList("worldblacklist")) { - world.getWorldBlacklist().clear(); - world.getWorldBlacklist().addAll(section.getStringList("worldblacklist")); - } - - // migrate scale - if (section.isDouble("scale")) { - world.setScaling(section.getDouble("scale")); - } - - // migrate gamemode - if (section.isString("gamemode")) { - final GameMode gameMode = GameMode.valueOf(section.getString("gamemode").toUpperCase()); - if (gameMode != null) { - world.setGameMode(gameMode); - } - } - - // migrate hunger - if (section.isBoolean("hunger")) { - world.setHunger(section.getBoolean("hunger")); - } - - // migrate hidden - if (section.isBoolean("hidden")) { - world.setHidden(section.getBoolean("hidden")); - } - - // migrate autoheal - if (section.isBoolean("autoheal")) { - world.setAutoHeal(section.getBoolean("autoheal")); - } - - // migrate portalform - if (section.isString("portalform")) { - try { - world.setProperty("portalform", section.getString("portalform"), true); - } catch (NoSuchPropertyException e) { - throw new RuntimeException("Who forgot to update the migrator?", e); - } - } - - // migrate environment - if (section.isString("environment")) { - try { - world.setProperty("environment", section.getString("environment"), true); - } catch (NoSuchPropertyException e) { - throw new RuntimeException("Who forgot to update the migrator?", e); - } - } - - // migrate generator - if (section.isString("generator")) { - world.setGenerator(section.getString("generator")); - } - - // migrate seed - if (section.isLong("seed")) { - world.setSeed(section.getLong("seed")); - } - - // migrate weather - if (section.isBoolean("allowweather")) { - world.setEnableWeather(section.getBoolean("allowweather")); - } - - // migrate adjustspawn - if (section.isBoolean("adjustspawn")) { - world.setAdjustSpawn(section.getBoolean("adjustspawn")); - } - - // migrate autoload - if (section.isBoolean("autoload")) { - world.setAutoLoad(section.getBoolean("autoload")); - } - - // migrate bedrespawn - if (section.isBoolean("bedrespawn")) { - world.setBedRespawn(section.getBoolean("bedrespawn")); - } - - // migrate spawn - if (section.isConfigurationSection("spawn")) { - ConfigurationSection spawnSect = section.getConfigurationSection("spawn"); - Location spawnLoc = new NullLocation(); - if (spawnSect.isDouble("yaw")) - spawnLoc.setYaw((float) spawnSect.getDouble("yaw")); - if (spawnSect.isDouble("pitch")) - spawnLoc.setPitch((float) spawnSect.getDouble("pitch")); - if (spawnSect.isDouble("x")) - spawnLoc.setX(spawnSect.getDouble("x")); - if (spawnSect.isDouble("y")) - spawnLoc.setY(spawnSect.getDouble("y")); - if (spawnSect.isDouble("z")) - spawnLoc.setZ(spawnSect.getDouble("z")); - - world.setSpawnLocation(spawnLoc); - } - - // migrate difficulty - if (section.isString("difficulty")) { - Difficulty difficulty; - try { - difficulty = Difficulty.valueOf(section.getString("difficulty").toUpperCase()); - } catch (IllegalArgumentException e) { - Logging.warning("Could not parse difficulty: " + section.getString("difficulty")); - Logging.warning("Setting world " + entry.getKey() + " difficulty to NORMAL"); - difficulty = Difficulty.NORMAL; - } - if (difficulty != null) { - world.setDifficulty(difficulty); - } - } - - // migrate keepspawninmemory - if (section.isBoolean("keepspawninmemory")) { - world.setKeepSpawnInMemory(section.getBoolean("keepspawninmemory")); - } - - newValues.put(entry.getKey(), world); - wasChanged = true; - } else { - // huh? - Logging.warning("Removing unknown entry in the config: " + entry); - // just don't add to newValues - wasChanged = true; - } - } - - if (wasChanged) { - // clear config - wconf.set("worlds", null); - - // and rebuild it - ConfigurationSection rootSection = wconf.createSection("worlds"); - for (Map.Entry entry : newValues.entrySet()) { - rootSection.set(entry.getKey(), entry.getValue()); - } - - try { - wconf.save(new File(getDataFolder(), "worlds.yml")); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public MultiverseMessaging getMessaging() { - return this.messaging; - } - - /** - * Register Multiverse-Core commands to Command Manager. - */ - private void registerCommands() { - // Intro Commands - this.commandHandler.registerCommand(new HelpCommand(this)); - this.commandHandler.registerCommand(new VersionCommand(this)); - this.commandHandler.registerCommand(new ListCommand(this)); - this.commandHandler.registerCommand(new InfoCommand(this)); - this.commandHandler.registerCommand(new CreateCommand(this)); - this.commandHandler.registerCommand(new CloneCommand(this)); - this.commandHandler.registerCommand(new ImportCommand(this)); - this.commandHandler.registerCommand(new ReloadCommand(this)); - this.commandHandler.registerCommand(new SetSpawnCommand(this)); - this.commandHandler.registerCommand(new CoordCommand(this)); - this.commandHandler.registerCommand(new TeleportCommand(this)); - this.commandHandler.registerCommand(new WhoCommand(this)); - this.commandHandler.registerCommand(new SpawnCommand(this)); - // Dangerous Commands - this.commandHandler.registerCommand(new UnloadCommand(this)); - this.commandHandler.registerCommand(new LoadCommand(this)); - this.commandHandler.registerCommand(new RemoveCommand(this)); - this.commandHandler.registerCommand(new DeleteCommand(this)); - this.commandHandler.registerCommand(new RegenCommand(this)); - this.commandHandler.registerCommand(new ConfirmCommand(this)); - // Modification commands - this.commandHandler.registerCommand(new ModifyCommand(this)); - this.commandHandler.registerCommand(new PurgeCommand(this)); - this.commandHandler.registerCommand(new ModifyAddCommand(this)); - this.commandHandler.registerCommand(new ModifySetCommand(this)); - this.commandHandler.registerCommand(new ModifyRemoveCommand(this)); - this.commandHandler.registerCommand(new ModifyClearCommand(this)); - this.commandHandler.registerCommand(new ConfigCommand(this)); - this.commandHandler.registerCommand(new AnchorCommand(this)); - // Misc Commands - this.commandHandler.registerCommand(new EnvironmentCommand(this)); - this.commandHandler.registerCommand(new DebugCommand(this)); - this.commandHandler.registerCommand(new SilentCommand(this)); - this.commandHandler.registerCommand(new GeneratorCommand(this)); - this.commandHandler.registerCommand(new CheckCommand(this)); - this.commandHandler.registerCommand(new ScriptCommand(this)); - this.commandHandler.registerCommand(new GameruleCommand(this)); - this.commandHandler.registerCommand(new GamerulesCommand(this)); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDisable() { - this.saveMVConfigs(); - Logging.shutdown(); - } - - /** - * {@inheritDoc} - */ - @Override - public MVPlayerSession getPlayerSession(Player player) { - if (this.playerSessions.containsKey(player.getName())) { - return this.playerSessions.get(player.getName()); - } else { - this.playerSessions.put(player.getName(), new MVPlayerSession(player, getMVConfig())); - return this.playerSessions.get(player.getName()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public MVPermissions getMVPerms() { - return this.ph; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onCommand(CommandSender sender, Command command, String commandLabel, String[] args) { - if (!this.isEnabled()) { - sender.sendMessage("This plugin is Disabled!"); - return true; - } - ArrayList allArgs = new ArrayList(Arrays.asList(args)); - allArgs.add(0, command.getName()); - try { - return this.commandHandler.locateAndRunCommand(sender, allArgs, getMVConfig().getDisplayPermErrors()); - } catch (Exception e) { - e.printStackTrace(); - sender.sendMessage(ChatColor.RED + "An internal error occurred when attempting to perform this command."); - if (sender.isOp()) - sender.sendMessage(ChatColor.RED + "Details were printed to the server console and logs, please add that to your bug report."); - else - sender.sendMessage(ChatColor.RED + "Try again and contact the server owner or an admin if this problem persists."); - return true; - } - } - - /** - * {@inheritDoc} - * - * @deprecated This is now deprecated, nobody needs it any longer. - * All logging is now done with {@link Logging}. - */ - @Override - @Deprecated - public void log(Level level, String msg) { - Logging.log(level, msg); - } - - /** - * Logs a message at the specified level. - * - * @param level The Log-{@link Level}. - * @param msg The message to log. - * - * @deprecated Replaced by {@link Logging}. Please refrain from using this from a third party plugin as the - * messages will appear to originate from Multiverse-Core. - */ - @Deprecated - public static void staticLog(Level level, String msg) { - Logging.log(level, msg); - } - - /** - * Print messages to the Debug Log, if the servers in Debug Mode then we also want to print the messages to the - * standard Server Console. - * - * @param level The Log-{@link Level} - * @param msg The message - * - * @deprecated Replaced by {@link Logging}. Please refrain from using this from a third party plugin as the - * messages will appear to originate from Multiverse-Core. - */ - @Deprecated - public static void staticDebugLog(Level level, String msg) { - Logging.log(level, msg); - } - - /** - * {@inheritDoc} - */ - @Override - public String getAuthors() { - String authors = ""; - List auths = this.getDescription().getAuthors(); - if (auths.size() == 0) { - return ""; - } - - if (auths.size() == 1) { - return auths.get(0); - } - - for (int i = 0; i < auths.size(); i++) { - if (i == this.getDescription().getAuthors().size() - 1) { - authors += " and " + this.getDescription().getAuthors().get(i); - } else { - authors += ", " + this.getDescription().getAuthors().get(i); - } - } - return authors.substring(2); - } - - /** - * {@inheritDoc} - */ - @Override - public CommandHandler getCommandHandler() { - return this.commandHandler; - } - - /** - * {@inheritDoc} - */ - @Override - @Deprecated - public CommandQueueManager getCommandQueueManager() { - return commandQueueManager; - } - - /** - * Gets the log-tag. - * - * @return The log-tag - */ - // TODO this should be static! - public String getTag() { - return MultiverseCore.LOG_TAG; - } - - /** - * Shows a message that the given world is not a MultiverseWorld. - * - * @param sender The {@link CommandSender} that should receive the message - * @param worldName The name of the invalid world - */ - public void showNotMVWorldMessage(CommandSender sender, String worldName) { - sender.sendMessage("Multiverse doesn't know about " + ChatColor.DARK_AQUA + worldName + ChatColor.WHITE + " yet."); - sender.sendMessage("Type " + ChatColor.DARK_AQUA + "/mv import ?" + ChatColor.WHITE + " for help!"); - } - - /** - * Removes a player-session. - * - * @param player The {@link Player} that owned the session. - */ - public void removePlayerSession(Player player) { - if (this.playerSessions.containsKey(player.getName())) { - this.playerSessions.remove(player.getName()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getPluginCount() { - return this.pluginCount; - } - - /** - * {@inheritDoc} - */ - @Override - public void incrementPluginCount() { - this.pluginCount += 1; - } - - /** - * {@inheritDoc} - */ - @Override - public void decrementPluginCount() { - this.pluginCount -= 1; - } - - /** - * {@inheritDoc} - */ - @Override - public DestinationFactory getDestFactory() { - return this.destFactory; - } - - /** - * This is a convenience method to allow the QueuedCommand system to call it. You should NEVER call this directly. - * - * @param teleporter The Person requesting that the teleport should happen. - * @param p Player The Person being teleported. - * @param l The potentially unsafe location. - */ - public void teleportPlayer(CommandSender teleporter, Player p, Location l) { - // This command is the override, and MUST NOT TELEPORT SAFELY - this.getSafeTTeleporter().safelyTeleport(teleporter, p, l, false); - } - - /** - * Gets the server's root-folder as {@link File}. - * - * @return The server's root-folder - */ - public File getServerFolder() { - return serverFolder; - } - - /** - * Sets this server's root-folder. - * - * @param newServerFolder The new server-root - */ - public void setServerFolder(File newServerFolder) { - if (!newServerFolder.isDirectory()) - throw new IllegalArgumentException("That's not a folder!"); - - this.serverFolder = newServerFolder; - } - - /** - * {@inheritDoc} - */ - @Override - public MVWorldManager getMVWorldManager() { - return this.worldManager; - } - - /** - * Gets the {@link MVPlayerListener}. - * - * @return The {@link MVPlayerListener}. - */ - public MVPlayerListener getPlayerListener() { - return this.playerListener; - } - - /** - * Gets the {@link MVChatListener}. - * - * @return The {@link MVChatListener}. - */ - public MVChatListener getChatListener() { - return this.chatListener; - } - - /** - * Gets the {@link MVEntityListener}. - * - * @return The {@link MVEntityListener}. - */ - public MVEntityListener getEntityListener() { - return this.entityListener; - } - - /** - * Gets the {@link MVWeatherListener}. - * - * @return The {@link MVWeatherListener}. - */ - public MVWeatherListener getWeatherListener() { - return this.weatherListener; - } - - /** - * Saves the Multiverse-Config. - * - * @return Whether the Multiverse-Config was successfully saved - */ - public boolean saveMVConfig() { - try { - this.multiverseConfig.set("multiverse-configuration", getMVConfig()); - this.multiverseConfig.save(new File(getDataFolder(), "config.yml")); - return true; - } catch (IOException e) { - Logging.severe("Could not save Multiverse config.yml config. Please check your file permissions."); - return false; - } - } - - /** - * Saves the world config. - * - * @return Whether the world-config was successfully saved - */ - public boolean saveWorldConfig() { - return this.worldManager.saveWorldsConfig(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean saveMVConfigs() { - return this.saveMVConfig() && this.saveWorldConfig(); - } - - /** - * NOT deprecated for the time as queued commands use this. - * However, this is not in the API and other plugins should therefore not use it. - * - * @param name World to delete - * @return True if success, false if fail. - */ - public Boolean deleteWorld(String name) { - return this.worldManager.deleteWorld(name); - } - - /** - * NOT deprecated for the time as queued commands use this. - * However, this is not in the API and other plugins should therefore not use it. - * - * @param oldName World to copy - * @param newName World to create - * @param generator The Custom generator plugin to use. - * @return True if success, false if fail. - */ - public Boolean cloneWorld(String oldName, String newName, String generator) { - return this.worldManager.cloneWorld(oldName, newName, generator); - } - - /** - * {@inheritDoc} - * @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, 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); - } - - /** - * {@inheritDoc} - */ - @Override - public AnchorManager getAnchorManager() { - return this.anchorManager; - } - - /** - * {@inheritDoc} - */ - @Override - public BlockSafety getBlockSafety() { - return blockSafety; - } - - /** - * {@inheritDoc} - */ - @Override - public void setBlockSafety(BlockSafety bs) { - if (bs == null) { - throw new NullPointerException("block safety may not be null."); - } - this.blockSafety = bs; - } - - /** - * {@inheritDoc} - */ - @Override - public LocationManipulation getLocationManipulation() { - return locationManipulation; - } - - /** - * {@inheritDoc} - */ - @Override - public void setLocationManipulation(LocationManipulation locationManipulation) { - this.locationManipulation = locationManipulation; - } - - /** - * {@inheritDoc} - */ - @Override - public SafeTTeleporter getSafeTTeleporter() { - return safeTTeleporter; - } - - /** - * {@inheritDoc} - */ - @Override - public void setSafeTTeleporter(SafeTTeleporter safeTTeleporter) { - this.safeTTeleporter = safeTTeleporter; - } - - /** - * {@inheritDoc} - */ - @Override - public MultiverseCoreConfig getMVConfig() { - return config; - } - - /** - * This method is currently used by other plugins. - * It will be removed in 2.4 - * @return The Multiverse config. - * @deprecated This is deprecated. - */ - @Deprecated - public static MultiverseCoreConfiguration getStaticConfig() { - return MultiverseCoreConfiguration.getInstance(); - } - - @Override - public Buscript getScriptAPI() { - return buscript; - } - - public UnsafeCallWrapper getUnsafeCallWrapper() { - return this.unsafeCallWrapper; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCoreConfiguration.java b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCoreConfiguration.java deleted file mode 100644 index aaf21275c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCoreConfiguration.java +++ /dev/null @@ -1,385 +0,0 @@ -package com.onarandombox.MultiverseCore; - -import java.util.Map; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.api.MultiverseCoreConfig; -import com.onarandombox.MultiverseCore.event.MVDebugModeEvent; -import me.main__.util.SerializationConfig.NoSuchPropertyException; -import me.main__.util.SerializationConfig.Property; -import me.main__.util.SerializationConfig.SerializationConfig; -import org.bukkit.Bukkit; - -/** - * Our configuration. - */ -public class MultiverseCoreConfiguration extends SerializationConfig implements MultiverseCoreConfig { - private static MultiverseCoreConfiguration instance; - - /** - * Sets the statically saved instance. - * @param instance The new instance. - */ - public static void setInstance(MultiverseCoreConfiguration instance) { - MultiverseCoreConfiguration.instance = instance; - } - - /** - * @return True if the static instance of config is set. - */ - public static boolean isSet() { - return instance != null; - } - - /** - * Gets the statically saved instance. - * @return The statically saved instance. - */ - public static MultiverseCoreConfiguration getInstance() { - if (instance == null) - throw new IllegalStateException("The instance wasn't set!"); - return instance; - } - - @Property - private volatile boolean enforceaccess; - @Property - private volatile boolean prefixchat; - @Property - private volatile String prefixchatformat; - @Property - private volatile boolean useasyncchat; - @Property - private volatile boolean teleportintercept; - @Property - private volatile boolean firstspawnoverride; - @Property - private volatile boolean displaypermerrors; - @Property - private volatile boolean enablebuscript; - @Property - private volatile int globaldebug; - @Property - private volatile boolean silentstart; - @Property - private volatile int messagecooldown; - @Property - private volatile double version; - @Property - private volatile String firstspawnworld; - @Property - private volatile int teleportcooldown; - @Property - private volatile boolean defaultportalsearch; - @Property - private volatile int portalsearchradius; - @Property - private volatile boolean autopurge; - @Property - private volatile boolean idonotwanttodonate; - - public MultiverseCoreConfiguration() { - super(); - MultiverseCoreConfiguration.setInstance(this); - } - - public MultiverseCoreConfiguration(Map values) { - super(values); - MultiverseCoreConfiguration.setInstance(this); - } - - /** - * {@inheritDoc} - */ - @Override - protected void setDefaults() { - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - enforceaccess = false; - useasyncchat = true; - prefixchat = false; - prefixchatformat = "[%world%]%chat%"; - teleportintercept = true; - firstspawnoverride = true; - displaypermerrors = true; - enablebuscript = true; - globaldebug = 0; - messagecooldown = 5000; - teleportcooldown = 1000; - this.version = 2.9; - silentstart = false; - defaultportalsearch = true; - portalsearchradius = 128; - autopurge = true; - idonotwanttodonate = false; - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - } - - /** - * {@inheritDoc} - */ - @Override - public boolean setConfigProperty(String property, String value) { - try { - return this.setProperty(property, value, true); - } catch (NoSuchPropertyException e) { - return false; - } - } - - // And here we go: - - /** - * {@inheritDoc} - */ - @Override - public boolean getEnforceAccess() { - return this.enforceaccess; - } - - /** - * {@inheritDoc} - */ - @Override - public void setEnforceAccess(boolean enforceAccess) { - this.enforceaccess = enforceAccess; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getPrefixChat() { - return this.prefixchat; - } - - /** - * {@inheritDoc} - */ - @Override - public void setPrefixChat(boolean prefixChat) { - this.prefixchat = prefixChat; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPrefixChatFormat() { - return this.prefixchatformat; - } - - /** - * {@inheritDoc} - */ - @Override - public void setPrefixChatFormat(String prefixChatFormat) { - this.prefixchatformat = prefixChatFormat; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getTeleportIntercept() { - return this.teleportintercept; - } - - /** - * {@inheritDoc} - */ - @Override - public void setTeleportIntercept(boolean teleportIntercept) { - this.teleportintercept = teleportIntercept; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getFirstSpawnOverride() { - return this.firstspawnoverride; - } - - /** - * {@inheritDoc} - */ - @Override - public void setFirstSpawnOverride(boolean firstSpawnOverride) { - this.firstspawnoverride = firstSpawnOverride; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getDisplayPermErrors() { - return this.displaypermerrors; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getEnableBuscript() { - return this.enablebuscript; - } - - /** - * {@inheritDoc} - */ - @Override - public void setEnableBuscript(boolean enableBuscript) { - this.enablebuscript = enableBuscript; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDisplayPermErrors(boolean displayPermErrors) { - this.displaypermerrors = displayPermErrors; - } - - /** - * {@inheritDoc} - */ - @Override - public int getGlobalDebug() { - return this.globaldebug; - } - - /** - * {@inheritDoc} - */ - @Override - public void setGlobalDebug(int globalDebug) { - this.globaldebug = globalDebug; - Logging.setDebugLevel(globalDebug); - Bukkit.getPluginManager().callEvent(new MVDebugModeEvent(globalDebug)); - } - - /** - * {@inheritDoc} - */ - @Override - public int getMessageCooldown() { - return this.messagecooldown; - } - - /** - * {@inheritDoc} - */ - @Override - public void setMessageCooldown(int messageCooldown) { - this.messagecooldown = messageCooldown; - } - - /** - * {@inheritDoc} - */ - @Override - public double getVersion() { - return this.version; - } - - /** - * {@inheritDoc} - */ - @Override - public void setVersion(int version) { - this.version = version; - } - - /** - * {@inheritDoc} - */ - @Override - public String getFirstSpawnWorld() { - return this.firstspawnworld; - } - - /** - * {@inheritDoc} - */ - @Override - public void setFirstSpawnWorld(String firstSpawnWorld) { - this.firstspawnworld = firstSpawnWorld; - } - - /** - * {@inheritDoc} - */ - @Override - public int getTeleportCooldown() { - return this.teleportcooldown; - } - - /** - * {@inheritDoc} - */ - @Override - public void setTeleportCooldown(int teleportCooldown) { - this.teleportcooldown = teleportCooldown; - } - - @Override - public void setUseAsyncChat(boolean useAsyncChat) { - this.useasyncchat = useAsyncChat; - } - - @Override - public boolean getUseAsyncChat() { - return this.useasyncchat; - } - - @Override - public void setSilentStart(boolean silentStart) { - Logging.setShowingConfig(!silentStart); - this.silentstart = silentStart; - } - - @Override - public boolean getSilentStart() { - return silentstart; - } - - @Override - public void setUseDefaultPortalSearch(boolean useDefaultPortalSearch) { - defaultportalsearch = useDefaultPortalSearch; - } - - @Override - public boolean isUsingDefaultPortalSearch() { - return defaultportalsearch; - } - - @Override - public void setPortalSearchRadius(int searchRadius) { - this.portalsearchradius = searchRadius; - } - - @Override - public int getPortalSearchRadius() { - return portalsearchradius; - } - - @Override - public boolean isAutoPurgeEnabled() { - return autopurge; - } - - @Override - public void setAutoPurgeEnabled(boolean autopurge) { - this.autopurge = autopurge; - } - - @Override - public boolean isShowingDonateMessage() { - return !idonotwanttodonate; - } - - @Override - public void setShowDonateMessage(boolean showDonateMessage) { - this.idonotwanttodonate = !showDonateMessage; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/WorldProperties.java b/src/main/java/com/onarandombox/MultiverseCore/WorldProperties.java deleted file mode 100644 index 3dd208b86..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/WorldProperties.java +++ /dev/null @@ -1,655 +0,0 @@ -package com.onarandombox.MultiverseCore; - -import com.onarandombox.MultiverseCore.MVWorld.NullLocation; -import com.onarandombox.MultiverseCore.configuration.EntryFee; -import com.onarandombox.MultiverseCore.configuration.SpawnLocation; -import com.onarandombox.MultiverseCore.configuration.SpawnSettings; -import com.onarandombox.MultiverseCore.configuration.WorldPropertyValidator; -import com.onarandombox.MultiverseCore.enums.AllowedPortalType; -import com.onarandombox.MultiverseCore.enums.EnglishChatColor; -import com.onarandombox.MultiverseCore.enums.EnglishChatStyle; -import me.main__.util.SerializationConfig.IllegalPropertyValueException; -import me.main__.util.SerializationConfig.Property; -import me.main__.util.SerializationConfig.SerializationConfig; -import me.main__.util.SerializationConfig.Serializor; -import me.main__.util.SerializationConfig.Validator; -import me.main__.util.SerializationConfig.VirtualProperty; -import org.bukkit.ChatColor; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World.Environment; -import org.bukkit.configuration.serialization.SerializableAs; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/* - * This is a property class, I think we don't need that much javadoc. - * BEGIN CHECKSTYLE-SUPPRESSION: Javadoc - */ - -@SerializableAs("MVWorld") -public class WorldProperties extends SerializationConfig { - - private static final Map PROPERTY_ALIASES; - - static { - PROPERTY_ALIASES = new HashMap(); - PROPERTY_ALIASES.put("curr", "entryfee.currency"); - PROPERTY_ALIASES.put("currency", "entryfee.currency"); - PROPERTY_ALIASES.put("price", "entryfee.amount"); - PROPERTY_ALIASES.put("scaling", "scale"); - PROPERTY_ALIASES.put("aliascolor", "color"); - PROPERTY_ALIASES.put("heal", "autoHeal"); - PROPERTY_ALIASES.put("storm", "allowWeather"); - PROPERTY_ALIASES.put("weather", "allowWeather"); - PROPERTY_ALIASES.put("spawnmemory", "keepSpawnInMemory"); - PROPERTY_ALIASES.put("memory", "keepSpawnInMemory"); - PROPERTY_ALIASES.put("mode", "gameMode"); - PROPERTY_ALIASES.put("diff", "difficulty"); - PROPERTY_ALIASES.put("spawnlocation", "spawn"); - PROPERTY_ALIASES.put("limit", "playerLimit"); - PROPERTY_ALIASES.put("animals", "spawning.animals.spawn"); - PROPERTY_ALIASES.put("monsters", "spawning.monsters.spawn"); - PROPERTY_ALIASES.put("animalsrate", "spawning.animals.spawnrate"); - PROPERTY_ALIASES.put("monstersrate", "spawning.monsters.spawnrate"); - PROPERTY_ALIASES.put("flight", "allowFlight"); - PROPERTY_ALIASES.put("fly", "allowFlight"); - PROPERTY_ALIASES.put("allowfly", "allowFlight"); - } - - private final boolean keepSpawnFallback; - - public WorldProperties(Map values) { - super(values); - Object keepSpawnObject = values.get("keepSpawnInMemory"); - keepSpawnFallback = keepSpawnObject == null || Boolean.parseBoolean(keepSpawnObject.toString()); - } - - public WorldProperties() { - super(); - keepSpawnFallback = true; - } - - public WorldProperties(final boolean fixSpawn, final Environment environment) { - super(); - if (!fixSpawn) { - this.adjustSpawn = false; - } - setScaling(getDefaultScale(environment)); - keepSpawnFallback = true; - } - - void setMVWorld(MVWorld world) { - registerObjectUsing(world); - registerGlobalValidator(new WorldPropertyValidator()); - } - - /** - * Serializor for the color-property. - */ - private static final class EnumPropertySerializor> implements Serializor { - @Override - public String serialize(T from) { - return from.toString(); - } - - @Override - public T deserialize(String serialized, Class wanted) throws IllegalPropertyValueException { - try { - return Enum.valueOf(wanted, serialized.toUpperCase()); - } catch (IllegalArgumentException e) { - throw new IllegalPropertyValueException(e); - } - } - } - - /** - * Serializor for the difficulty-property. - */ - private static final class DifficultyPropertySerializor implements Serializor { - @Override - public String serialize(Difficulty from) { - return from.toString(); - } - - @Override - public Difficulty deserialize(String serialized, Class wanted) throws IllegalPropertyValueException { - try { - return Difficulty.getByValue(Integer.parseInt(serialized)); - } catch (Exception e) { - } - try { - return Difficulty.valueOf(serialized.toUpperCase()); - } catch (Exception e) { - } - throw new IllegalPropertyValueException(); - } - } - - /** - * Serializor for the gameMode-property. - */ - private static final class GameModePropertySerializor implements Serializor { - @Override - public String serialize(GameMode from) { - return from.toString(); - } - - @Override - public GameMode deserialize(String serialized, Class wanted) throws IllegalPropertyValueException { - try { - return GameMode.getByValue(Integer.parseInt(serialized)); - } catch (NumberFormatException nfe) { - } - try { - return GameMode.valueOf(serialized.toUpperCase()); - } catch (Exception e) { - } - throw new IllegalPropertyValueException(); - } - } - - /** - * Serializor for the time-property. - */ - private static final class TimePropertySerializor implements Serializor { - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - private static final String TIME_REGEX = "(\\d\\d?):?(\\d\\d)(a|p)?m?"; - private static final Map TIME_ALIASES; - static { - Map staticTimes = new HashMap(); - staticTimes.put("morning", "8:00"); - staticTimes.put("day", "12:00"); - staticTimes.put("noon", "12:00"); - staticTimes.put("midnight", "0:00"); - staticTimes.put("night", "20:00"); - - // now set TIME_ALIASES to a "frozen" map - TIME_ALIASES = Collections.unmodifiableMap(staticTimes); - } - - @Override - public String serialize(Long from) { - // I'm tired, so they get time in 24 hour for now. - // Someone else can add 12 hr format if they want :P - - int hours = (int) ((from / 1000 + 8) % 24); - int minutes = (int) (60 * (from % 1000) / 1000); - - return String.format("%d:%02d", hours, minutes); - } - - @Override - public Long deserialize(String serialized, Class wanted) throws IllegalPropertyValueException { - if (TIME_ALIASES.containsKey(serialized.toLowerCase())) { - serialized = TIME_ALIASES.get(serialized.toLowerCase()); - } - // Regex that extracts a time in the following formats: - // 11:11pm, 11:11, 23:11, 1111, 1111p, and the aliases at the top of this file. - Pattern pattern = Pattern.compile(TIME_REGEX, Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(serialized); - matcher.find(); - int hour = 0; - double minute = 0; - int count = matcher.groupCount(); - if (count >= 2) { - hour = Integer.parseInt(matcher.group(1)); - minute = Integer.parseInt(matcher.group(2)); - } - // If there were 4 matches (all, hour, min, am/pm) - if (count == 4) { - // We want 24 hour time for calcs, but if they - // added a p[m], turn it into a 24 hr one. - if (matcher.group(3).equals("p")) { - hour += 12; - } - } - // Translate 24th hour to 0th hour. - if (hour == 24) { - hour = 0; - } - // Clamp the hour - if (hour > 23 || hour < 0) { - throw new IllegalPropertyValueException("Illegal hour!"); - } - // Clamp the minute - if (minute > 59 || minute < 0) { - throw new IllegalPropertyValueException("Illegal minute!"); - } - // 60 seconds in a minute, time needs to be in hrs * 1000, per - // the bukkit docs. - double totaltime = (hour + (minute / 60.0)) * 1000; - // Somehow there's an 8 hour offset... - totaltime -= 8000; - if (totaltime < 0) { - totaltime = 24000 + totaltime; - } - - return (long) totaltime; - } - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - } - - // -------------------------------------------------------------- - // Begin properties - @Property(description = "Sorry, 'hidden' must either be: true or false.") - private volatile boolean hidden; - @Property(description = "Alias must be a valid string.") - private volatile String alias; - @Property(serializor = EnumPropertySerializor.class, description = "Sorry, 'color' must be a valid color-name.") - private volatile EnglishChatColor color; - @Property(serializor = EnumPropertySerializor.class, description = "Sorry, 'style' must be a valid style-name.") - private volatile EnglishChatStyle style; - @Property(description = "Sorry, 'pvp' must either be: true or false.", virtualType = Boolean.class, persistVirtual = true) - volatile VirtualProperty pvp; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property(description = "Scale must be a positive double value. ex: 2.3") - private volatile double scale; - @Property(description = "You must set this to the NAME not alias of a world.") - private volatile String respawnWorld; - @Property(description = "Sorry, this must either be: true or false.") - private volatile boolean allowWeather; - @Property(serializor = DifficultyPropertySerializor.class, virtualType = Difficulty.class, persistVirtual = true, - description = "Difficulty must be set as one of the following: peaceful easy normal hard") - volatile VirtualProperty difficulty; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property(description = "Sorry, 'animals' must either be: true or false.") - private volatile SpawnSettings spawning; - @Property - volatile EntryFee entryfee; - @Property(description = "Sorry, 'hunger' must either be: true or false.") - private volatile boolean hunger; - @Property(description = "Sorry, 'autoheal' must either be: true or false.") - private volatile boolean autoHeal; - @Property(description = "Sorry, 'adjustspawn' must either be: true or false.") - private volatile boolean adjustSpawn; - @Property(serializor = EnumPropertySerializor.class, description = "Allow portal forming must be NONE, ALL, NETHER or END.") - private volatile AllowedPortalType portalForm; - @Property(serializor = GameModePropertySerializor.class, description = "GameMode must be set as one of the following: survival creative") - private volatile GameMode gameMode; - @Property(description = "Sorry, this must either be: true or false.", virtualType = Boolean.class, persistVirtual = true) - volatile VirtualProperty keepSpawnInMemory; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property - volatile SpawnLocation spawnLocation; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property(virtualType = Location.class, - description = "There is no help available for this variable. Go bug Rigby90 about it.") - volatile VirtualProperty spawn; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property(description = "Set this to false ONLY if you don't want this world to load itself on server restart.") - private volatile boolean autoLoad; - @Property(description = "If a player dies in this world, shoudld they go to their bed?") - private volatile boolean bedRespawn; - @Property - private volatile List worldBlacklist; - @Property(serializor = TimePropertySerializor.class, virtualType = Long.class, - description = "Set the time to whatever you want! (Will NOT freeze time)") - volatile VirtualProperty time; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property - volatile Environment environment; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property - volatile long seed; // SUPPRESS CHECKSTYLE: VisibilityModifier - @Property - private volatile String generator; - @Property - private volatile int playerLimit; - @Property - private volatile boolean allowFlight; - // End of properties - // -------------------------------------------------------------- - - void setValidator(String fieldName, Validator validator) { - registerValidator(fieldName, validator); //To change body of overridden methods use File | Settings | File Templates. - } - - /** - * {@inheritDoc} - */ - @Override - public void copyValues(SerializationConfig other) { - super.copyValues(other); - } - - /** - * This prepares the MVWorld for unloading. - */ - public void cacheVirtualProperties() { - try { - this.buildVPropChanges(); - } catch (IllegalStateException e) { - // do nothing - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void setDefaults() { - this.hidden = false; - this.alias = new String(); - this.color = EnglishChatColor.WHITE; - this.style = EnglishChatStyle.NORMAL; - this.scale = 1D; - this.respawnWorld = new String(); - this.allowWeather = true; - this.spawning = new SpawnSettings(); - this.entryfee = new EntryFee(); - this.hunger = true; - this.autoHeal = true; - this.adjustSpawn = true; - this.portalForm = AllowedPortalType.ALL; - this.gameMode = GameMode.SURVIVAL; - this.spawnLocation = new NullLocation(); - this.autoLoad = true; - this.bedRespawn = true; - this.worldBlacklist = new ArrayList(); - this.generator = null; - this.playerLimit = -1; - this.allowFlight = true; - } - - private static double getDefaultScale(Environment environment) { - if (environment == Environment.NETHER) { - return 8.0; // SUPPRESS CHECKSTYLE: MagicNumberCheck - } else if (environment == Environment.THE_END) { - return 16.0; // SUPPRESS CHECKSTYLE: MagicNumberCheck - } - return 1.0; - } - - /** - * getAliases(). - * @return The alias-map. - * @see SerializationConfig - */ - protected static Map getAliases() { - return PROPERTY_ALIASES; - } - - void flushChanges() { - this.flushPendingVPropChanges(); - } - - public String getAlias() { - return this.alias; - } - - public void setAlias(String alias) { - this.setPropertyValueUnchecked("alias", alias); - } - - public Environment getEnvironment() { - return this.environment; - } - - public void setEnvironment(Environment environment) { - this.setPropertyValueUnchecked("environment", environment); - } - - public long getSeed() { - return this.seed; - } - - public void setSeed(long seed) { - this.setPropertyValueUnchecked("seed", seed); - } - - public String getGenerator() { - return this.generator; - } - - public void setGenerator(String generator) { - this.setPropertyValueUnchecked("generator", generator); - } - - public int getPlayerLimit() { - return this.playerLimit; - } - - public void setPlayerLimit(int limit) { - this.setPropertyValueUnchecked("playerLimit", limit); - } - - public boolean canAnimalsSpawn() { - return this.spawning.getAnimalSettings().doSpawn(); - } - - public void setAllowAnimalSpawn(boolean animals) { - this.setPropertyValueUnchecked("spawning.animals.spawn", animals); - } - - public List getAnimalList() { - // These don't fire events at the moment. Should they? - return this.spawning.getAnimalSettings().getExceptions(); - } - - public boolean canMonstersSpawn() { - return this.spawning.getMonsterSettings().doSpawn(); - } - - public void setAllowMonsterSpawn(boolean monsters) { - this.setPropertyValueUnchecked("spawning.monsters.spawn", monsters); - } - - public int getAnimalSpawnRate() { - return this.spawning.getAnimalSettings().getSpawnRate(); - } - - public int getMonsterSpawnRate() { - return this.spawning.getMonsterSettings().getSpawnRate(); - } - - public List getMonsterList() { - // These don't fire events at the moment. Should they? - return this.spawning.getMonsterSettings().getExceptions(); - } - - public boolean isPVPEnabled() { - return this.pvp.get(); - } - - public void setPVPMode(boolean pvp) { - this.setPropertyValueUnchecked("pvp", pvp); - } - - public boolean isHidden() { - return this.hidden; - } - - public void setHidden(boolean hidden) { - this.setPropertyValueUnchecked("hidden", hidden); - } - - public List getWorldBlacklist() { - return this.worldBlacklist; - } - - public double getScaling() { - return this.scale; - } - - public boolean setScaling(double scaling) { - return this.setPropertyValueUnchecked("scale", scaling); - } - - public boolean setColor(String aliasColor) { - return this.setPropertyUnchecked("color", aliasColor); - } - - public boolean setColor(EnglishChatColor color) { - return this.setPropertyValueUnchecked("color", color); - } - - public EnglishChatColor getColor() { - return this.color; - } - - public String getRespawnToWorld() { - return this.respawnWorld; - } - - public boolean setRespawnToWorld(String respawnToWorld) { - return this.setPropertyValueUnchecked("respawnWorld", respawnToWorld); - } - - public Material getCurrency() { - return this.entryfee.getCurrency(); - } - - public void setCurrency(@Nullable Material currency) { - this.setPropertyValueUnchecked("entryfee.currency", currency); - } - - public double getPrice() { - return this.entryfee.getAmount(); - } - - public void setPrice(double price) { - this.setPropertyValueUnchecked("entryfee.amount", price); - } - - public boolean setGameMode(String mode) { - return this.setPropertyUnchecked("gameMode", mode); - } - - public boolean setGameMode(GameMode mode) { - return this.setPropertyValueUnchecked("gameMode", mode); - } - - public GameMode getGameMode() { - return this.gameMode; - } - - public void setEnableWeather(boolean weather) { - this.setPropertyValueUnchecked("allowWeather", weather); - } - - public boolean isWeatherEnabled() { - return this.allowWeather; - } - - public boolean isKeepingSpawnInMemory() { - if (keepSpawnInMemory == null) { - return keepSpawnFallback; - } - try { - return this.keepSpawnInMemory.get(); - } catch (IllegalStateException e) { - return keepSpawnFallback; - } - } - - public void setKeepSpawnInMemory(boolean value) { - this.setPropertyValueUnchecked("keepSpawnInMemory", value); - } - - public boolean getHunger() { - return this.hunger; - } - - public void setHunger(boolean hunger) { - this.setPropertyValueUnchecked("hunger", hunger); - } - - public Location getSpawnLocation() { - return this.spawn.get(); - } - - public void setSpawnLocation(Location l) { - this.setPropertyValueUnchecked("spawn", l); - } - - public Difficulty getDifficulty() { - return this.difficulty.get(); - } - - @Deprecated // SUPPRESS CHECKSTYLE: Deprecated - public boolean setDifficulty(String difficulty) { - return this.setPropertyUnchecked("difficulty", difficulty); - } - - public boolean setDifficulty(Difficulty difficulty) { - return this.setPropertyValueUnchecked("difficulty", difficulty); - } - - public boolean getAutoHeal() { - return this.autoHeal; - } - - public void setAutoHeal(boolean heal) { - this.setPropertyValueUnchecked("autoHeal", heal); - } - - public void setAdjustSpawn(boolean adjust) { - this.setPropertyValueUnchecked("adjustSpawn", adjust); - } - - public boolean getAdjustSpawn() { - return this.adjustSpawn; - } - - public void setAutoLoad(boolean load) { - this.setPropertyValueUnchecked("autoLoad", load); - } - - public boolean getAutoLoad() { - return this.autoLoad; - } - - public void setBedRespawn(boolean respawn) { - this.setPropertyValueUnchecked("bedRespawn", respawn); - } - - public boolean getBedRespawn() { - return this.bedRespawn; - } - - public String getAllPropertyNames() { - ChatColor myColor = ChatColor.AQUA; - StringBuilder result = new StringBuilder(); - Map serialized = this.serialize(); - for (String key : serialized.keySet()) { - result.append(myColor).append(key).append(' '); - myColor = (myColor == ChatColor.AQUA) ? ChatColor.GOLD : ChatColor.AQUA; - } - return result.toString(); - } - - public String getTime() { - return this.getPropertyUnchecked("time"); - } - - public boolean setTime(String timeAsString) { - return this.setPropertyUnchecked("time", timeAsString); - } - - public AllowedPortalType getAllowedPortals() { - return portalForm; - } - - public void allowPortalMaking(AllowedPortalType portalType) { - this.setPropertyValueUnchecked("portalForm", portalType); - } - - public EnglishChatStyle getStyle() { - return style; - } - - public boolean setStyle(String style) { - return this.setPropertyUnchecked("style", style); - } - - public boolean getAllowFlight() { - return this.allowFlight; - } - - public void setAllowFlight(final boolean allowFlight) { - this.setPropertyValueUnchecked("allowFlight", allowFlight); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/BlockSafety.java b/src/main/java/com/onarandombox/MultiverseCore/api/BlockSafety.java deleted file mode 100644 index 5dc4f7ebc..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/BlockSafety.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Minecart; -import org.bukkit.entity.Vehicle; - -/** - * Used to get block/location-related information. - */ -public interface BlockSafety { - /** - * This function checks whether the block at the given coordinates are above air or not. - * @param l The {@link Location} of the block. - * @return True if the block at that {@link Location} is above air. - */ - boolean isBlockAboveAir(Location l); - - /** - * Checks if a player can spawn safely at the given coordinates. - * @param world The {@link World}. - * @param x The x-coordinate. - * @param y The y-coordinate. - * @param z The z-coordinate. - * @return True if a player can spawn safely at the given coordinates. - */ - boolean playerCanSpawnHereSafely(World world, double x, double y, double z); - - /** - * This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air - * etc. This also ensures there is enough space for a player to spawn! - * - * @param l The {@link Location} - * @return Whether the player can spawn safely at the given {@link Location} - */ - boolean playerCanSpawnHereSafely(Location l); - - /** - * Gets a safe bed spawn location OR null if the bed is invalid. - * @param l The location of the bead head (block with the pillow on it). - * @return Safe location around the bed or null if no location was found. - */ - Location getSafeBedSpawn(Location l); - - /** - * Gets the location of the top block at the specified {@link Location}. - * @param l Any {@link Location}. - * @return The {@link Location} of the top-block. - */ - Location getTopBlock(Location l); - - /** - * Gets the location of the top block at the specified {@link Location}. - * @param l Any {@link Location}. - * @return The {@link Location} of the top-block. - */ - Location getBottomBlock(Location l); - - /** - * Checks if an entity would be on track at the specified {@link Location}. - * @param l The {@link Location}. - * @return True if an entity would be on tracks at the specified {@link Location}. - */ - boolean isEntitiyOnTrack(Location l); - - /** - * Checks if the specified {@link Minecart} can spawn safely. - * @param cart The {@link Minecart}. - * @return True if the minecart can spawn safely. - */ - boolean canSpawnCartSafely(Minecart cart); - - /** - * Checks if the specified {@link Vehicle} can spawn safely. - * @param vehicle The {@link Vehicle}. - * @return True if the vehicle can spawn safely. - */ - boolean canSpawnVehicleSafely(Vehicle vehicle); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/Core.java b/src/main/java/com/onarandombox/MultiverseCore/api/Core.java deleted file mode 100644 index 5f66cddb1..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/Core.java +++ /dev/null @@ -1,245 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.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; -import com.onarandombox.MultiverseCore.utils.MVPermissions; -import com.onarandombox.MultiverseCore.utils.MVPlayerSession; -import com.onarandombox.MultiverseCore.utils.SimpleBlockSafety; -import com.onarandombox.MultiverseCore.utils.SimpleLocationManipulation; -import com.onarandombox.MultiverseCore.utils.SimpleSafeTTeleporter; -import com.onarandombox.MultiverseCore.utils.VaultHandler; -import com.pneumaticraft.commandhandler.CommandHandler; -import org.bukkit.entity.Player; - -/** - * Multiverse 2 Core API - *

- * This API contains a bunch of useful things you can get out of Multiverse in general! - */ -public interface Core { - - /** - * Returns the Vault handler used by Multiverse. The returned object will have all methods necessary for - * interfacing with Vault. - * - * @return the Vault handler for Multiverse. - * @deprecated we are now using {@link #getEconomist()} for all economy needs. - */ - @Deprecated - VaultHandler getVaultHandler(); - - /** - * Retrieves Multiverse's friendly economist. The economist can be used for dealing with economies without - * worrying about any of the messy details. - * - * @return the economy manager for Multiverse. - */ - MVEconomist getEconomist(); - - /** - * Reloads the Multiverse Configuration files: - * worlds.yml and config.yml. - */ - void loadConfigs(); - - /** - * Gets the Multiverse message system. This allows you to send messages - * to users at specified intervals. - * - * @return The loaded {@link MultiverseMessaging}. - */ - MultiverseMessaging getMessaging(); - - /** - * Gets the {@link MVPlayerSession} for the given player. - * This will also create a player session if one does not exist - * for a player. - * - * @param player The player's session to grab. - * - * @return The corresponding {@link MVPlayerSession}. - */ - MVPlayerSession getPlayerSession(Player player); - - /** - * Multiverse uses an advanced permissions setup, this object - * simplifies getting/setting permissions. - * - * @return A non-null {@link MVPermissions}. - */ - MVPermissions getMVPerms(); - - /** - * Multiverse uses {@link CommandHandler} to make adding and using commands - * a piece of cake. - * - * @return A non-null {@link CommandHandler}. - */ - 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. - * - * @return A valid {@link DestinationFactory}. - */ - DestinationFactory getDestFactory(); - - /** - * Gets the primary class responsible for managing Multiverse Worlds. - * - * @return {@link MVWorldManager}. - */ - MVWorldManager getMVWorldManager(); - - /** - * Saves all configs. - * - * @return Whether the config was successfully saved - */ - boolean saveMVConfigs(); - - /** - * Gets the {@link AnchorManager}. - * - * @return The {@link AnchorManager} - */ - AnchorManager getAnchorManager(); - - /** - * 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. - * - * @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); - - /** - * 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. - */ - void decrementPluginCount(); - - /** - * Increments the number of plugins that have specifically hooked into core. - */ - void incrementPluginCount(); - - /** - * Returns the number of plugins that have specifically hooked into core. - * - * @return The number if plugins that have hooked into core. - */ - int getPluginCount(); - - /** - * Parse the Authors Array into a readable String with ',' and 'and'. - * - * @return The readable authors-{@link String} - */ - String getAuthors(); - - /** - * Gets the {@link BlockSafety} this {@link Core} is using. - * @return The {@link BlockSafety} this {@link Core} is using. - * @see BlockSafety - * @see SimpleBlockSafety - */ - BlockSafety getBlockSafety(); - - /** - * Sets the {@link BlockSafety} this {@link Core} is using. - * @param blockSafety The new {@link BlockSafety}. - * @see BlockSafety - * @see SimpleBlockSafety - */ - void setBlockSafety(BlockSafety blockSafety); - - /** - * Gets the {@link LocationManipulation} this {@link Core} is using. - * @return The {@link LocationManipulation} this {@link Core} is using. - * @see LocationManipulation - * @see SimpleLocationManipulation - */ - LocationManipulation getLocationManipulation(); - - /** - * Sets the {@link LocationManipulation} this {@link Core} is using. - * @param locationManipulation The new {@link LocationManipulation}. - * @see LocationManipulation - * @see SimpleLocationManipulation - */ - void setLocationManipulation(LocationManipulation locationManipulation); - - /** - * Gets the {@link SafeTTeleporter} this {@link Core} is using. - * @return The {@link SafeTTeleporter} this {@link Core} is using. - * @see SafeTTeleporter - * @see SimpleSafeTTeleporter - */ - SafeTTeleporter getSafeTTeleporter(); - - /** - * Sets the {@link SafeTTeleporter} this {@link Core} is using. - * @param safeTTeleporter The new {@link SafeTTeleporter}. - * @see SafeTTeleporter - * @see SimpleSafeTTeleporter - */ - void setSafeTTeleporter(SafeTTeleporter safeTTeleporter); - - /** - * Gets the {@link MultiverseCoreConfig}. - * @return The configuration. - */ - MultiverseCoreConfig getMVConfig(); - - /** - * Gets the buscript object for Multiverse. This is what handles Javascript processing. - * - * @return The Multiverse buscript object. - */ - Buscript getScriptAPI(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/FancyText.java b/src/main/java/com/onarandombox/MultiverseCore/api/FancyText.java deleted file mode 100644 index abc740db0..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/FancyText.java +++ /dev/null @@ -1,20 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -/** - * A fancy text. - */ -public interface FancyText { - /** - * Gets the {@link String}-representation of this {@link FancyText}. - * - * @return The {@link String}-representation of this {@link FancyText}. - */ - String getFancyText(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/LocationManipulation.java b/src/main/java/com/onarandombox/MultiverseCore/api/LocationManipulation.java deleted file mode 100644 index e246c8da1..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/LocationManipulation.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import org.bukkit.Location; -import org.bukkit.entity.Vehicle; -import org.bukkit.util.Vector; - -/** - * Used to manipulate locations. - */ -public interface LocationManipulation { - /** - * Convert a Location into a Colon separated string to allow us to store it in text. - *

- * WORLD:X,Y,Z:yaw:pitch - *

- * The corresponding String2Loc function is {@link #stringToLocation} - * - * @param location The Location to save. - * @return The location as a string in this format: WORLD:x,y,z:yaw:pitch - */ - String locationToString(Location location); - - /** - * This method simply does some rounding, rather than forcing a call to the server to get the blockdata. - * - * @param l The location to round to the block location - * @return A rounded location. - */ - Location getBlockLocation(Location l); - - /** - * Returns a new location from a given string. The format is as follows: - *

- * WORLD:X,Y,Z:yaw:pitch - *

- * The corresponding Location2String function is {@link #stringToLocation} - * - * @param locationString The location represented as a string (WORLD:X,Y,Z:yaw:pitch) - * @return A new location defined by the string or null if the string was invalid. - */ - Location stringToLocation(String locationString); - - /** - * Returns a colored string with the coords. - * - * @param l The {@link Location} - * @return The {@link String} - */ - String strCoords(Location l); - - /** - * Converts a location to a printable readable formatted string including pitch/yaw. - * - * @param l The {@link Location} - * @return The {@link String} - */ - String strCoordsRaw(Location l); - - /** - * Return the NESW Direction a Location is facing. - * - * @param location The {@link Location} - * @return The NESW Direction - */ - String getDirection(Location location); - - /** - * Returns the float yaw position for the given cardinal direction. - * - * @param orientation The cardinal direction - * @return The yaw - */ - float getYaw(String orientation); - - /** - * Returns a speed float from a given vector. - * - * @param v The {@link Vector} - * @return The speed - */ - float getSpeed(Vector v); - - /** - * Returns a translated vector from the given direction. - * - * @param v The old {@link Vector} - * @param direction The new direction - * @return The translated {@link Vector} - */ - Vector getTranslatedVector(Vector v, String direction); - - /** - * Returns the next Location that a {@link Vehicle} is traveling at. - * - * @param v The {@link Vehicle} - * @return The {@link Location} - */ - Location getNextBlock(Vehicle v); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/LoggablePlugin.java b/src/main/java/com/onarandombox/MultiverseCore/api/LoggablePlugin.java deleted file mode 100644 index 56852e45c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/LoggablePlugin.java +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.Server; - -import java.util.logging.Level; - -/** - * A simple API to require plugins to have a log method. - * - * @deprecated Replaced by {@link Logging}. - * */ -@Deprecated -public interface LoggablePlugin { - /** - * Logs a message at the specified level. - * - * @param level The Log-{@link Level}. - * @param msg The message to log. - */ - void log(Level level, String msg); - - /** - * Gets the server instance that this plugin is attached to. - * - * @return A {@link Server} instance. - */ - Server getServer(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MVDestination.java b/src/main/java/com/onarandombox/MultiverseCore/api/MVDestination.java deleted file mode 100644 index c4e64de8a..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MVDestination.java +++ /dev/null @@ -1,141 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -/** - * A destination API for Multiverse - * Any plugin can add these to MV and when they are, any action that uses them (portals, MVTP, etc.) can use them! - */ -public interface MVDestination { - /** - * Returns the identifier or prefix that is required for this destination. - *

- * Portals have a prefix of "p" for example and OpenWarp (third party plugin) uses "ow". This is derived from a - * hash and cannot have duplicate values. Read that as your plugin cannot use 'p' because it's already used. - * Please check the wiki when adding a custom destination! - * - * @return The identifier or prefix that is required for this destination. - */ - String getIdentifier(); - - /** - * Allows you to determine if a Destination is valid for the type it thinks it is. - *

- * An example of this would be the exact destination. A valid string would be: e:0,0,0 where an invalid one would - * be e:1:2:3. The first string would return true the second would return false. This is simply a convenience - * method - * and does not even NEED to be called, but it's highly recommended if you're teleporting, but it's mainly for - * Multiverse Internal use. - * - * @param plugin The plugin who the type belongs to. - * @param destination The destination string. ex: p:MyPortal:nw - * - * @return True if the destination is valid, false if not. - */ - boolean isThisType(JavaPlugin plugin, String destination); - - /** - * Returns the location a specific entity will spawn at when being teleported to this Destination. - *

- * To just retrieve the location as it is stored you can just pass null, but be warned some destinations may return - * null back to you if you do this. It is always safer to pass an actual entity. This is used so things like - * minecarts can be teleported. - *

- * Do not forget to use {@link #getVelocity()} as destinations can use this too! - * - * @param entity The entity to be teleported. - * - * @return The location of the entity. - */ - Location getLocation(Entity entity); - - /** - * Returns the velocity vector for this destination. - *

- * Plugins wishing to fully support MVDestinations MUST implement this. - * - * @return A vector representing the speed/direction the player should travel when arriving - */ - Vector getVelocity(); - - /** - * Sets the destination string. - *

- * This should be used when you want to tell this destination object about a change in where it should take people. - * The destination param should be match the result from {@link #getIdentifier()}. A valid example would be that if - * {@link #getIdentifier()} returned "ow" our destination string could be "ow:TownCenter" but could not be - * "p:HomePortal" - * - * @param plugin The plugin who the type belongs to. - * @param destination The destination string. ex: p:MyPortal:nw - */ - void setDestination(JavaPlugin plugin, String destination); - - /** - * Returns true if the destination is valid and players will be taken to it. - *

- * Even if destinations are in the correct format (p:MyPortal) MyPortal may not exist, and therefore this would - * return false. - * - * @return True if the destination is valid; false if not. - */ - boolean isValid(); - - /** - * Gives you a general friendly description of the type of destination. - *

- * For example, the PlayerDestination sets this to "Player". You can use this to show where a player will be taken. - * - * @return A friendly string description of the type of destination. - */ - String getType(); - - /** - * Gives you a specific name of the destination. - *

- * For example, the PlayerDestination sets this to The Player's Name. - * - * @return A friendly string stating the name of the destination. - */ - String getName(); - - /** - * Returns a string that can easily be saved in the config that contains all the details needed to rebuild this - * destination. - *

- * ex: e:0,0,0:50:50 - * - * @return The savable config string. - */ - String toString(); - - /** - * Returns the permissions string required to go here. - *

- * ex: multiverse.access.world - *

- * NOTE: This is NOT the permission to use the teleport command. - * - * @return the permissions string required to go here. - */ - String getRequiredPermission(); - - /** - * Should the Multiverse SafeTeleporter be used? - *

- * If not, MV will blindly take people to the location specified. - * - * @return True if the SafeTeleporter will be used, false if not. - */ - boolean useSafeTeleporter(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MVPlugin.java b/src/main/java/com/onarandombox/MultiverseCore/api/MVPlugin.java deleted file mode 100644 index e70cd35dd..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MVPlugin.java +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.event.MVVersionEvent; - -/** - * This interface is implemented by every official Multiverse-plugin. - */ -public interface MVPlugin extends LoggablePlugin { - /** - * Adds This plugin's version information to the buffer and returns the new string. - * - * @param buffer The string that contains Core and all other MV plugins' versions. - * - * @return A modified buffer that contains this MVPlugin's version information. - * - * @deprecated This is now deprecated, nobody needs it any longer. - * All version info-dumping is now done with {@link MVVersionEvent}. - */ - @Deprecated - String dumpVersionInfo(String buffer); - - /** - * Gets the reference to MultiverseCore. - * - * @return A valid {@link com.onarandombox.MultiverseCore}. - */ - MultiverseCore getCore(); - - /** - * Sets the reference to MultiverseCore. - * - * @param core A valid {@link com.onarandombox.MultiverseCore}. - */ - void setCore(MultiverseCore core); - - /** - * Allows Multiverse or a plugin to query another Multiverse plugin to see what version its protocol is. This - * number - * should change when something will break the code. - * - * @return The Integer protocol version. - */ - int getProtocolVersion(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java deleted file mode 100644 index 599ce7cb9..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MVWorldManager.java +++ /dev/null @@ -1,358 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -import com.onarandombox.MultiverseCore.utils.PurgeWorlds; -import com.onarandombox.MultiverseCore.utils.SimpleWorldPurger; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.WorldType; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.generator.ChunkGenerator; - -import java.io.File; -import java.util.Collection; -import java.util.List; - -/** - * Multiverse 2 World Manager API - *

- * This API contains all of the world managing - * functions that your heart desires! - */ -public interface MVWorldManager { - /** - * Add a new World to the Multiverse Setup. - * - * @param name World Name - * @param env Environment Type - * @param seedString The seed in the form of a string. - * If the seed is a Long, - * it will be interpreted as such. - * @param type The Type of the world to be made. - * @param generateStructures If true, this world will get NPC villages. - * @param generator The Custom generator plugin to use. - * @return True if the world is added, false if not. - */ - boolean addWorld(String name, Environment env, String seedString, WorldType type, Boolean generateStructures, - String generator); - - /** - * Add a new World to the Multiverse Setup. - * - * @param name World Name - * @param env Environment Type - * @param seedString The seed in the form of a string. - * If the seed is a Long, - * it will be interpreted as such. - * @param type The Type of the world to be made. - * @param generateStructures If true, this world will get NPC villages. - * @param generator The Custom generator plugin to use. - * @param useSpawnAdjust If true, multiverse will search for a safe spawn. If not, It will not modify the level.dat. - * @return True if the world is added, false if not. - */ - boolean addWorld(String name, Environment env, String seedString, WorldType type, Boolean generateStructures, - String generator, boolean useSpawnAdjust); - - /** - * Make a copy of a world. - * - * @param oldName Name of world to be copied - * @param newName Name of world to be created - * @param generator The Custom generator plugin to use. Ignored. - * @return True if the world is copied successfully, false if not. - * @deprecated Use {@link #cloneWorld(String, String)} instead. - */ - @Deprecated - boolean cloneWorld(String oldName, String newName, String generator); - - /** - * Make a copy of a world. - * - * @param oldName - * Name of world to be copied - * @param newName - * Name of world to be created - * @return True if the world is copied successfully, false if not. - */ - boolean cloneWorld(String oldName, String newName); - - /** - * Remove the world from the Multiverse list, from the config and deletes - * the folder. - * - * @param name - * The name of the world to remove - * @return True if success, false if failure. - */ - boolean deleteWorld(String name); - - /** - * Remove the world from the Multiverse list, from the - * config if wanted, and deletes the folder. - * - * @param name The name of the world to remove - * @param removeConfig If true(default), we'll remove the entries from the - * config. If false, they'll stay and the world may come back. - * @return True if success, false if failure. - */ - boolean deleteWorld(String name, boolean removeConfig); - - /** - * - * @param name The name of the world to remove - * @param removeFromConfig If true(default), we'll remove the entries from the - * config. If false, they'll stay and the world may come back. - * @param deleteWorldFolder If true the world folder will be completely deleted. If false - * only the contents of the world folder will be deleted - * @return True if success, false if failure. - */ - boolean deleteWorld(String name, boolean removeFromConfig, boolean deleteWorldFolder); - - /** - * Unload a world from Multiverse. - * - * @param name Name of the world to unload - * @return True if the world was unloaded, false if not. - */ - boolean unloadWorld(String name); - - /** - * Unload a world from Multiverse with option to prevent calling unloadWorld in Bukkit. - * - * @param name Name of the world to unload - * @param unloadBukkit True if Bukkit world should be unloaded - * @return True if the world was unloaded, false if not. - */ - boolean unloadWorld(String name, boolean unloadBukkit); - - /** - * Loads the world. Only use this if the world has been - * unloaded with {@link #unloadWorld(String)}. - * - * @param name The name of the world to load - * @return True if success, false if failure. - */ - boolean loadWorld(String name); - - /** - * Removes all players from the specified world. - * - * @param name World to remove players from. - */ - void removePlayersFromWorld(String name); - - /** - * Test if a given chunk generator is valid. - * - * @param generator The generator name. - * @param generatorID The generator id. - * @param worldName The worldName to use as the default. - * @return A {@link ChunkGenerator} or null - */ - ChunkGenerator getChunkGenerator(String generator, String generatorID, String worldName); - - /** - * Returns a list of all the worlds Multiverse knows about. - * - * @return A list of {@link MultiverseWorld}. - */ - Collection getMVWorlds(); - - - /** - * Returns a {@link MultiverseWorld} if it exists, and null if it does not. - * This will search name AND alias. - * - * @param name The name or alias of the world to get. - * @return A {@link MultiverseWorld} or null. - */ - 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. - * - * @param world The Bukkit world to check. - * @return A {@link MultiverseWorld} or null. - */ - MultiverseWorld getMVWorld(World world); - - /** - * 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}. - * - * @param world The Bukkit world to check. - * @return True if the world has been loaded into MV2, false if not. - */ - boolean isMVWorld(World world); - - /** - * Load the Worlds & Settings from the configuration file. - * - * @param forceLoad If set to true, this will perform a total - * reset and not just load new worlds. - */ - void loadWorlds(boolean forceLoad); - - /** - * Loads the Worlds & Settings for any worlds that bukkit loaded before us. - *

- * This way people will _always_ have some worlds in the list. - */ - void loadDefaultWorlds(); - - /** - * Return the World Purger. - * - * @return A valid {@link PurgeWorlds}. - * @deprecated {@link PurgeWorlds} is deprecated! - */ - @Deprecated - PurgeWorlds getWorldPurger(); - - /** - * Gets the {@link WorldPurger}. - *

- * TODO: Remove {@link #getWorldPurger()} and replace it with this method. - * @return The {@link WorldPurger} this {@link MVWorldManager} is using. - * @see WorldPurger - * @see SimpleWorldPurger - */ - WorldPurger getTheWorldPurger(); - - /** - * Gets the world players will spawn in on first join. - * Currently this always returns worlds.get(0) from Bukkit. - * - * @return A Multiverse world that players will spawn in or null if no MV world has been set. - */ - MultiverseWorld getSpawnWorld(); - - /** - * Gets the list of worlds in the config, but unloaded. - * - * @return A List of worlds as strings. - */ - List getUnloadedWorlds(); - - /** - * This method populates an internal list and needs to be called after multiverse initialization. - */ - void getDefaultWorldGenerators(); - - /** - * Load the config from a file. - * - * @param file The file to load. - * @return A loaded configuration. - */ - FileConfiguration loadWorldConfig(File file); - - /** - * Saves the world config to disk. - * - * @return True if success, false if fail. - */ - boolean saveWorldsConfig(); - - /** - * Remove the world from the Multiverse list and from the config. - * - * @param name The name of the world to remove - * @return True if success, false if failure. - */ - boolean removeWorldFromConfig(String name); - - /** - * Sets the initial spawn world for new players. - * - * @param world The World new players should spawn in. - */ - void setFirstSpawnWorld(String world); - - /** - * Gets the world players should spawn in first. - * - * @return The {@link MultiverseWorld} new players should spawn in. - */ - MultiverseWorld getFirstSpawnWorld(); - - /** - * 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. - * - * @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); - - /** - * Checks whether Multiverse knows about a provided unloaded world. This - * method will check the parameter against the alias mappings. - * - * @param name The name of the unloaded world - * @param includeLoaded The value to return if the world is loaded - * - * @return True if the world exists and is unloaded. False if the world - * 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 getPotentialWorlds(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseCoreConfig.java b/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseCoreConfig.java deleted file mode 100644 index 07b2bb91d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseCoreConfig.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import org.bukkit.configuration.serialization.ConfigurationSerializable; - -/** - * The configuration of MultiverseCore. - */ -public interface MultiverseCoreConfig extends ConfigurationSerializable { - /** - * Sets a property using a {@link String}. - * @param property The name of the property. - * @param value The value. - * @return True on success, false if the operation failed. - */ - boolean setConfigProperty(String property, String value); - - /** - * Sets portalCooldown. - * @param portalCooldown The new value. - */ - void setTeleportCooldown(int portalCooldown); - - /** - * Gets portalCooldown. - * @return portalCooldown. - */ - int getTeleportCooldown(); - - /** - * Sets firstSpawnWorld. - * @param firstSpawnWorld The new value. - */ - void setFirstSpawnWorld(String firstSpawnWorld); - - /** - * Gets firstSpawnWorld. - * @return firstSpawnWorld. - */ - String getFirstSpawnWorld(); - - /** - * Sets version. - * @param version The new value. - */ - void setVersion(int version); - - /** - * Gets version. - * @return version. - */ - double getVersion(); - - /** - * Sets messageCooldown. - * @param messageCooldown The new value. - */ - void setMessageCooldown(int messageCooldown); - - /** - * Gets messageCooldown. - * @return messageCooldown. - */ - int getMessageCooldown(); - - /** - * Sets globalDebug. - * @param globalDebug The new value. - */ - void setGlobalDebug(int globalDebug); - - /** - * Gets globalDebug. - * @return globalDebug. - */ - int getGlobalDebug(); - - /** - * Sets displayPermErrors. - * @param displayPermErrors The new value. - */ - void setDisplayPermErrors(boolean displayPermErrors); - - /** - * Gets displayPermErrors. - * @return displayPermErrors. - */ - boolean getDisplayPermErrors(); - - /** - * Sets enableBuscript. - * @param enableBuscript The new value. - */ - void setEnableBuscript(boolean enableBuscript); - - /** - * Gets enableBuscript. - * @return enableBuscript. - */ - boolean getEnableBuscript(); - - /** - * Sets firstSpawnOverride. - * @param firstSpawnOverride The new value. - */ - void setFirstSpawnOverride(boolean firstSpawnOverride); - - /** - * Gets firstSpawnOverride. - * @return firstSpawnOverride. - */ - boolean getFirstSpawnOverride(); - - /** - * Sets teleportIntercept. - * @param teleportIntercept The new value. - */ - void setTeleportIntercept(boolean teleportIntercept); - - /** - * Gets teleportIntercept. - * @return teleportIntercept. - */ - boolean getTeleportIntercept(); - - /** - * Sets prefixChat. - * @param prefixChat The new value. - */ - void setPrefixChat(boolean prefixChat); - - /** - * Gets prefixChat. - * @return prefixChat. - */ - boolean getPrefixChat(); - - /** - * Sets prefixChatFormat. - * @param prefixChatFormat The new value. - */ - void setPrefixChatFormat(String prefixChatFormat); - - /** - * Gets prefixChatFormat. - * @return prefixChatFormat. - */ - String getPrefixChatFormat(); - - /** - * Sets enforceAccess. - * @param enforceAccess The new value. - */ - void setEnforceAccess(boolean enforceAccess); - - /** - * Gets enforceAccess. - * @return enforceAccess. - */ - boolean getEnforceAccess(); - - /** - * Sets useasyncchat. - * @param useAsyncChat The new value. - */ - void setUseAsyncChat(boolean useAsyncChat); - - /** - * Gets useasyncchat. - * @return useasyncchat. - */ - boolean getUseAsyncChat(); - - /** - * Sets whether to suppress startup messages. - * - * @param silentStart true to suppress messages. - */ - void setSilentStart(boolean silentStart); - - /** - * Whether we are suppressing startup messages. - * - * @return true if we are suppressing startup messages. - */ - boolean getSilentStart(); - - /** - * Sets whether or not to let Bukkit determine portal search radius on its own or if Multiverse should give input. - * - * @param useDefaultPortalSearch True to let Bukkit determine portal search radius on its own. - */ - void setUseDefaultPortalSearch(boolean useDefaultPortalSearch); - - /** - * Gets whether or not Bukkit will be determining portal search radius on its own or if Multiverse should help. - * - * @return True means Bukkit will use its own default values. - */ - boolean isUsingDefaultPortalSearch(); - - /** - * Sets the radius at which vanilla style portals will be searched for to connect to worlds together. - * - * @param searchRadius The portal search radius. - */ - void setPortalSearchRadius(int searchRadius); - - /** - * Gets the radius at which vanilla style portals will be searched for to connect to worlds together. - * - * @return The portal search radius. - */ - int getPortalSearchRadius(); - - /** - * Gets whether or not the automatic purge of entities is enabled. - * - * @return True if automatic purge is enabled. - */ - boolean isAutoPurgeEnabled(); - - /** - * Sets whether or not the automatic purge of entities is enabled. - * - * @param autopurge True if automatic purge should be enabled. - */ - void setAutoPurgeEnabled(boolean autopurge); - - /** - * Gets whether or not the donation/patreon messages are shown. - * - * @return True if donation/patreon messages should be shown. - */ - boolean isShowingDonateMessage(); - - /** - * Sets whether or not the donation/patreon messages are shown. - * - * @param idonotwanttodonate True if donation/patreon messages should be shown. - */ - void setShowDonateMessage(boolean idonotwanttodonate); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseMessaging.java b/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseMessaging.java deleted file mode 100644 index ae2bd004d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseMessaging.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import java.util.Collection; - -import org.bukkit.command.CommandSender; - -/** - * Multiverse-messaging. - */ -public interface MultiverseMessaging { - /** - * Sets the message-cooldown. - * @param milliseconds The new message-cooldown in milliseconds. - */ - void setCooldown(int milliseconds); - - /** - * Sends a message to the specified sender if the cooldown has passed. - * - * @param sender The person/console to send the message to. - * @param message The message to send. - * @param ignoreCooldown If true this message will always be sent. Useful for things like menus - * @return true if the message was sent, false if not. - */ - boolean sendMessage(CommandSender sender, String message, boolean ignoreCooldown); - - /** - * Sends a group of messages to the specified sender if the cooldown has passed. - * This method is needed, since sending many messages in quick succession would violate - * the cooldown. - * - * @param sender The person/console to send the message to. - * @param messages The messages to send. - * @param ignoreCooldown If true these messages will always be sent. Useful for things like menus - * @return true if the message was sent, false if not. - */ - boolean sendMessages(CommandSender sender, String[] messages, boolean ignoreCooldown); - - /** - * Sends a group of messages to the specified sender if the cooldown has passed. - * This method is needed, since sending many messages in quick succession would violate - * the cooldown. - * - * @param sender The person/console to send the message to. - * @param messages The messages to send. - * @param ignoreCooldown If true these messages will always be sent. Useful for things like menus - * @return true if the message was sent, false if not. - */ - boolean sendMessages(CommandSender sender, Collection messages, boolean ignoreCooldown); - - /** - * Gets the message-cooldown. - * @return The message-cooldown. - */ - int getCooldown(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MultiversePlugin.java b/src/main/java/com/onarandombox/MultiverseCore/api/MultiversePlugin.java deleted file mode 100644 index a6a064b8a..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MultiversePlugin.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.plugin.java.JavaPlugin; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.utils.DebugLog; -import com.pneumaticraft.commandhandler.CommandHandler; - -/** - * Make things easier for MV-Plugins! - */ -public abstract class MultiversePlugin extends JavaPlugin implements MVPlugin { - private MultiverseCore core; - /** - * Prefix for standard log entrys. - */ - protected String logTag; - private DebugLog debugLog; - - /** - * {@inheritDoc} - * - * Note: You can't override this, use {@link #onPluginEnable()} instead! - * @see #onPluginEnable() - */ - @Override - public final void onEnable() { - MultiverseCore theCore = (MultiverseCore) this.getServer().getPluginManager().getPlugin("Multiverse-Core"); - if (theCore == null) { - this.getLogger().severe("Core not found! The plugin dev needs to add a dependency!"); - this.getLogger().severe("Disabling!"); - this.getServer().getPluginManager().disablePlugin(this); - return; - } - if (theCore.getProtocolVersion() < this.getProtocolVersion()) { - this.getLogger().severe("You need a newer version of Multiverse-Core!"); - this.getLogger().severe("Disabling!"); - this.getServer().getPluginManager().disablePlugin(this); - return; - } - this.setCore(theCore); - - this.getServer().getLogger().info(String.format("%s - Version %s enabled - By %s", - this.getDescription().getName(), this.getDescription().getVersion(), getAuthors())); - getDataFolder().mkdirs(); - File debugLogFile = new File(getDataFolder(), "debug.log"); - try { - debugLogFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - debugLog = new DebugLog(this.getDescription().getName(), getDataFolder() + File.separator + "debug.log"); - debugLog.setTag(String.format("[%s-Debug]", this.getDescription().getName())); - - this.onPluginEnable(); - } - - /** - * Parse the Authors Array into a readable String with ',' and 'and'. - * - * @return The readable authors-{@link String} - */ - protected String getAuthors() { - String authors = ""; - List auths = this.getDescription().getAuthors(); - if (auths.size() == 0) { - return ""; - } - - if (auths.size() == 1) { - return auths.get(0); - } - - for (int i = 0; i < auths.size(); i++) { - if (i == this.getDescription().getAuthors().size() - 1) { - authors += " and " + this.getDescription().getAuthors().get(i); - } else { - authors += ", " + this.getDescription().getAuthors().get(i); - } - } - return authors.substring(2); - } - - /** - * Called when the plugin is enabled. - * @see #onEnable() - */ - protected abstract void onPluginEnable(); - - /** - * You can register commands here. - * @param handler The CommandHandler. - */ - protected abstract void registerCommands(CommandHandler handler); - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (!this.isEnabled()) { - sender.sendMessage("This plugin is Disabled!"); - return true; - } - - ArrayList allArgs = new ArrayList(args.length + 1); - allArgs.add(command.getName()); - allArgs.addAll(Arrays.asList(args)); - return this.getCore().getCommandHandler().locateAndRunCommand(sender, allArgs); - } - - @Override - public void log(Level level, String msg) { - int debugLevel = this.getCore().getMVConfig().getGlobalDebug(); - if ((level == Level.FINE && debugLevel >= 1) || (level == Level.FINER && debugLevel >= 2) - || (level == Level.FINEST && debugLevel >= 3)) { - debugLog.log(level, msg); - } else if (level != Level.FINE && level != Level.FINER && level != Level.FINEST) { - String message = new StringBuilder(getLogTag()).append(msg).toString(); - this.getServer().getLogger().log(level, message); - debugLog.log(level, message); - } - } - - private String getLogTag() { - if (logTag == null) - logTag = String.format("[%s]", this.getDescription().getName()); - return logTag; - } - - /** - * Sets the debug log-tag. - * @param tag The new tag. - */ - protected final void setDebugLogTag(String tag) { - this.debugLog.setTag(tag); - } - - @Override - public final String dumpVersionInfo(String buffer) { - throw new UnsupportedOperationException("This is gone."); - } - - @Override - public final MultiverseCore getCore() { - if (this.core == null) - throw new IllegalStateException("Core is null!"); - return this.core; - } - - @Override - public final void setCore(MultiverseCore core) { - this.core = core; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseWorld.java b/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseWorld.java deleted file mode 100644 index 07ba347f3..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/MultiverseWorld.java +++ /dev/null @@ -1,674 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.api; - -import com.onarandombox.MultiverseCore.enums.AllowedPortalType; -import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException; -import org.bukkit.ChatColor; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.WorldType; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.Permission; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * The API for a Multiverse Handled World. - */ -public interface MultiverseWorld { - /** - * Returns the Bukkit world object that this world describes. - * - * @return A {@link World} - */ - World getCBWorld(); - - /** - * Gets the name of this world. The name cannot be changed. - *

- * Note for plugin developers: Usually {@link #getAlias()} - * is what you want to use instead of this method. - * - * @return The name of the world as a String. - */ - String getName(); - - /** - * Gets the type of this world. As of 1.2 this will be: - * FLAT, NORMAL or VERSION_1_1 - *

- * This is not the generator. - * - * @return The Type of this world. - */ - WorldType getWorldType(); - - /** - * Gets the environment of this world. - * - * @return A {@link org.bukkit.World.Environment}. - */ - World.Environment getEnvironment(); - - /** - * Sets the environment of a world. - *

- * Note: This will ONLY take effect once the world is unloaded/reloaded. - * - * @param environment A {@link org.bukkit.World.Environment}. - */ - void setEnvironment(World.Environment environment); - - /** - * Gets the difficulty of this world. - * - * @return The difficulty of this world. - */ - Difficulty getDifficulty(); - - /** - * Sets the difficulty of this world and returns true if success. - * Valid string values are either an integer of difficulty(0-3) or - * the name that resides in the Bukkit enum, ex. {@code PEACEFUL} - * - * @param difficulty The difficulty to set the world to as a string. - * @return True if success, false if the provided string - * could not be translated to a difficulty. - * @deprecated Use {@link #setDifficulty(Difficulty)} or, if you have to - * pass a string, use {@link #setPropertyValue(String, String)} instead. - */ - @Deprecated - boolean setDifficulty(String difficulty); - - /** - * Sets the difficulty of this world and returns {@code true} on success. - * Valid string values are either an integer of difficulty(0-3) or - * the name that resides in the Bukkit enum, ex. PEACEFUL - * - * @param difficulty The new difficulty. - * @return True if success, false if the operation failed... for whatever reason. - */ - boolean setDifficulty(Difficulty difficulty); - - /** - * Gets the world seed of this world. - * - * @return The Long version of the seed. - */ - long getSeed(); - - /** - * Sets the seed of this world. - * - * @param seed A Long that is the seed. - */ - void setSeed(long seed); - - /** - * Gets the generator of this world. - * - * @return The name of the generator. - */ - String getGenerator(); - - /** - * Sets the generator of this world. - * - * @param generator The new generator's name. - */ - void setGenerator(String generator); - - /** - * Gets the help-message for a property. - * @param property The name of the property. - * @return The help-message. - * @throws PropertyDoesNotExistException Thrown if the property was not found. - */ - String getPropertyHelp(String property) throws PropertyDoesNotExistException; - - /** - * Gets a property as {@link String}. - * - * @param property The name of a world property to get. - * @return The string-representation of that property. - * @throws PropertyDoesNotExistException Thrown if the property was not found in the world. - */ - String getPropertyValue(String property) throws PropertyDoesNotExistException; - - /** - * Sets a property to a given value. - * - * @param property The name of a world property to set. - * @param value A value in string representation, it will be parsed to the correct type. - * @return True if the value was set, false if not. - * @throws PropertyDoesNotExistException Thrown if the property was not found in the world. - */ - boolean setPropertyValue(String property, String value) throws PropertyDoesNotExistException; - - /** - * Adds a value to the given property. The property must be a {@link com.onarandombox.MultiverseCore.enums.AddProperties}. - * - * @param property The name of a {@link com.onarandombox.MultiverseCore.enums.AddProperties} to add a value to. - * @param value A value in string representation, it will be parsed to the correct type. - * @return True if the value was added, false if not. - * @deprecated We changed the entire world-config-system. This is not compatible any more. - */ - @Deprecated - boolean addToVariable(String property, String value); - - /** - * Removes a value from the given property. The property must be a {@link com.onarandombox.MultiverseCore.enums.AddProperties}. - * - * @param property The name of a {@link com.onarandombox.MultiverseCore.enums.AddProperties} to remove a value - * from. - * @param value A value in string representation, it will be parsed to the correct type. - * @return True if the value was removed, false if not. - * @deprecated We changed the entire world-config-system. This is not compatible any more. - */ - @Deprecated - boolean removeFromVariable(String property, String value); - - /** - * Removes all values from the given property. The property must be a {@link com.onarandombox.MultiverseCore.enums.AddProperties}. - * - * @param property The name of a {@link com.onarandombox.MultiverseCore.enums.AddProperties} to clear. - * @return True if it was cleared, false if not. - * @deprecated We changed the entire world-config-system. This is not compatible any more. - */ - @Deprecated - boolean clearVariable(String property); - - /** - * Clears a list property (sets it to []). - * - * @param property The property to clear. - * @return True if success, false if fail. - * @deprecated We changed the entire world-config-system. This is not compatible any more. - */ - @Deprecated - boolean clearList(String property); - // end of old config stuff - - // permission stuff - /** - * Gets the lowercased name of the world. This method is required, since the permissables - * lowercase all permissions when recalculating. - *

- * Note: This also means if a user has worlds named: world and WORLD, that they can both - * exist, and both be teleported to independently, but their permissions **cannot** be - * uniqueified at this time. See bug report #. - * - * @return The lowercased name of the world. - */ - String getPermissibleName(); - - /** - * Gets the permission required to enter this world. - * - * @return The permission required to be exempt from charges to/from this world. - */ - Permission getAccessPermission(); - - /** - * Gets the permission required to be exempt when entering. - * - * @return The permission required to be exempt when entering. - */ - Permission getExemptPermission(); - // end of permission stuff - - /** - * Gets the alias of this world. - *

- * This alias allows users to have a world named "world" but show up in the list as "FernIsland" - * - * @return The alias of the world as a String. - */ - String getAlias(); - - /** - * Sets the alias of the world. - * - * @param alias A string that is the new alias. - */ - void setAlias(String alias); - - /** - * Gets the color that this world's name/alias will display as. - * - * @return The color of this world. - */ - ChatColor getColor(); - - /** - * Sets the color that this world's name/alias will display as. - * - * @param color A valid color name. - * @return True if the color was set, false if not. - */ - boolean setColor(String color); - - /** - * Gets the style that this world's name/alias will display as. - * - * @return The style of this world. {@code null} for "normal" style. - */ - ChatColor getStyle(); - - /** - * Sets the style that this world's name/alias will display as. - * - * @param style A valid style name. - * @return True if the style was set, false if not. - */ - boolean setStyle(String style); - - /** - * Tells you if someone entered a valid color. - * - * @param color A string that may translate to a color. - * @return True if it is a color, false if not. - * - * @deprecated This has been moved: {@link com.onarandombox.MultiverseCore.enums.EnglishChatColor#isValidAliasColor(String)} - */ - @Deprecated - boolean isValidAliasColor(String color); - - /** - * Returns a very nicely colored string (using Alias and Color if they are set). - * - * @return A nicely colored string. - */ - String getColoredWorldString(); - - // animals&monster stuff - /** - * Gets whether or not animals are allowed to spawn in this world. - * - * @return True if ANY animal can, false if no animals can spawn. - */ - boolean canAnimalsSpawn(); - - /** - * Sets whether or not animals can spawn. - * If there are values in {@link #getAnimalList()} and this is false, - * those animals become the exceptions, and will spawn - * - * @param allowAnimalSpawn True to allow spawning of monsters, false to prevent. - */ - void setAllowAnimalSpawn(boolean allowAnimalSpawn); - - /** - * Returns a list of animals. This list always negates the {@link #canAnimalsSpawn()} result. - * - * @return A list of animals that will spawn if {@link #canAnimalsSpawn()} is false. - */ - List getAnimalList(); - - /** - * Gets whether or not monsters are allowed to spawn in this world. - * - * @return True if ANY monster can, false if no monsters can spawn. - */ - boolean canMonstersSpawn(); - - /** - * Sets whether or not monsters can spawn. - * If there are values in {@link #getMonsterList()} and this is false, - * those monsters become the exceptions, and will spawn - * - * @param allowMonsterSpawn True to allow spawning of monsters, false to prevent. - */ - void setAllowMonsterSpawn(boolean allowMonsterSpawn); - - /** - * Returns a list of monsters. This list always negates the {@link #canMonstersSpawn()} result. - * - * @return A list of monsters that will spawn if {@link #canMonstersSpawn()} is false. - */ - List getMonsterList(); - // end of animal&monster stuff - - /** - * Gets whether or not PVP is enabled in this world in some form (fake or not). - * - * @return True if players can take damage from other players. - */ - boolean isPVPEnabled(); - - /** - * Turn pvp on or off. This setting is used to set the world's PVP mode. - * - * @param pvpMode True to enable PVP damage, false to disable it. - */ - void setPVPMode(boolean pvpMode); - - /** - * Turn pvp on or off. This setting is used to set the world's PVP mode, and thus relies on fakePVP - * - * @return True if this world has fakepvp on - * @deprecated This is deprecated. - */ - @Deprecated - boolean getFakePVP(); - - /** - * Gets whether or not this world will display in chat, mvw and mvl regardless if a user has the - * access permissions to go to this world. - * - * @return True if the world will be hidden, false if not. - */ - boolean isHidden(); - - /** - * Sets whether or not this world will display in chat, mvw and mvl regardless if a user has the - * access permissions to go to this world. - * - * @param hidden Set - */ - void setHidden(boolean hidden); - - /** - * Gets whether weather is enabled in this world. - * - * @return True if weather events will occur, false if not. - */ - boolean isWeatherEnabled(); - - /** - * Sets whether or not there will be weather events in a given world. - * If set to false, Multiverse will disable the weather in the world immediately. - * - * @param enableWeather True if weather events should occur in a world, false if not. - */ - void setEnableWeather(boolean enableWeather); - - /** - * Gets whether or not CraftBukkit is keeping the chunks for this world in memory. - * - * @return True if CraftBukkit is keeping spawn chunks in memory. - */ - boolean isKeepingSpawnInMemory(); - - /** - * If true, tells Craftbukkit to keep a worlds spawn chunks loaded in memory (default: true) - * If not, CraftBukkit will attempt to free memory when players have not used that world. - * This will not happen immediately. - * - * @param keepSpawnInMemory If true, CraftBukkit will keep the spawn chunks loaded in memory. - */ - void setKeepSpawnInMemory(boolean keepSpawnInMemory); - - /** - * Gets the spawn location of this world. - * - * @return The spawn location of this world. - */ - Location getSpawnLocation(); - - /** - * Sets the spawn location for a world. - * - * @param spawnLocation The spawn location for a world. - */ - void setSpawnLocation(Location spawnLocation); - - /** - * Gets whether or not the hunger level of players will go down in a world. - * - * @return True if it will go down, false if it will remain steady. - */ - boolean getHunger(); - - /** - * Sets whether or not the hunger level of players will go down in a world. - * - * @param hungerEnabled True if hunger will go down, false to keep it at - * the level they entered a world with. - */ - void setHunger(boolean hungerEnabled); - - /** - * Gets the GameMode of this world. - * - * @return The GameMode of this world. - */ - GameMode getGameMode(); - - /** - * Sets the game mode of this world. - * - * @param gameMode A valid game mode string (either - * an int ex. 0 or a string ex. creative). - * @return True if the game mode was successfully changed, false if not. - * @deprecated Use {@link #setGameMode(GameMode)} instead. If you have to - * pass a string, use {@link #setPropertyValue(String, String)}. - */ - @Deprecated - boolean setGameMode(String gameMode); - - /** - * Sets the game mode of this world. - * - * @param gameMode The new {@link GameMode}. - * @return True if the game mode was successfully changed, false if not. - */ - boolean setGameMode(GameMode gameMode); - - /** - * Gets the amount of currency it requires to enter this world. - * - * @return The amount it costs to enter this world. - */ - double getPrice(); - - /** - * Sets the price for entry to this world. - * You can think of this like an amount. - * The type can be set with {@link #setCurrency(Material)} - * - * @param price The Amount of money/item to enter the world. - */ - void setPrice(double price); - - /** - * Gets the type of currency that will be used when users enter this world. A value of null indicates a non-item - * based currency is used. - * - * @return The type of currency that will be used when users enter this world. - */ - @Nullable - Material getCurrency(); - - /** - * Sets the type of item that will be required given the price is not 0. - * Use a value of null to specify a non-item based currency. - * - * @param item The Type of currency that will be used when users enter this world. - */ - void setCurrency(@Nullable Material item); - - /** - * Gets the world players will respawn in if they die in this one. - * - * @return A world that exists on the server. - */ - World getRespawnToWorld(); - - /** - * Sets the world players will respawn in if they die in this one. - * Returns true upon success, false upon failure. - * - * @param respawnWorld The name of a world that exists on the server. - * @return True if respawnWorld existed, false if not. - */ - boolean setRespawnToWorld(String respawnWorld); - - /** - * Gets the scaling value of this world.Really only has an effect if you use - * Multiverse-NetherPortals. - * - * @return This world's non-negative, non-zero scale. - */ - double getScaling(); - - /** - * Sets the scale of this world. Really only has an effect if you use - * Multiverse-NetherPortals. - * - * @param scaling A scaling value, cannot be negative or 0. - * @return Whether the scale was set successfully. - */ - boolean setScaling(double scaling); - - /** - * Gets whether or not a world will auto-heal players if the difficulty is on peaceful. - * - * @return True if the world should heal (default), false if not. - */ - boolean getAutoHeal(); - - /** - * Sets whether or not a world will auto-heal players if the difficulty is on peaceful. - * - * @param heal True if the world will heal. - */ - void setAutoHeal(boolean heal); - - /** - * Gets whether or not Multiverse should auto-adjust the spawn for this world. - * - * @return True if Multiverse should adjust the spawn, false if not. - */ - boolean getAdjustSpawn(); - - /** - * Sets whether or not Multiverse should auto-adjust the spawn for this world. - * - * @param adjust True if multiverse should adjust the spawn, false if not. - */ - void setAdjustSpawn(boolean adjust); - - /** - * Gets whether or not Multiverse should auto-load this world. - * - * @return True if Multiverse should auto-load this world. - */ - boolean getAutoLoad(); - - /** - * Sets whether or not Multiverse should auto-load this world. - *

- * True is default. - * - * @param autoLoad True if multiverse should autoload this world the spawn, false if not. - */ - void setAutoLoad(boolean autoLoad); - - /** - * Gets whether or not a player who dies in this world will respawn in their - * bed or follow the normal respawn pattern. - * - * @return True if players dying in this world should respawn at their bed. - */ - boolean getBedRespawn(); - - /** - * Sets whether or not a player who dies in this world will respawn in their - * bed or follow the normal respawn pattern. - *

- * True is default. - * - * @param autoLoad True if players dying in this world respawn at their bed. - */ - void setBedRespawn(boolean autoLoad); - - /** - * Sets the player limit for this world after which players without an override - * permission node will not be allowed in. A value of -1 or less signifies no limit - * - * @param limit The new limit - */ - void setPlayerLimit(int limit); - - /** - * Gets the player limit for this world after which players without an override - * permission node will not be allowed in. A value of -1 or less signifies no limit - * - * @return The player limit - */ - int getPlayerLimit(); - - /** - * Same as {@link #getTime()}, but returns a string. - * @return The time as a short string: 12:34pm - */ - String getTime(); - - /** - * Sets the current time in a world. - *

- * This method will take the following formats: - * 11:37am - * 4:30p - * day(morning), night, noon, midnight - * - * @param timeAsString The formatted time to set the world to. - * @return True if the time was set, false if not. - */ - boolean setTime(String timeAsString); - - /** - * Sets The types of portals that are allowed in this world. - * - * @param type The type of portals allowed in this world. - */ - void allowPortalMaking(AllowedPortalType type); - - /** - * Gets which type(s) of portals are allowed to be constructed in this world. - * - * @return The type of portals that are allowed. - */ - AllowedPortalType getAllowedPortals(); - - // properties that are not "getter+setter" style - /** - * Gets a list of all the worlds that players CANNOT travel to from this world, - * regardless of their access permissions. - * - * @return A List of world names. - */ - List getWorldBlacklist(); - - /** - * Gets all the names of all properties that can be SET. - * - * @return All property names, with alternating colors. - */ - String getAllPropertyNames(); - - /** - * Whether or not players are allowed to fly in this world. - * - * @return True if players allowed to fly in this world. - */ - boolean getAllowFlight(); - - /** - * Sets whether or not players are allowed to fly in this world. - * - * @param allowFlight True to allow flight in this world. - */ - void setAllowFlight(final boolean allowFlight); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/SafeTTeleporter.java b/src/main/java/com/onarandombox/MultiverseCore/api/SafeTTeleporter.java deleted file mode 100644 index c6e7e76bf..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/SafeTTeleporter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; - -import com.onarandombox.MultiverseCore.enums.TeleportResult; - -/** - * Used to safely teleport people. - */ -public interface SafeTTeleporter extends Teleporter { - - /** - * Gets the next safe location around the given location. - * @param l A {@link Location}. - * @return A safe {@link Location}. - */ - Location getSafeLocation(Location l); - - /** - * Gets the next safe location around the given location. - * @param l A {@link Location}. - * @param tolerance The tolerance. - * @param radius The radius. - * @return A safe {@link Location}. - */ - Location getSafeLocation(Location l, int tolerance, int radius); - - /** - * Safely teleport the entity to the MVDestination. This will perform checks to see if the place is safe, and if - * it's not, will adjust the final destination accordingly. - * - * @param teleporter Person who performed the teleport command. - * @param teleportee Entity to teleport - * @param d Destination to teleport them to - * @return true for success, false for failure - */ - TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, MVDestination d); - - /** - * Safely teleport the entity to the Location. This may perform checks to - * see if the place is safe, and if - * it's not, will adjust the final destination accordingly. - * - * @param teleporter Person who issued the teleport command. - * @param teleportee Entity to teleport. - * @param location Location to teleport them to. - * @param safely Should the destination be checked for safety before teleport? - * @return true for success, false for failure. - */ - TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, Location location, - boolean safely); - - /** - * Returns a safe location for the entity to spawn at. - * - * @param e The entity to spawn - * @param d The MVDestination to take the entity to. - * @return A new location to spawn the entity at. - */ - Location getSafeLocation(Entity e, MVDestination d); - - /** - * Finds a portal-block next to the specified {@link Location}. - * @param l The {@link Location} - * @return The next portal-block's {@link Location}. - */ - Location findPortalBlockNextTo(Location l); - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/Teleporter.java b/src/main/java/com/onarandombox/MultiverseCore/api/Teleporter.java deleted file mode 100644 index aead219cd..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/Teleporter.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.enums.TeleportResult; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -public interface Teleporter { - TeleportResult teleport(CommandSender teleporter, Player teleportee, MVDestination destination); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/WorldPurger.java b/src/main/java/com/onarandombox/MultiverseCore/api/WorldPurger.java deleted file mode 100644 index b0ccad276..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/WorldPurger.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.onarandombox.MultiverseCore.api; - -import java.util.List; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; - -/** - * Used to remove animals from worlds that don't belong there. - */ -public interface WorldPurger { - /** - * Synchronizes the given worlds with their settings. - * - * @param worlds A list of {@link MultiverseWorld} - */ - void purgeWorlds(List worlds); - - /** - * Convenience method for {@link #purgeWorld(MultiverseWorld, java.util.List, boolean, boolean)} that takes the settings from the world-config. - * - * @param world The {@link MultiverseWorld}. - */ - void purgeWorld(MultiverseWorld world); - - /** - * Clear all animals/monsters that do not belong to a world according to the config. - * - * @param mvworld The {@link MultiverseWorld}. - * @param thingsToKill A {@link List} of animals/monsters to be killed. - * @param negateAnimals Whether the monsters in the list should be negated. - * @param negateMonsters Whether the animals in the list should be negated. - */ - void purgeWorld(MultiverseWorld mvworld, List thingsToKill, boolean negateAnimals, - boolean negateMonsters); - - /** - * Clear all animals/monsters that do not belong to a world according to the config. - * - * @param mvworld The {@link MultiverseWorld}. - * @param thingsToKill A {@link List} of animals/monsters to be killed. - * @param negateAnimals Whether the monsters in the list should be negated. - * @param negateMonsters Whether the animals in the list should be negated. - * @param sender The {@link CommandSender} that initiated the action. He will/should be notified. - */ - void purgeWorld(MultiverseWorld mvworld, List thingsToKill, boolean negateAnimals, - boolean negateMonsters, CommandSender sender); - - /** - * Determines whether the specified creature should be killed. - * - * @param e The creature. - * @param thingsToKill A {@link List} of animals/monsters to be killed. - * @param negateAnimals Whether the monsters in the list should be negated. - * @param negateMonsters Whether the animals in the list should be negated. - * @return {@code true} if the creature should be killed, otherwise {@code false}. - */ - boolean shouldWeKillThisCreature(Entity e, List thingsToKill, boolean negateAnimals, boolean negateMonsters); - - /** - * Determines whether the specified creature should be killed and automatically reads the params from a world object. - * - * @param w The world. - * @param e The creature. - * @return {@code true} if the creature should be killed, otherwise {@code false}. - */ - boolean shouldWeKillThisCreature(MultiverseWorld w, Entity e); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/api/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/api/package-info.java deleted file mode 100644 index c19d6732d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/api/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * The Multiverse-API, containing lots of interfaces that can be quite useful for other - * plugins when interacting with Multiverse. - */ -package com.onarandombox.MultiverseCore.api; diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/AnchorCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/AnchorCommand.java deleted file mode 100644 index 55d0b2cff..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/AnchorCommand.java +++ /dev/null @@ -1,162 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.List; - -/** - * Allows management of Anchor Destinations. - */ -public class AnchorCommand extends PaginatedCoreCommand { - - public AnchorCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Create, Delete and Manage Anchor Destinations."); - this.setCommandUsage("/mv anchor " + ChatColor.GREEN + "{name}" + ChatColor.GOLD + " [-d]"); - this.setArgRange(0, 2); - this.addKey("mv anchor"); - this.addKey("mv anchors"); - this.addKey("mvanchor"); - this.addKey("mvanchors"); - this.addCommandExample("/mv anchor " + ChatColor.GREEN + "awesomething"); - this.addCommandExample("/mv anchor " + ChatColor.GREEN + "otherthing"); - this.addCommandExample("/mv anchor " + ChatColor.GREEN + "awesomething " + ChatColor.RED + "-d"); - this.addCommandExample("/mv anchors "); - this.setPermission("multiverse.core.anchor.list", "Allows a player to list all anchors.", PermissionDefault.OP); - this.addAdditonalPermission(new Permission("multiverse.core.anchor.create", - "Allows a player to create anchors.", PermissionDefault.OP)); - this.addAdditonalPermission(new Permission("multiverse.core.anchor.delete", - "Allows a player to delete anchors.", PermissionDefault.OP)); - this.setItemsPerPage(8); // SUPPRESS CHECKSTYLE: MagicNumberCheck - } - - private List getFancyAnchorList(Player p) { - List anchorList = new ArrayList(); - ChatColor color = ChatColor.GREEN; - for (String anchor : this.plugin.getAnchorManager().getAnchors(p)) { - anchorList.add(color + anchor); - color = (color == ChatColor.GREEN) ? ChatColor.GOLD : ChatColor.GREEN; - } - return anchorList; - } - - private void showList(CommandSender sender, List args) { - if (!this.plugin.getMVPerms().hasPermission(sender, "multiverse.core.anchor.list", true)) { - sender.sendMessage(ChatColor.RED + "You don't have the permission to list anchors!"); - return; - } - - sender.sendMessage(ChatColor.LIGHT_PURPLE + "====[ Multiverse Anchor List ]===="); - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - - - FilterObject filterObject = this.getPageAndFilter(args); - - List availableAnchors = new ArrayList(this.getFancyAnchorList(p)); - if (filterObject.getFilter().length() > 0) { - availableAnchors = this.getFilteredItems(availableAnchors, filterObject.getFilter()); - if (availableAnchors.size() == 0) { - sender.sendMessage(ChatColor.RED + "Sorry... " + ChatColor.WHITE - + "No anchors matched your filter: " + ChatColor.AQUA + filterObject.getFilter()); - return; - } - } else { - if (availableAnchors.size() == 0) { - sender.sendMessage(ChatColor.RED + "Sorry... " + ChatColor.WHITE + "No anchors were defined."); - return; - } - } - - - if (!(sender instanceof Player)) { - for (String c : availableAnchors) { - sender.sendMessage(c); - } - return; - } - - int totalPages = (int) Math.ceil(availableAnchors.size() / (this.itemsPerPage + 0.0)); - - if (filterObject.getPage() > totalPages) { - filterObject.setPage(totalPages); - } else if (filterObject.getPage() < 1) { - filterObject.setPage(1); - } - - sender.sendMessage(ChatColor.AQUA + " Page " + filterObject.getPage() + " of " + totalPages); - - this.showPage(filterObject.getPage(), sender, availableAnchors); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (args.size() == 0) { - this.showList(sender, args); - return; - } - if (args.size() == 1 && (this.getPageAndFilter(args).getPage() != 1 || args.get(0).equals("1"))) { - this.showList(sender, args); - return; - } - if (args.size() == 2 && args.get(1).equalsIgnoreCase("-d")) { - if (!this.plugin.getMVPerms().hasPermission(sender, "multiverse.core.anchor.delete", true)) { - sender.sendMessage(ChatColor.RED + "You don't have the permission to delete anchors!"); - } else { - if (this.plugin.getAnchorManager().deleteAnchor(args.get(0))) { - sender.sendMessage("Anchor '" + args.get(0) + "' was successfully " + ChatColor.RED + "deleted!"); - } else { - sender.sendMessage("Anchor '" + args.get(0) + "' was " + ChatColor.RED + " NOT " + ChatColor.WHITE + "deleted!"); - } - } - return; - } - - if (!(sender instanceof Player)) { - sender.sendMessage("You must be a player to create Anchors."); - return; - } - - if (!this.plugin.getMVPerms().hasPermission(sender, "multiverse.core.anchor.create", true)) { - sender.sendMessage(ChatColor.RED + "You don't have the permission to create anchors!"); - } else { - Player player = (Player) sender; - if (this.plugin.getAnchorManager().saveAnchorLocation(args.get(0), player.getLocation())) { - sender.sendMessage("Anchor '" + args.get(0) + "' was successfully " + ChatColor.GREEN + "created!"); - } else { - sender.sendMessage("Anchor '" + args.get(0) + "' was " + ChatColor.RED + " NOT " + ChatColor.WHITE + "created!"); - } - } - } - - @Override - protected List getFilteredItems(List availableItems, String filter) { - List filtered = new ArrayList(); - for (String s : availableItems) { - if (s.matches("(?i).*" + filter + ".*")) { - filtered.add(s); - } - } - return filtered; - } - - @Override - protected String getItemText(String item) { - return item; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CheckCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CheckCommand.java deleted file mode 100644 index 7bc4ba480..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CheckCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.destination.InvalidDestination; -import com.onarandombox.MultiverseCore.utils.MVPermissions; -import com.onarandombox.MultiverseCore.utils.PlayerFinder; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; -/** - * Checks to see if a player can go to a destination. - */ -public class CheckCommand extends MultiverseCommand { - - public CheckCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Help you validate your multiverse settings"); - this.setCommandUsage("/mv check " + ChatColor.GREEN + "{PLAYER} {DESTINATION}"); - this.setArgRange(2, 2); - this.addKey("mv check"); - this.addKey("mvcheck"); - this.addCommandExample("/mv check " + ChatColor.GREEN + "fernferret " + ChatColor.LIGHT_PURPLE + "w:MyWorld"); - this.addCommandExample("/mv check " + ChatColor.GREEN + "Rigby90 " + ChatColor.LIGHT_PURPLE + "p:MyPortal"); - this.addCommandExample("/mv check " + ChatColor.GREEN + "lithium3141 " + ChatColor.LIGHT_PURPLE + "ow:WarpName"); - this.setPermission("multiverse.core.debug", "Checks to see if a player can go to a destination. Prints debug if false.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - Player p = PlayerFinder.get(args.get(0), sender); - if (p == null) { - sender.sendMessage("Could not find player " + ChatColor.GREEN + args.get(0)); - sender.sendMessage("Are they online?"); - return; - } - MVDestination dest = this.plugin.getDestFactory().getDestination(args.get(1)); - if (dest instanceof InvalidDestination) { - sender.sendMessage(String.format("You asked if '%s' could go to %s%s%s,", - args.get(0), ChatColor.GREEN, args.get(0), ChatColor.WHITE)); - sender.sendMessage("but I couldn't find a Destination of that name? Did you type it correctly?"); - return; - } - - MVPermissions perms = this.plugin.getMVPerms(); - perms.tellMeWhyICantDoThis(sender, p, dest); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CloneCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CloneCommand.java deleted file mode 100644 index 95a0bf146..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CloneCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import java.util.List; - -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; - -/** - * Creates a clone of a world. - */ -public class CloneCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public CloneCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Clone World"); - this.setCommandUsage("/mv clone" + ChatColor.GREEN + " {TARGET} {NAME}" + ChatColor.GOLD + " -g [GENERATOR[:ID]]"); - this.setArgRange(2, 4); // SUPPRESS CHECKSTYLE: MagicNumberCheck - this.addKey("mvclone"); - this.addKey("mvcl"); - this.addKey("mv cl"); - this.addKey("mv clone"); - this.addCommandExample("/mv clone " + ChatColor.GOLD + "world" + ChatColor.GREEN + " world_backup"); - this.addCommandExample("/mv clone " + ChatColor.GOLD + "skyblock_pristine" + ChatColor.GREEN + " skyblock"); - this.setPermission("multiverse.core.clone", "Clones a world.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - String oldName = args.get(0); - if (!this.worldManager.hasUnloadedWorld(oldName, true)) { - // If no world was found, we can't clone. - sender.sendMessage("Sorry, Multiverse doesn't know about world " + oldName + ", so we can't clone it!"); - sender.sendMessage("Check the " + ChatColor.GREEN + "/mv list" + ChatColor.WHITE + " command to verify it is listed."); - return; - } - if (this.plugin.getMVWorldManager().cloneWorld(oldName, args.get(1))) { - sender.sendMessage(ChatColor.GREEN + "World cloned!"); - } else { - sender.sendMessage(ChatColor.RED + "World could NOT be cloned!"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ConfigCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ConfigCommand.java deleted file mode 100644 index 55424a941..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ConfigCommand.java +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; -import java.util.Map; - -/** - * Allows you to set Global MV Variables. - */ -public class ConfigCommand extends MultiverseCommand { - public ConfigCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Configuration"); - this.setCommandUsage("/mv config " + ChatColor.GREEN + "{PROPERTY} {VALUE}"); - this.setArgRange(1, 2); - this.addKey("mv config"); - this.addKey("mvconfig"); - this.addKey("mv conf"); - this.addKey("mvconf"); - this.addCommandExample("/mv config show"); - this.addCommandExample("/mv config " + ChatColor.GREEN + "debug" + ChatColor.AQUA + " 3"); - this.addCommandExample("/mv config " + ChatColor.GREEN + "enforceaccess" + ChatColor.AQUA + " false"); - this.setPermission("multiverse.core.config", "Allows you to set Global MV Variables.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (args.size() <= 1) { - StringBuilder builder = new StringBuilder(); - Map serializedConfig = this.plugin.getMVConfig().serialize(); - for (Map.Entry entry : serializedConfig.entrySet()) { - builder.append(ChatColor.GREEN); - builder.append(entry.getKey()); - builder.append(ChatColor.WHITE).append(" = ").append(ChatColor.GOLD); - builder.append(entry.getValue().toString()); - builder.append(ChatColor.WHITE).append(", "); - } - String message = builder.toString(); - message = message.substring(0, message.length() - 2); - sender.sendMessage(message); - return; - } - if (!this.plugin.getMVConfig().setConfigProperty(args.get(0).toLowerCase(), args.get(1))) { - sender.sendMessage(String.format("%sSetting '%s' to '%s' failed!", ChatColor.RED, args.get(0).toLowerCase(), args.get(1))); - return; - } - - // special rule - if (args.get(0).equalsIgnoreCase("firstspawnworld")) { - // Don't forget to set the world! - this.plugin.getMVWorldManager().setFirstSpawnWorld(args.get(1)); - } - - if (this.plugin.saveMVConfigs()) { - sender.sendMessage(ChatColor.GREEN + "SUCCESS!" + ChatColor.WHITE + " Values were updated successfully!"); - this.plugin.loadConfigs(); - } else { - sender.sendMessage(ChatColor.RED + "FAIL!" + ChatColor.WHITE + " Check your console for details!"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ConfirmCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ConfirmCommand.java deleted file mode 100644 index 498a1431b..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ConfirmCommand.java +++ /dev/null @@ -1,39 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Confirms actions. - */ -public class ConfirmCommand extends MultiverseCommand { - - public ConfirmCommand(MultiverseCore plugin) { - super(plugin); - // Any command that is dangerous should require op - this.setName("Confirms a command that could destroy life, the universe and everything."); - this.setCommandUsage("/mv confirm"); - this.setArgRange(0, 0); - this.addKey("mvconfirm"); - this.addKey("mv confirm"); - this.addCommandExample("/mv confirm"); - this.setPermission("multiverse.core.confirm", "If you have not been prompted to use this, it will not do anything.", PermissionDefault.OP); - - } - - @Override - public void runCommand(CommandSender sender, List args) { - this.plugin.getCommandQueueManager().runQueuedCommand(sender); - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CoordCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CoordCommand.java deleted file mode 100644 index 485cc3701..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CoordCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.text.DecimalFormat; -import java.util.List; - -/** - * Returns detailed information on the Players where abouts. - */ -public class CoordCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public CoordCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Coordinates"); - this.setCommandUsage("/mv coord"); - this.setArgRange(0, 0); - this.addKey("mv coord"); - this.addKey("mvcoord"); - this.addKey("mvco"); - this.addCommandExample("/mv coord"); - this.setPermission("multiverse.core.coord", "Returns detailed information on the Players where abouts.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // Check if the command was sent from a Player. - if (sender instanceof Player) { - Player p = (Player) sender; - World world = p.getWorld(); - - if (!this.worldManager.isMVWorld(world.getName())) { - this.plugin.showNotMVWorldMessage(sender, world.getName()); - return; - } - - MultiverseWorld mvworld = this.worldManager.getMVWorld(world.getName()); - - p.sendMessage(ChatColor.AQUA + "--- Location Information ---"); - p.sendMessage(ChatColor.AQUA + "World: " + ChatColor.WHITE + world.getName()); - p.sendMessage(ChatColor.AQUA + "Alias: " + mvworld.getColoredWorldString()); - p.sendMessage(ChatColor.AQUA + "World Scale: " + ChatColor.WHITE + mvworld.getScaling()); - DecimalFormat df = new DecimalFormat(); - df.setMinimumFractionDigits(0); - df.setMaximumFractionDigits(2); - p.sendMessage(ChatColor.AQUA + "Coordinates: " + ChatColor.WHITE + plugin.getLocationManipulation().strCoords(p.getLocation())); - p.sendMessage(ChatColor.AQUA + "Direction: " + ChatColor.WHITE + plugin.getLocationManipulation().getDirection(p.getLocation())); - p.sendMessage(ChatColor.AQUA + "Block: " + ChatColor.WHITE + world.getBlockAt(p.getLocation()).getType()); - } else { - sender.sendMessage("This command needs to be used from a Player."); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java deleted file mode 100644 index 6d8f3833b..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java +++ /dev/null @@ -1,131 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.pneumaticraft.commandhandler.CommandHandler; -import org.bukkit.ChatColor; -import org.bukkit.World.Environment; -import org.bukkit.WorldType; -import org.bukkit.command.Command; -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; - -/** - * Creates a new world and loads it. - */ -public class CreateCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public CreateCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Create World"); - this.setCommandUsage(String.format("/mv create %s{NAME} {ENV} %s-s [SEED] -g [GENERATOR[:ID]] -t [WORLDTYPE] [-n] -a [true|false]", - ChatColor.GREEN, ChatColor.GOLD)); - this.setArgRange(2, 11); // SUPPRESS CHECKSTYLE: MagicNumberCheck - this.addKey("mvcreate"); - this.addKey("mvc"); - this.addKey("mv create"); - this.setPermission("multiverse.core.create", "Creates a new world and loads it.", PermissionDefault.OP); - this.addCommandExample("/mv create " + ChatColor.GOLD + "world" + ChatColor.GREEN + " normal"); - this.addCommandExample("/mv create " + ChatColor.GOLD + "lavaland" + ChatColor.RED + " nether"); - this.addCommandExample("/mv create " + ChatColor.GOLD + "starwars" + ChatColor.AQUA + " end"); - this.addCommandExample("/mv create " + ChatColor.GOLD + "flatroom" + ChatColor.GREEN + " normal" + ChatColor.AQUA + " -t flat"); - this.addCommandExample("/mv create " + ChatColor.GOLD + "gargamel" + ChatColor.GREEN + " normal" + ChatColor.DARK_AQUA + " -s gargamel"); - 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 args) { - 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); - String generator = CommandHandler.getFlag("-g", args); - boolean allowStructures = true; - String structureString = CommandHandler.getFlag("-a", args); - if (structureString != null) { - allowStructures = Boolean.parseBoolean(structureString); - } - String typeString = CommandHandler.getFlag("-t", args); - boolean useSpawnAdjust = true; - for (String s : args) { - if (s.equalsIgnoreCase("-n")) { - 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); - return; - } - - if (worldFile.exists()) { - sender.sendMessage(ChatColor.RED + "A Folder/World already exists with this name!"); - sender.sendMessage(ChatColor.RED + "If you are confident it is a world you can import with /mvimport"); - return; - } - - Environment environment = EnvironmentCommand.getEnvFromString(env); - if (environment == null) { - sender.sendMessage(ChatColor.RED + "That is not a valid environment."); - EnvironmentCommand.showEnvironments(sender); - return; - } - - // If they didn't specify a type, default to NORMAL - if (typeString == null) { - typeString = "NORMAL"; - } - WorldType type = EnvironmentCommand.getWorldTypeFromString(typeString); - if (type == null) { - sender.sendMessage(ChatColor.RED + "That is not a valid World Type."); - EnvironmentCommand.showWorldTypes(sender); - return; - } - // Determine if the generator is valid. #918 - if (generator != null) { - List genarray = new ArrayList(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. - sender.sendMessage("Invalid generator! '" + generator + "'. " + ChatColor.RED + "Aborting world creation."); - return; - } - } - Command.broadcastCommandMessage(sender, "Starting creation of world '" + worldName + "'..."); - - if (this.worldManager.addWorld(worldName, environment, seed, type, allowStructures, generator, useSpawnAdjust)) { - Command.broadcastCommandMessage(sender, "Complete!"); - } else { - Command.broadcastCommandMessage(sender, "FAILED."); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java deleted file mode 100644 index b2ccd99e4..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/DebugCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; -import java.util.logging.Level; - -/** - * Enables debug-information. - */ -public class DebugCommand extends MultiverseCommand { - - public DebugCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Turn Debug on/off?"); - this.setCommandUsage("/mv debug" + ChatColor.GOLD + " [1|2|3|off|silent]"); - this.setArgRange(0, 1); - this.addKey("mv debug"); - this.addKey("mv d"); - this.addKey("mvdebug"); - this.addCommandExample("/mv debug " + ChatColor.GOLD + "2"); - this.setPermission("multiverse.core.debug", "Spams the console a bunch.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (args.size() == 1) { - if (args.get(0).equalsIgnoreCase("off")) { - plugin.getMVConfig().setGlobalDebug(0); - } else { - try { - int debugLevel = Integer.parseInt(args.get(0)); - if (debugLevel > 3 || debugLevel < 0) { - throw new NumberFormatException(); - } - plugin.getMVConfig().setGlobalDebug(debugLevel); - } catch (NumberFormatException e) { - sender.sendMessage(ChatColor.RED + "Error" + ChatColor.WHITE - + " setting debug level. Please use a number 0-3 " + ChatColor.AQUA + "(3 being many many messages!)"); - } - } - plugin.saveMVConfigs(); - } - this.displayDebugMode(sender); - } - - private void displayDebugMode(CommandSender sender) { - final int debugLevel = plugin.getMVConfig().getGlobalDebug(); - if (debugLevel == 0) { - sender.sendMessage("Multiverse Debug mode is " + ChatColor.RED + "OFF"); - } else { - sender.sendMessage("Multiverse Debug mode is " + ChatColor.GREEN + debugLevel); - Logging.fine("Multiverse Debug ENABLED"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/DeleteCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/DeleteCommand.java deleted file mode 100644 index 603454935..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/DeleteCommand.java +++ /dev/null @@ -1,60 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.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; - -/** - * Deletes worlds. - */ -public class DeleteCommand extends MultiverseCommand { - - public DeleteCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Delete World"); - this.setCommandUsage("/mv delete" + ChatColor.GREEN + " {WORLD}"); - this.setArgRange(1, 1); - this.addKey("mvdelete"); - this.addKey("mv delete"); - this.addCommandExample("/mv delete " + ChatColor.GOLD + "MyWorld"); - this.setPermission("multiverse.core.delete", "Deletes a world on your server. " + ChatColor.RED + "PERMANENTLY.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - String worldName = args.get(0); - - 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)); - }; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/EnvironmentCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/EnvironmentCommand.java deleted file mode 100644 index f9ac88600..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/EnvironmentCommand.java +++ /dev/null @@ -1,118 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.World; -import org.bukkit.WorldType; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Lists valid known environments. - */ -public class EnvironmentCommand extends MultiverseCommand { - - public EnvironmentCommand(MultiverseCore plugin) { - super(plugin); - this.setName("List Environments"); - this.setCommandUsage("/mv env"); - this.setArgRange(0, 0); - this.addKey("mvenv"); - this.addKey("mv env"); - this.addKey("mv type"); - this.addKey("mv environment"); - this.addKey("mv environments"); - this.addCommandExample("/mv env"); - this.setPermission("multiverse.core.list.environments", "Lists valid known environments/world types.", PermissionDefault.OP); - } - - /** - * Shows all valid known environments to a {@link CommandSender}. - * - * @param sender The {@link CommandSender}. - */ - public static void showEnvironments(CommandSender sender) { - sender.sendMessage(ChatColor.YELLOW + "Valid Environments are:"); - sender.sendMessage(ChatColor.GREEN + "NORMAL"); - sender.sendMessage(ChatColor.RED + "NETHER"); - sender.sendMessage(ChatColor.AQUA + "END"); - } - /** - * Shows all valid known world types to a {@link CommandSender}. - * - * @param sender The {@link CommandSender}. - */ - public static void showWorldTypes(CommandSender sender) { - sender.sendMessage(ChatColor.YELLOW + "Valid World Types are:"); - sender.sendMessage(String.format("%sNORMAL%s, %sFLAT, %sLARGEBIOMES %sor %sVERSION_1_1", - ChatColor.GREEN, ChatColor.WHITE, ChatColor.AQUA, ChatColor.RED, ChatColor.WHITE, ChatColor.GOLD)); - } - - @Override - public void runCommand(CommandSender sender, List args) { - EnvironmentCommand.showEnvironments(sender); - EnvironmentCommand.showWorldTypes(sender); - } - - /** - * Converts a {@link String} into a {@link WorldType}. - * - * @param type The WorldType as a {@link String} - * @return The WorldType as a {@link WorldType} - */ - public static WorldType getWorldTypeFromString(String type) { - // Don't reference the enum directly as there aren't that many, and we can be more forgiving to users this way - if (type.equalsIgnoreCase("normal")) { - type = "NORMAL"; - } else if (type.equalsIgnoreCase("flat")) { - type = "FLAT"; - } else if (type.equalsIgnoreCase("largebiomes")) { - type = "LARGE_BIOMES"; - } else if (type.equalsIgnoreCase("amplified")) { - type = "AMPLIFIED"; - } - try { - // Now that we've converted a potentially unfriendly value - // to a friendly one, get it from the ENUM! - return WorldType.valueOf(type); - } catch (IllegalArgumentException e) { - return null; - } - } - - /** - * Converts a {@link String} into an {@link org.bukkit.World.Environment}. - * - * @param env The environment as {@link String} - * @return The environment as {@link org.bukkit.World.Environment} - */ - public static World.Environment getEnvFromString(String env) { - env = env.toUpperCase(); - // Don't reference the enum directly as there aren't that many, and we can be more forgiving to users this way - if (env.equalsIgnoreCase("HELL") || env.equalsIgnoreCase("NETHER")) - env = "NETHER"; - - if (env.equalsIgnoreCase("END") || env.equalsIgnoreCase("THEEND") || env.equalsIgnoreCase("STARWARS")) - env = "THE_END"; - - if (env.equalsIgnoreCase("NORMAL") || env.equalsIgnoreCase("WORLD")) - env = "NORMAL"; - - try { - // Now that we've converted a potentially unfriendly value - // to a friendly one, get it from the ENUM! - return World.Environment.valueOf(env); - } catch (IllegalArgumentException e) { - return null; - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/GameruleCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/GameruleCommand.java deleted file mode 100644 index ca0e1e076..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/GameruleCommand.java +++ /dev/null @@ -1,116 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.GameRule; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Allows management of Anchor Destinations. - */ -public class GameruleCommand extends MultiverseCommand { - - public GameruleCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Set a Minecraft Game Rule for a World."); - this.setCommandUsage("/mv gamerule " + ChatColor.GREEN + "{RULE} {VALUE}" + ChatColor.GOLD + " [WORLD]"); - this.setArgRange(2, 3); - this.addKey("mv gamerule"); - this.addKey("mv rule"); - this.addKey("mvgamerule"); - this.addKey("mvrule"); - this.addCommandExample("/mv gamerule " + ChatColor.GREEN + "doMobLoot false"); - this.addCommandExample("/mvrule " + ChatColor.GREEN + "keepInventory true " + ChatColor.RED + "world_nether"); - this.setPermission("multiverse.core.gamerule.set", "Allows a player to set a gamerule.", PermissionDefault.OP); - } - - - @Override - public void runCommand(CommandSender sender, List args) { - // We NEED a world from the command line - final Player p; - if (sender instanceof Player) { - p = (Player) sender; - } else { - p = null; - } - - if (args.size() == 2 && p == null) { - sender.sendMessage("From the command line, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - final GameRule gameRule = GameRule.getByName(args.get(0)); - final String value = args.get(1); - final World world; - if (args.size() == 2) { - world = p.getWorld(); - } else { - world = Bukkit.getWorld(args.get(2)); - if (world == null) { - sender.sendMessage(ChatColor.RED + "Failure!" + ChatColor.WHITE + " World " + ChatColor.AQUA + args.get(2) - + ChatColor.WHITE + " does not exist."); - return; - } - } - - if (gameRule == null) { - sender.sendMessage(ChatColor.RED + "Failure! " + ChatColor.AQUA + args.get(0) + ChatColor.WHITE - + " is not a valid gamerule."); - } else { - if (gameRule.getType() == Boolean.class) { - boolean booleanValue; - if (value.equalsIgnoreCase("true")) { - booleanValue = true; - } else if (value.equalsIgnoreCase("false")) { - booleanValue = false; - } else { - sender.sendMessage(getErrorMessage(gameRule.getName(), value) + "it can only be set to true or false."); - return; - } - - if (!world.setGameRule(gameRule, booleanValue)) { - sender.sendMessage(getErrorMessage(gameRule.getName(), value) + "something went wrong."); - return; - } - } else if (gameRule.getType() == Integer.class) { - try { - if (!world.setGameRule(gameRule, Integer.parseInt(value))) { - throw new NumberFormatException(); - } - } catch (NumberFormatException e) { - sender.sendMessage(getErrorMessage(gameRule.getName(), value) + "it can only be set to a positive integer."); - return; - } - } else { - sender.sendMessage(ChatColor.RED + "Failure!" + ChatColor.WHITE + " Gamerule " + ChatColor.AQUA + gameRule.getName() - + ChatColor.WHITE + " isn't supported yet, please let us know about it."); - return; - } - - sender.sendMessage(ChatColor.GREEN + "Success!" + ChatColor.WHITE + " Gamerule " + ChatColor.AQUA + gameRule.getName() - + ChatColor.WHITE + " was set to " + ChatColor.GREEN + value + ChatColor.WHITE + "."); - } - } - - private String getErrorMessage(String gameRule, String value) { - return ChatColor.RED + "Failure!" + ChatColor.WHITE + " Gamerule " + ChatColor.AQUA + gameRule - + ChatColor.WHITE + " could not be set to " + ChatColor.RED + value + ChatColor.WHITE + ", "; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/GamerulesCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/GamerulesCommand.java deleted file mode 100644 index 448b5c134..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/GamerulesCommand.java +++ /dev/null @@ -1,95 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.display.ColorAlternator; -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.GameRule; -import org.bukkit.World; -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. - */ -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.addKey("mv gamerules"); - this.addKey("mv rules"); - this.addKey("mvgamerules"); - this.addKey("mvrules"); - this.addCommandExample("/mv gamerules"); - this.addCommandExample("/mvrules " + ChatColor.RED + "world_nether"); - this.setPermission("multiverse.core.gamerule.list", "Allows a player to list gamerules.", PermissionDefault.OP); - } - - - @Override - public void runCommand(CommandSender sender, List args) { - // We NEED a world from the command line - final Player p; - if (sender instanceof Player) { - p = (Player) sender; - } else { - p = null; - } - - if (args.size() == 0 && p == null) { - sender.sendMessage("From the command line, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - final World world; - if (args.size() == 0) { - world = p.getWorld(); - } else { - world = Bukkit.getWorld(args.get(0)); - if (world == null) { - sender.sendMessage(ChatColor.RED + "Failure!" + ChatColor.WHITE + " World " + ChatColor.AQUA + args.get(0) - + ChatColor.WHITE + " does not exist."); - return; - } - } - - ContentDisplay.forContent(getGameRuleMap(world)) - .header("=== Gamerules for %s%s%s ===", ChatColor.AQUA, world.getName(), ChatColor.WHITE) - .colorTool(ColorAlternator.with(ChatColor.GREEN, ChatColor.GOLD)) - .setting(MapDisplaySettings.OPERATOR, ": ") - .show(sender); - } - - private Map getGameRuleMap(World world) { - Map gameRuleMap = new HashMap<>(); - for (GameRule rule : GameRule.values()) { - Object value = world.getGameRuleValue(rule); - if (value == null) { - gameRuleMap.put(rule.getName(), "null"); - continue; - } - gameRuleMap.put(rule.getName(), value); - } - return gameRuleMap; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java deleted file mode 100644 index 72d4379b6..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/GeneratorCommand.java +++ /dev/null @@ -1,60 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.plugin.Plugin; - -import java.util.ArrayList; -import java.util.List; - -/** - * Returns a list of loaded generator plugins. - */ -public class GeneratorCommand extends MultiverseCommand { - - public GeneratorCommand(MultiverseCore plugin) { - super(plugin); - this.setName("World Information"); - 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.setPermission("multiverse.core.generator", "Returns a list of Loaded Generator Plugins.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - Logging.info("PLEASE IGNORE the 'Plugin X does not contain any generators' message below!"); - Plugin[] plugins = this.plugin.getServer().getPluginManager().getPlugins(); - List generators = new ArrayList(); - for (Plugin p : plugins) { - if (p.isEnabled() && p.getDefaultWorldGenerator("world", "") != null) { - generators.add(p.getDescription().getName()); - } - } - sender.sendMessage(ChatColor.AQUA + "--- Loaded Generator Plugins ---"); - String loadedGens = ""; - boolean altColor = false; - for (String s : generators) { - loadedGens += (altColor ? ChatColor.YELLOW : ChatColor.WHITE) + s + " "; - altColor = !altColor; - } - if (loadedGens.length() == 0) { - loadedGens = ChatColor.RED + "No Generator Plugins found."; - } - sender.sendMessage(loadedGens); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/HelpCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/HelpCommand.java deleted file mode 100644 index 59a429e44..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/HelpCommand.java +++ /dev/null @@ -1,98 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.pneumaticraft.commandhandler.Command; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.List; - -/** - * Displays a nice help menu. - */ -public class HelpCommand extends PaginatedCoreCommand { - - public HelpCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Get Help with Multiverse"); - this.setCommandUsage("/mv " + ChatColor.GOLD + "[FILTER] [PAGE #]"); - this.setArgRange(0, 2); - this.addKey("mv"); - this.addKey("mvh"); - this.addKey("mvhelp"); - this.addKey("mv help"); - this.addKey("mvsearch"); - this.addKey("mv search"); - this.addCommandExample("/mv help ?"); - this.setPermission("multiverse.help", "Displays a nice help menu.", PermissionDefault.TRUE); - this.setItemsPerPage(7); // SUPPRESS CHECKSTYLE: MagicNumberCheck - } - - @Override - protected List getFilteredItems(List availableItems, String filter) { - String expression = "(?i).*" + cleanFilter(filter) + ".*"; - List filtered = new ArrayList(); - - for (Command c : availableItems) { - 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); - } - } - return filtered; - } - - @Override - protected String getItemText(Command item) { - return ChatColor.AQUA + item.getCommandUsage(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - sender.sendMessage(ChatColor.AQUA + "====[ Multiverse Help ]===="); - - FilterObject filterObject = this.getPageAndFilter(args); - - List availableCommands = new ArrayList(this.plugin.getCommandHandler().getCommands(sender)); - if (filterObject.getFilter().length() > 0) { - availableCommands = this.getFilteredItems(availableCommands, filterObject.getFilter()); - if (availableCommands.size() == 0) { - sender.sendMessage(ChatColor.RED + "Sorry... " + ChatColor.WHITE - + "No commands matched your filter: " + ChatColor.AQUA + filterObject.getFilter()); - return; - } - } - - if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.AQUA + " Add a '" + ChatColor.DARK_PURPLE + "?" + ChatColor.AQUA + "' after a command to see more about it."); - for (Command c : availableCommands) { - sender.sendMessage(ChatColor.AQUA + c.getCommandUsage()); - } - return; - } - - int totalPages = (int) Math.ceil(availableCommands.size() / (this.itemsPerPage + 0.0)); - - if (filterObject.getPage() > totalPages) { - filterObject.setPage(totalPages); - } - - sender.sendMessage(ChatColor.AQUA + " Page " + filterObject.getPage() + " of " + totalPages); - sender.sendMessage(ChatColor.AQUA + " Add a '" + ChatColor.DARK_PURPLE + "?" + ChatColor.AQUA + "' after a command to see more about it."); - - this.showPage(filterObject.getPage(), sender, availableCommands); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ImportCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ImportCommand.java deleted file mode 100644 index 7a52d5a15..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ImportCommand.java +++ /dev/null @@ -1,137 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.utils.WorldNameChecker; -import com.pneumaticraft.commandhandler.CommandHandler; -import org.bukkit.ChatColor; -import org.bukkit.World.Environment; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.io.File; -import java.util.Collection; -import java.util.List; - -/** - * Imports a new world of the specified type. - */ -public class ImportCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public ImportCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Import World"); - this.setCommandUsage("/mv import" + ChatColor.GREEN + " {NAME} {ENV}" + ChatColor.GOLD + " -g [GENERATOR[:ID]] [-n]"); - this.setArgRange(1, 5); // SUPPRESS CHECKSTYLE: MagicNumberCheck - this.addKey("mvimport"); - this.addKey("mvim"); - this.addKey("mv import"); - this.addCommandExample("/mv import " + ChatColor.GOLD + "gargamel" + ChatColor.GREEN + " normal"); - this.addCommandExample("/mv import " + ChatColor.GOLD + "hell_world" + ChatColor.GREEN + " nether"); - this.addCommandExample("To import a world that uses a generator, you'll need the generator:"); - this.addCommandExample("/mv import " + ChatColor.GOLD + "CleanRoom" + ChatColor.GREEN + " normal" + ChatColor.DARK_AQUA + " CleanRoomGenerator"); - this.setPermission("multiverse.core.import", "Imports a new world of the specified type.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - private String getPotentialWorldStrings() { - final Collection potentialWorlds = this.worldManager.getPotentialWorlds(); - StringBuilder worldList = new StringBuilder(); - ChatColor currColor = ChatColor.WHITE; - - for (String world : potentialWorlds) { - worldList.append(currColor).append(world).append(' '); - currColor = currColor == ChatColor.WHITE ? ChatColor.YELLOW : ChatColor.WHITE; - } - - return worldList.toString(); - } - - private String trimWorldName(String userInput) { - // Removes relative paths. - return userInput.replaceAll("^[./\\\\]+", ""); - } - - @Override - public void runCommand(CommandSender sender, List args) { - String worldName = trimWorldName(args.get(0)); - - if (worldName.toLowerCase().equals("--list") || worldName.toLowerCase().equals("-l")) { - String worldList = this.getPotentialWorldStrings(); - if (worldList.length() > 2) { - sender.sendMessage(ChatColor.AQUA + "====[ These look like worlds ]===="); - sender.sendMessage(worldList); - } else { - sender.sendMessage(ChatColor.RED + "No potential worlds found. Sorry!"); - } - return; - } - // Since we made an exception for the list, we have to make sure they have at least 2 params: - // Note the exception is --list, which is covered above. - if (args.size() == 1 || worldName.length() < 1) { - 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)) { - sender.sendMessage(ChatColor.GREEN + "Multiverse" + ChatColor.WHITE - + " already knows about '" + ChatColor.AQUA + worldName + ChatColor.WHITE + "'!"); - return; - } - - File worldFile = new File(this.plugin.getServer().getWorldContainer(), worldName); - - String generator = CommandHandler.getFlag("-g", args); - boolean useSpawnAdjust = true; - for (String s : args) { - if (s.equalsIgnoreCase("-n")) { - useSpawnAdjust = false; - } - } - - String env = args.get(1); - Environment environment = EnvironmentCommand.getEnvFromString(env); - if (environment == null) { - sender.sendMessage(ChatColor.RED + "That is not a valid environment."); - EnvironmentCommand.showEnvironments(sender); - return; - } - - if (!worldFile.exists()) { - sender.sendMessage(ChatColor.RED + "FAILED."); - 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)) { - sender.sendMessage(ChatColor.RED + "FAILED."); - sender.sendMessage(String.format("'%s' does not appear to be a world. It is lacking a .dat file.", - worldName)); - } else if (env == null) { - sender.sendMessage(ChatColor.RED + "FAILED."); - sender.sendMessage("That world environment did not exist."); - sender.sendMessage("For a list of available world types, type: " + ChatColor.AQUA + "/mvenv"); - } else { - Command.broadcastCommandMessage(sender, String.format("Starting import of world '%s'...", worldName)); - if (this.worldManager.addWorld(worldName, environment, null, null, null, generator, useSpawnAdjust)) - Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Complete!"); - else - Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed!"); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/InfoCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/InfoCommand.java deleted file mode 100644 index 701424e70..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/InfoCommand.java +++ /dev/null @@ -1,274 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.FancyText; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.utils.FancyColorScheme; -import com.onarandombox.MultiverseCore.utils.FancyHeader; -import com.onarandombox.MultiverseCore.utils.FancyMessage; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.List; - -// Will use when we can compile with JDK 6 -//import com.sun.xml.internal.ws.util.StringUtils; - -/** - * Returns detailed information about a world. - */ -public class InfoCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public InfoCommand(MultiverseCore plugin) { - super(plugin); - this.setName("World Information"); - this.setCommandUsage("/mv info" + ChatColor.GOLD + " [WORLD] [PAGE]"); - this.setArgRange(0, 2); - this.addKey("mvinfo"); - this.addKey("mvi"); - this.addKey("mv info"); - this.addCommandExample("/mv info " + ChatColor.GOLD + "1"); - this.addCommandExample("/mv info " + ChatColor.GOLD + "3"); - this.setPermission("multiverse.core.info", "Returns detailed information on the world.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // Check if the command was sent from a Player. - String worldName = ""; - int pageNum = 0; - - if (args.size() == 0) { - if (sender instanceof Player) { - Player p = (Player) sender; - worldName = p.getWorld().getName(); - } else { - sender.sendMessage("You must enter a" + ChatColor.LIGHT_PURPLE + " world" + ChatColor.WHITE + " from the console!"); - return; - } - } else if (args.size() == 1) { - if (this.worldManager.isMVWorld(args.get(0))) { - // then we have a world! - worldName = args.get(0); - } else if (this.worldManager.getUnloadedWorlds().contains(args.get(0))) { - sender.sendMessage("That world exists, but it is unloaded!"); - sender.sendMessage(String.format("You can load it with: %s/mv load %s", ChatColor.AQUA, args.get(0))); - return; - } else { - if (sender instanceof Player) { - Player p = (Player) sender; - worldName = p.getWorld().getName(); - try { - pageNum = Integer.parseInt(args.get(0)) - 1; - } catch (NumberFormatException e) { - sender.sendMessage("That world does not exist."); - return; - } - } else { - sender.sendMessage("You must enter a" + ChatColor.LIGHT_PURPLE + " world" + ChatColor.WHITE + " from the console!"); - return; - } - } - } else if (args.size() == 2) { - worldName = args.get(0); - try { - pageNum = Integer.parseInt(args.get(1)) - 1; - } catch (NumberFormatException e) { - pageNum = 0; - } - } - - if (this.worldManager.isMVWorld(worldName)) { - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - showPage(pageNum, sender, this.buildEntireCommand(this.worldManager.getMVWorld(worldName), p)); - } else if (this.worldManager.getUnloadedWorlds().contains(worldName)) { - sender.sendMessage("That world exists, but it is unloaded!"); - sender.sendMessage(String.format("You can load it with: %s/mv load %s", ChatColor.AQUA, worldName)); - } else if (this.plugin.getServer().getWorld(worldName) != null) { - sender.sendMessage("That world exists, but Multiverse does not know about it!"); - sender.sendMessage("You can import it with" + ChatColor.AQUA + "/mv import " + ChatColor.GREEN + worldName + ChatColor.LIGHT_PURPLE + "{ENV}"); - sender.sendMessage("For available environments type " + ChatColor.GREEN + "/mv env"); - } - } - - private List> buildEntireCommand(MultiverseWorld world, Player p) { - List message = new ArrayList(); - List> worldInfo = new ArrayList>(); - // Page 1 - FancyColorScheme colors = new FancyColorScheme(ChatColor.AQUA, ChatColor.AQUA, ChatColor.GOLD, ChatColor.WHITE); - message.add(new FancyHeader("General Info", colors)); - message.add(new FancyMessage("World Name: ", world.getName(), colors)); - message.add(new FancyMessage("World UID: ", world.getCBWorld().getUID().toString(), colors)); - message.add(new FancyMessage("World Alias: ", world.getColoredWorldString(), colors)); - message.add(new FancyMessage("Game Mode: ", world.getGameMode().toString(), colors)); - message.add(new FancyMessage("Difficulty: ", world.getDifficulty().toString(), colors)); - - //message.add(new FancyMessage("Game Mode: ", StringUtils.capitalize(world.getGameMode().toString()), colors)); - Location spawn = world.getSpawnLocation(); - message.add(new FancyMessage("Spawn Location: ", plugin.getLocationManipulation().strCoords(spawn), colors)); - message.add(new FancyMessage("World Scale: ", String.valueOf(world.getScaling()), colors)); - message.add(new FancyMessage("World Seed: ", String.valueOf(world.getSeed()), colors)); - if (world.getPrice() > 0) { - message.add(new FancyMessage("Price to enter this world: ", - plugin.getEconomist().formatPrice(world.getPrice(), world.getCurrency()), - colors)); - } else if (world.getPrice() < 0) { - message.add(new FancyMessage("Reward for entering this world: ", - plugin.getEconomist().formatPrice(-world.getPrice(), world.getCurrency()), - colors)); - } else { - message.add(new FancyMessage("Price to enter this world: ", ChatColor.GREEN + "FREE!", colors)); - } - - if (world.getRespawnToWorld() != null) { - MultiverseWorld respawn = this.worldManager.getMVWorld(world.getRespawnToWorld()); - if (respawn != null) { - message.add(new FancyMessage("Players will respawn in: ", respawn.getColoredWorldString(), colors)); - } else { - message.add(new FancyMessage("Players will respawn in: ", ChatColor.RED + "!!INVALID!!", colors)); - } - - } - - worldInfo.add(message); - // Page 2 - message = new ArrayList(); - message.add(new FancyHeader("More World Settings", colors)); - message.add(new FancyMessage("World Type: ", world.getWorldType().toString(), colors)); - message.add(new FancyMessage("Generator: ", world.getGenerator(), colors)); - message.add(new FancyMessage("Structures: ", world.getCBWorld().canGenerateStructures() + "", colors)); - message.add(new FancyMessage("Weather: ", world.isWeatherEnabled() + "", colors)); - message.add(new FancyMessage("Players will get hungry: ", world.getHunger() + "", colors)); - message.add(new FancyMessage("Keep spawn in memory: ", world.isKeepingSpawnInMemory() + "", colors)); - message.add(new FancyHeader("PVP Settings", colors)); - message.add(new FancyMessage("Multiverse Setting: ", world.isPVPEnabled() + "", colors)); - message.add(new FancyMessage("Bukkit Setting: ", world.getCBWorld().getPVP() + "", colors)); - worldInfo.add(message); - // Page 3 - message = new ArrayList(); - message.add(new FancyHeader("Monster Settings", colors)); - message.add(new FancyMessage("Multiverse Setting: ", world.canMonstersSpawn() + "", colors)); - message.add(new FancyMessage("Bukkit Setting: ", world.getCBWorld().getAllowMonsters() + "", colors)); - if (world.getMonsterList().size() > 0) { - if (world.canMonstersSpawn()) { - message.add(new FancyMessage("Monsters that" + ChatColor.RED + " CAN NOT " - + ChatColor.GREEN + "spawn: ", toCommaSeperated(world.getMonsterList()), colors)); - } else { - message.add(new FancyMessage("Monsters that" + ChatColor.GREEN + " CAN SPAWN: ", toCommaSeperated(world.getMonsterList()), colors)); - } - } else { - message.add(new FancyMessage("Monsters that CAN spawn: ", world.canMonstersSpawn() ? "ALL" : "NONE", colors)); - } - worldInfo.add(message); - - // Page 4 - message = new ArrayList(); - message.add(new FancyHeader("Animal Settings", colors)); - message.add(new FancyMessage("Multiverse Setting: ", world.canAnimalsSpawn() + "", colors)); - message.add(new FancyMessage("Bukkit Setting: ", world.getCBWorld().getAllowAnimals() + "", colors)); - if (world.getAnimalList().size() > 0) { - if (world.canAnimalsSpawn()) { - message.add(new FancyMessage("Animals that" + ChatColor.RED + " CAN NOT " - + ChatColor.GREEN + "spawn: ", toCommaSeperated(world.getAnimalList()), colors)); - } else { - message.add(new FancyMessage("Animals that" + ChatColor.GREEN + " CAN SPAWN: ", toCommaSeperated(world.getAnimalList()), colors)); - } - } else { - message.add(new FancyMessage("Animals that CAN spawn: ", world.canAnimalsSpawn() ? "ALL" : "NONE", colors)); - } - worldInfo.add(message); - - return worldInfo; - } - - private static String toCommaSeperated(List list) { - if (list == null || list.size() == 0) { - return ""; - } - if (list.size() == 1) { - return list.get(0); - } - String result = list.get(0); - - for (int i = 1; i < list.size() - 1; i++) { - result += ", " + list.get(i); - } - result += " and " + list.get(list.size() - 1); - return result; - } - - /** - * Gets a "positive" or "negative" {@link ChatColor}. - * - * @param positive Whether this {@link ChatColor} should be "positive". - * @return The {@link ChatColor}. - */ - protected ChatColor getChatColor(boolean positive) { - return positive ? ChatColor.GREEN : ChatColor.RED; - } - - private static void showPage(int page, CommandSender sender, List> doc) { - page = page < 0 ? 0 : page; - page = page > doc.size() - 1 ? doc.size() - 1 : page; - boolean altColor = false; - boolean appendedPageNum = false; - if (sender instanceof Player) { - List list = doc.get(page); - for (FancyText fancyT : list) { - if (fancyT instanceof FancyMessage) { - FancyMessage text = (FancyMessage) fancyT; - text.setAltColor(altColor); - altColor = !altColor; - sender.sendMessage(text.getFancyText()); - } else if (fancyT instanceof FancyHeader) { - FancyHeader text = (FancyHeader) fancyT; - if (!appendedPageNum) { - text.appendText(ChatColor.DARK_PURPLE + " [ Page " + (page + 1) + " of " + doc.size() + " ]"); - appendedPageNum = true; - } - sender.sendMessage(text.getFancyText()); - altColor = false; - } - } - - } else { - for (List list : doc) { - for (FancyText fancyT : list) { - if (fancyT instanceof FancyMessage) { - FancyMessage text = (FancyMessage) fancyT; - text.setAltColor(altColor); - altColor = !altColor; - sender.sendMessage(text.getFancyText()); - } else { - FancyText text = fancyT; - if (appendedPageNum) { - sender.sendMessage(" "); - } else { - appendedPageNum = true; - } - sender.sendMessage(text.getFancyText()); - altColor = false; - } - } - } - } - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ListCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ListCommand.java deleted file mode 100644 index db8ee1a45..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ListCommand.java +++ /dev/null @@ -1,119 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.display.ColorAlternator; -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.ContentFilter; -import com.onarandombox.MultiverseCore.display.DisplayHandlers; -import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings; -import org.bukkit.ChatColor; -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.Collection; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Displays a listing of all worlds that a player can enter. - */ -public class ListCommand extends MultiverseCommand { - - public ListCommand(MultiverseCore plugin) { - super(plugin); - this.setName("World Listing"); - 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); - } - - @Override - public void runCommand(CommandSender sender, List args) { - ContentFilter filter = ContentFilter.DEFAULT; - int page = 1; - - // Either page or filter. - if (args.size() == 1) { - try { - page = Integer.parseInt(args.get(0)); - } catch (NumberFormatException ignore) { - filter = new ContentFilter(args.get(0)); - } - } - - // Filter then page. - if (args.size() == 2) { - filter = new ContentFilter(args.get(0)); - try { - page = Integer.parseInt(args.get(1)); - } catch (NumberFormatException ignore) { - sender.sendMessage(ChatColor.RED + args.get(1) + " is not valid number!"); - } - } - - ContentDisplay.forContent(getListContents(sender)) - .header("%s====[ Multiverse World List ]====", ChatColor.GOLD) - .displayHandler(DisplayHandlers.PAGE_LIST) - .colorTool(ColorAlternator.with(ChatColor.AQUA, ChatColor.GOLD)) - .filter(filter) - .setting(PagedDisplaySettings.SHOW_PAGE, page) - .show(sender); - } - - private Collection getListContents(@NotNull CommandSender sender) { - Player player = (sender instanceof Player) ? (Player) sender : null; - - List worldList = this.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())) - .collect(Collectors.toList()); - - this.plugin.getMVWorldManager().getUnloadedWorlds().stream() - .filter(world -> plugin.getMVPerms().hasPermission(sender, "multiverse.access." + world, true)) - .map(world -> ChatColor.GRAY + world + " - UNLOADED") - .forEach(worldList::add); - - return worldList; - } - - private boolean canSeeWorld(Player player, MultiverseWorld world) { - return !world.isHidden() - || player == null - || this.plugin.getMVPerms().hasPermission(player, "multiverse.core.modify", true); - } - - 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(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/LoadCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/LoadCommand.java deleted file mode 100644 index 4a96ef36c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/LoadCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Loads a world into Multiverse. - */ -public class LoadCommand extends MultiverseCommand { - - public LoadCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Load World"); - this.setCommandUsage("/mv load" + ChatColor.GREEN + " {WORLD}"); - this.setArgRange(1, 1); - this.addKey("mvload"); - this.addKey("mv load"); - this.addCommandExample("/mv load " + ChatColor.GREEN + "MyUnloadedWorld"); - this.setPermission("multiverse.core.load", "Loads a world into Multiverse.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (this.plugin.getMVWorldManager().loadWorld(args.get(0))) { - Command.broadcastCommandMessage(sender, "Loaded world '" + args.get(0) + "'!"); - } else { - sender.sendMessage("Error trying to load world '" + args.get(0) + "'!"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyAddCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyAddCommand.java deleted file mode 100644 index 1175d3408..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyAddCommand.java +++ /dev/null @@ -1,95 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.enums.Action; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -// This will contain all the properties that support the ADD/REMOVE -// Anything not in here will only support the SET action - -/** - * Used to modify various aspects of worlds. - */ -public class ModifyAddCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public ModifyAddCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Modify a World (Add a value)"); - this.setCommandUsage("/mv modify " + ChatColor.GREEN + "add {VALUE} {PROPERTY}" + ChatColor.GOLD + " [WORLD] "); - this.setArgRange(2, 3); - this.addKey("mvm add"); - this.addKey("mvmadd"); - this.addKey("mv modify add"); - this.addKey("mvmodify add"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "add " + ChatColor.GREEN + "sheep " + ChatColor.RED + "animals"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "add " + ChatColor.GREEN + "creeper " + ChatColor.RED + "monsters"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "add " + ChatColor.GREEN + "MyWorld " + ChatColor.RED + "worldblacklist"); - this.setPermission("multiverse.core.modify.add", "Modify various aspects of worlds. See the help wiki for how to use this command properly. " - + "If you do not include a world, the current world will be used.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // We NEED a world from the command line - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - - if (args.size() == 2 && p == null) { - sender.sendMessage(ChatColor.RED + "From the console, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - MultiverseWorld world; - String value = args.get(0); - String property = args.get(1); - - if (args.size() == 2) { - world = this.worldManager.getMVWorld(p.getWorld().getName()); - } else { - world = this.worldManager.getMVWorld(args.get(2)); - } - - if (world == null) { - sender.sendMessage("That world does not exist!"); - return; - } - - if (!ModifyCommand.validateAction(Action.Add, property)) { - sender.sendMessage("Sorry, you can't ADD to " + property); - sender.sendMessage("Please visit our Github Wiki for more information: https://goo.gl/OMGwzx"); - return; - } - - // TODO fix this - if (world.addToVariable(property, value)) { - sender.sendMessage(ChatColor.GREEN + "Success! " + ChatColor.AQUA - + value + ChatColor.WHITE + " was " + ChatColor.GREEN + "added to " + ChatColor.GREEN + property); - if (!plugin.saveWorldConfig()) { - sender.sendMessage(ChatColor.RED + "There was an issue saving worlds.yml! Your changes will only be temporary!"); - } - } else { - sender.sendMessage(value + " could not be added to " + property); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyClearCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyClearCommand.java deleted file mode 100644 index 788b7452b..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyClearCommand.java +++ /dev/null @@ -1,92 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.enums.Action; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Removes all values from a world-property. - */ -public class ModifyClearCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public ModifyClearCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Modify a World (Clear a property)"); - this.setCommandUsage("/mv modify" + ChatColor.GREEN + " clear {PROPERTY}" + ChatColor.GOLD + " [WORLD]"); - this.setArgRange(1, 2); - this.addKey("mvm clear"); - this.addKey("mvmclear"); - this.addKey("mv modify clear"); - this.addKey("mvmodify clear"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "clear " + ChatColor.RED + "animals"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "clear " + ChatColor.RED + "monsters"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "clear " + ChatColor.RED + "worldblacklist"); - this.setPermission("multiverse.core.modify.clear", - "Removes all values from a property. This will work on properties that contain lists.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // We NEED a world from the command line - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - if (args.size() == 1 && p == null) { - sender.sendMessage(ChatColor.RED + "From the console, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - MultiverseWorld world; - String property = args.get(0); - - if (args.size() == 1) { - world = this.worldManager.getMVWorld(p.getWorld().getName()); - } else { - world = this.worldManager.getMVWorld(args.get(1)); - } - - if (world == null) { - sender.sendMessage("That world does not exist!"); - return; - } - - if (!ModifyCommand.validateAction(Action.Clear, property)) { - sender.sendMessage("Sorry, you can't use CLEAR with " + property); - sender.sendMessage("Please visit our Github Wiki for more information: https://goo.gl/q1h01S"); - return; - } - // TODO fix this - if (world.clearList(property)) { - sender.sendMessage(property + " was cleared. It contains 0 values now."); - sender.sendMessage(ChatColor.GREEN + "Success! " + ChatColor.AQUA + property + ChatColor.WHITE + " was " - + ChatColor.GREEN + "CLEARED" + ChatColor.WHITE + ". It contains " + ChatColor.LIGHT_PURPLE + "0" + ChatColor.WHITE + " values now."); - if (!plugin.saveWorldConfig()) { - sender.sendMessage(ChatColor.RED + "There was an issue saving worlds.yml! Your changes will only be temporary!"); - } - } else { - sender.sendMessage(ChatColor.RED + "Error: " + ChatColor.GOLD + property - + ChatColor.WHITE + " was " + ChatColor.GOLD + "NOT" + ChatColor.WHITE + " cleared."); - } - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyCommand.java deleted file mode 100644 index d8c74ac3d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyCommand.java +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.enums.Action; -import com.onarandombox.MultiverseCore.enums.AddProperties; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Used to modify various aspects of worlds. - */ -public class ModifyCommand extends MultiverseCommand { - - public ModifyCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Modify a World"); - this.setCommandUsage("/mv modify" + ChatColor.GREEN + " {set|add|remove|clear} ..."); - this.setArgRange(2, 3); - this.addKey("mvm"); - this.addKey("mvmodify"); - this.addKey("mv modify"); - Map children = new HashMap(); - children.put("multiverse.core.modify.add", true); - children.put("multiverse.core.modify.modify", true); - children.put("multiverse.core.modify.clear", true); - children.put("multiverse.core.modify.remove", true); - Permission modify = new Permission("multiverse.core.modify", - "Modify various aspects of worlds. It requires add/set/clear/remove. See the examples below", PermissionDefault.OP, children); - this.addCommandExample(ChatColor.AQUA + "/mv modify set ?"); - this.addCommandExample(ChatColor.GREEN + "/mv modify add ?"); - this.addCommandExample(ChatColor.BLUE + "/mv modify clear ?"); - this.addCommandExample(ChatColor.RED + "/mv modify remove ?"); - this.setPermission(modify); - } - - /** - * Validates the specified action. - * - * @param action The {@link Action}. - * @param property The property. - * @return Whether this action is valid. - */ - protected static boolean validateAction(Action action, String property) { - if (action != Action.Set) { - try { - AddProperties.valueOf(property); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } - return false; - } - - @Override - public void runCommand(CommandSender sender, List args) { - // This is just a place holder. The real commands are in: - // ModifyAddCommand - // ModifyRemoveCommand - // ModifySetCommand - // ModifyClearCommand - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyRemoveCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyRemoveCommand.java deleted file mode 100644 index f714be763..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifyRemoveCommand.java +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.enums.Action; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Removes values from a world-property. - */ -public class ModifyRemoveCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public ModifyRemoveCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Modify a World (Remove a value)"); - this.setCommandUsage("/mv modify" + ChatColor.GREEN + " remove {PROPERTY} {VALUE}" + ChatColor.GOLD + " [WORLD]"); - this.setArgRange(2, 3); - this.addKey("mvm remove"); - this.addKey("mvmremove"); - this.addKey("mv modify remove"); - this.addKey("mvmodify remove"); - this.addKey("mvm delete"); - this.addKey("mvmdelete"); - this.addKey("mv modify delete"); - this.addKey("mvmodify delete"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "remove " + ChatColor.GREEN + "sheep " + ChatColor.RED + "animals"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "remove " + ChatColor.GREEN + "creeper " + ChatColor.RED + "monsters"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "remove " + ChatColor.GREEN + "MyWorld " + ChatColor.RED + "worldblacklist"); - this.setPermission("multiverse.core.modify.remove", "Modify various aspects of worlds. See the help wiki for how to use this command properly. " - + "If you do not include a world, the current world will be used.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // We NEED a world from the command line - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - - if (args.size() == 2 && p == null) { - sender.sendMessage(ChatColor.RED + "From the console, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - MultiverseWorld world; - String value = args.get(0); - String property = args.get(1); - - if (args.size() == 2) { - world = this.worldManager.getMVWorld(p.getWorld().getName()); - } else { - world = this.worldManager.getMVWorld(args.get(2)); - } - - if (world == null) { - sender.sendMessage("That world does not exist!"); - return; - } - - if (!ModifyCommand.validateAction(Action.Remove, property)) { - sender.sendMessage("Sorry, you can't REMOVE anything from" + property); - sender.sendMessage("Please visit our Github Wiki for more information: https://goo.gl/OMGwzx"); - return; - } - // TODO fix this - if (world.removeFromVariable(property, value)) { - sender.sendMessage(ChatColor.GREEN + "Success! " + ChatColor.AQUA + value + ChatColor.WHITE - + " was " + ChatColor.RED + "removed from " + ChatColor.GREEN + property); - if (!plugin.saveWorldConfig()) { - sender.sendMessage(ChatColor.RED + "There was an issue saving worlds.yml! Your changes will only be temporary!"); - } - } else { - sender.sendMessage(ChatColor.RED + "There was an error removing " + ChatColor.GRAY - + value + ChatColor.WHITE + " from " + ChatColor.GOLD + property); - } - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifySetCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ModifySetCommand.java deleted file mode 100644 index 54bf8a11e..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ModifySetCommand.java +++ /dev/null @@ -1,125 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.enums.EnglishChatColor; -import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Used to set world-properties. - */ -public class ModifySetCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public ModifySetCommand(MultiverseCore plugin) { - super(plugin); - this.worldManager = this.plugin.getMVWorldManager(); - this.setName("Modify a World (Set a value)"); - this.setCommandUsage("/mv modify" + ChatColor.GREEN + " set {PROPERTY} {VALUE}" + ChatColor.GOLD + " [WORLD]"); - this.setArgRange(1, 3); - this.addKey("mvm set"); - this.addKey("mvmset"); - this.addKey("mv modify set"); - this.addKey("mvmodify set"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "mode " + ChatColor.RED + "creative"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "animals " + ChatColor.RED + "false"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "monsters " + ChatColor.RED + "false"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "alias " + ChatColor.RED + "MyWorld"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "color " + ChatColor.RED + "green"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "curr " + ChatColor.RED + "3"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "price " + ChatColor.RED + "5"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "scale " + ChatColor.RED + "1.2"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "memory " + ChatColor.RED + "true"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "diff " + ChatColor.RED + "hard"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "hunger " + ChatColor.RED + "false"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "hidden " + ChatColor.RED + "true"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "pvp " + ChatColor.RED + "false"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "heal " + ChatColor.RED + "true"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "adjustspawn " + ChatColor.RED + "false"); - this.addCommandExample("/mvm " + ChatColor.GOLD + "set " + ChatColor.GREEN + "spawn"); - this.setPermission("multiverse.core.modify.set", "Modify various aspects of worlds. See the help wiki for how to use this command properly. " - + "If you do not include a world, the current world will be used.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // Special case for spawn: - if (args.size() == 1) { - if (!(sender instanceof Player)) { - sender.sendMessage("You must be a player to set the" + ChatColor.GREEN + " spawn"); - return; - } - if (args.get(0).equalsIgnoreCase("spawn")) { - SetSpawnCommand c = new SetSpawnCommand(this.plugin); - c.setWorldSpawn(sender); - - } else { - sender.sendMessage("Spawn is the only param with no" + ChatColor.GREEN + " VALUE"); - sender.sendMessage("Type " + ChatColor.GREEN + "/mv modify ?" + ChatColor.WHITE + " For help."); - } - return; - } - // We NEED a world from the command line - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - - if (args.size() == 2 && p == null) { - sender.sendMessage("From the command line, WORLD is required."); - sender.sendMessage(this.getCommandDesc()); - sender.sendMessage(this.getCommandUsage()); - sender.sendMessage("Nothing changed."); - return; - } - - MultiverseWorld world; - String value = args.get(1); - String property = args.get(0); - - if (args.size() == 2) { - world = this.worldManager.getMVWorld(p.getWorld().getName()); - } else { - world = this.worldManager.getMVWorld(args.get(2)); - } - - if (world == null) { - sender.sendMessage("That world does not exist!"); - return; - } - - if ((property.equalsIgnoreCase("aliascolor") || property.equalsIgnoreCase("color")) && !EnglishChatColor.isValidAliasColor(value)) { - sender.sendMessage(value + " is not a valid color. Please pick one of the following:"); - sender.sendMessage(EnglishChatColor.getAllColors()); - return; - } - try { - if (world.setPropertyValue(property, value)) { - sender.sendMessage(ChatColor.GREEN + "Success!" + ChatColor.WHITE + " Property " + ChatColor.AQUA + property - + ChatColor.WHITE + " was set to " + ChatColor.GREEN + value); - if (!plugin.saveWorldConfig()) { - sender.sendMessage(ChatColor.RED + "There was an issue saving worlds.yml! Your changes will only be temporary!"); - } - } else { - sender.sendMessage(ChatColor.RED + world.getPropertyHelp(property)); - } - } catch (PropertyDoesNotExistException e) { - sender.sendMessage(ChatColor.RED + "Sorry, You can't set: '" + ChatColor.GRAY + property + ChatColor.RED + "'"); - sender.sendMessage("Valid world-properties: " + world.getAllPropertyNames()); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java deleted file mode 100644 index ae54a9b8f..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/MultiverseCommand.java +++ /dev/null @@ -1,40 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseMessaging; -import com.pneumaticraft.commandhandler.Command; -import org.bukkit.command.CommandSender; - -import java.util.List; - -/** - * A generic Multiverse-command. - */ -public abstract class MultiverseCommand extends Command { - - /** - * The reference to the core. - */ - protected MultiverseCore plugin; - /** - * The reference to {@link MultiverseMessaging}. - */ - protected MultiverseMessaging messaging; - - public MultiverseCommand(MultiverseCore plugin) { - super(plugin); - this.plugin = plugin; - this.messaging = this.plugin.getMessaging(); - } - - @Override - public abstract void runCommand(CommandSender sender, List args); - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCommand.java deleted file mode 100644 index 2dd11a2aa..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCommand.java +++ /dev/null @@ -1,177 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.pneumaticraft.commandhandler.Command; -import org.bukkit.command.CommandSender; -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 The type of items on the page. - */ -public abstract class PaginatedCommand 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. - */ - protected int itemsPerPage = DEFAULT_ITEMS_PER_PAGE; - - public PaginatedCommand(JavaPlugin plugin) { - super(plugin); - } - - /** - * Set the number of items per page. - * - * @param items The new number of items per page. - */ - protected void setItemsPerPage(int items) { - itemsPerPage = items; - } - - /** - * 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 getFilteredItems(List 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. - * - * @param list The {@link List} of strings. - * @return A single {@link String}. - */ - protected String stitchThisString(List list) { - StringBuilder builder = new StringBuilder(); - for (String s : list) { - builder.append(s); - builder.append(' '); - } - return builder.toString(); - } - - /** - * Shows a page. - * - * @param page The number of the page to show. - * @param sender The {@link CommandSender} that wants to see the page. - * @param cmds The items that should be displayed on the page. - */ - protected void showPage(int page, CommandSender sender, List cmds) { - // Ensure the page is at least 1. - page = (page <= 0) ? 1 : page; - int start = (page - 1) * itemsPerPage; - int end = start + itemsPerPage; - - for (int i = start; i < end; i++) { - // For consistancy, print some extra lines if it's a player: - if (i < cmds.size()) { - sender.sendMessage(this.getItemText(cmds.get(i))); - } else if (sender instanceof Player) { - sender.sendMessage(" "); - } - } - } - - /** - * Converts an item into a string. - * - * @param item The item. - * @return A {@link String}. - */ - protected abstract String getItemText(T item); - - /** - * Constructs a {@link FilterObject} from a {@link List} of arguments. - * - * @param args The {@link List} of arguments. - * @return The {@link FilterObject}. - */ - protected FilterObject getPageAndFilter(List args) { - int page = 1; - - String filter = ""; - - if (args.size() == 0) { - filter = ""; - page = 1; - } else if (args.size() == 1) { - try { - page = Integer.parseInt(args.get(0)); - } catch (NumberFormatException ex) { - filter = args.get(0); - page = 1; - } - } else if (args.size() == 2) { - filter = args.get(0); - try { - page = Integer.parseInt(args.get(1)); - } catch (NumberFormatException ex) { - page = 1; - } - } - return new FilterObject(page, filter); - } - - /** - * "Key-Object" containing information about the page and the filter that were requested. - */ - protected class FilterObject { - private Integer page; - private String filter; - - public FilterObject(Integer page, String filter) { - this.page = page; - this.filter = filter; - } - - /** - * Gets the page. - * @return The page. - */ - public Integer getPage() { - return this.page; - } - - /** - * Sets the page. - * - * @param page The new page. - */ - public void setPage(int page) { - this.page = page; - } - - /** - * Gets the filter. - * @return The filter. - */ - public String getFilter() { - return this.filter; - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCoreCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCoreCommand.java deleted file mode 100644 index 7071311aa..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/PaginatedCoreCommand.java +++ /dev/null @@ -1,26 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; - -/** - * A generic paginated Multiverse-command. - * @param The type of items on the page. - */ -public abstract class PaginatedCoreCommand extends PaginatedCommand { - /** - * The reference to the core. - */ - protected MultiverseCore plugin; - - public PaginatedCoreCommand(MultiverseCore plugin) { - super(plugin); - this.plugin = plugin; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/PurgeCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/PurgeCommand.java deleted file mode 100644 index 8b7c4a26c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/PurgeCommand.java +++ /dev/null @@ -1,92 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.api.WorldPurger; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Removes a type of mob from a world. - */ -public class PurgeCommand extends MultiverseCommand { - private MVWorldManager worldManager; - - public PurgeCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Purge World of Creatures"); - this.setCommandUsage("/mv purge" + ChatColor.GOLD + " [WORLD|all] " + ChatColor.GREEN + "{all|animals|monsters|MOBNAME}"); - this.setArgRange(1, 2); - this.addKey("mvpurge"); - this.addKey("mv purge"); - this.addCommandExample("/mv purge " + ChatColor.GREEN + "all"); - this.addCommandExample("/mv purge " + ChatColor.GOLD + "all " + ChatColor.GREEN + "all"); - this.addCommandExample("/mv purge " + ChatColor.GREEN + "monsters"); - this.addCommandExample("/mv purge " + ChatColor.GOLD + "all " + ChatColor.GREEN + "animals"); - this.addCommandExample("/mv purge " + ChatColor.GOLD + "MyWorld " + ChatColor.GREEN + "squid"); - this.addCommandExample("/mv purge " + ChatColor.GOLD + "MyWorld_nether " + ChatColor.GREEN + "ghast"); - this.setPermission("multiverse.core.purge", "Removed the specified type of mob from the specified world.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - if (args.size() == 1 && p == null) { - sender.sendMessage("This command requires a WORLD when being run from the console!"); - sender.sendMessage(this.getCommandUsage()); - return; - } - String worldName = null; - String deathName = null; - if (args.size() == 1) { - worldName = p.getWorld().getName(); - deathName = args.get(0); - } else { - worldName = args.get(0); - deathName = args.get(1); - } - - if (!worldName.equalsIgnoreCase("all") && !this.worldManager.isMVWorld(worldName)) { - this.plugin.showNotMVWorldMessage(sender, worldName); - sender.sendMessage("It cannot be purged."); - return; - } - - List worldsToRemoveEntitiesFrom = new ArrayList(); - // Handle all case any user who names a world "all" should know better... - if (worldName.equalsIgnoreCase("all")) { - worldsToRemoveEntitiesFrom.addAll(this.worldManager.getMVWorlds()); - } else { - worldsToRemoveEntitiesFrom.add(this.worldManager.getMVWorld(worldName)); - } - - WorldPurger purger = this.worldManager.getTheWorldPurger(); - ArrayList thingsToKill = new ArrayList(); - if (deathName.equalsIgnoreCase("all") || deathName.equalsIgnoreCase("animals") || deathName.equalsIgnoreCase("monsters")) { - thingsToKill.add(deathName.toUpperCase()); - } else { - Collections.addAll(thingsToKill, deathName.toUpperCase().split(",")); - } - for (MultiverseWorld w : worldsToRemoveEntitiesFrom) { - purger.purgeWorld(w, thingsToKill, false, false, sender); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java deleted file mode 100644 index 3c5baf842..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.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.List; - -/** - * Regenerates a world. - */ -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]] [--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:"); - this.addCommandExample("/mv regen " + ChatColor.GREEN + "MyWorld" + ChatColor.GOLD + " -s"); - this.addCommandExample("or specifiy a seed to get that one:"); - this.addCommandExample("/mv regen " + ChatColor.GREEN + "MyWorld" + ChatColor.GOLD + " -s" + ChatColor.AQUA + " gargamel"); - this.setPermission("multiverse.core.regen", "Regenerates a world on your server. The previous state will be lost " - + ChatColor.RED + "PERMANENTLY.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - String worldName = args.get(0); - boolean useseed = CommandHandler.hasFlag("-s", args); - String seedflag = CommandHandler.getFlag("-s", args); - boolean keepGamerules = CommandHandler.hasFlag("--keep-gamerules", args); - - boolean randomseed = seedflag == null || seedflag.isEmpty() || seedflag.equalsIgnoreCase("--keep-gamerules"); - String seed = randomseed ? "" : seedflag; - - 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) - )); - } - - 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!"); - }; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ReloadCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ReloadCommand.java deleted file mode 100644 index abe2b0a64..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ReloadCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.event.MVConfigReloadEvent; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.List; - -/** - * Reloads worlds.yml and config.yml. - */ -public class ReloadCommand extends MultiverseCommand { - - public ReloadCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Reload Configs"); - this.setCommandUsage("/mv reload"); - this.setArgRange(0, 0); - this.addKey("mvreload"); - this.addKey("mvr"); - this.addKey("mv reload"); - this.addCommandExample("/mv reload"); - this.setPermission("multiverse.core.reload", "Reloads worlds.yml and config.yml.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - sender.sendMessage(ChatColor.GOLD + "Reloading all Multiverse Plugin configs..."); - this.plugin.loadConfigs(); - this.plugin.getAnchorManager().loadAnchors(); - this.plugin.getMVWorldManager().loadWorlds(true); - - List configsLoaded = new ArrayList(); - configsLoaded.add("Multiverse-Core - config.yml"); - configsLoaded.add("Multiverse-Core - worlds.yml"); - configsLoaded.add("Multiverse-Core - anchors.yml"); - // Create the event - MVConfigReloadEvent configReload = new MVConfigReloadEvent(configsLoaded); - // Fire it off - this.plugin.getServer().getPluginManager().callEvent(configReload); - for (String s : configReload.getAllConfigsLoaded()) { - sender.sendMessage(s); - } - - sender.sendMessage(ChatColor.GREEN + "Reload Complete!"); - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/RemoveCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/RemoveCommand.java deleted file mode 100644 index ff24e7408..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/RemoveCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Unloads a world and removes it from the config. - */ -public class RemoveCommand extends MultiverseCommand { - - public RemoveCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Remove World"); - this.setCommandUsage("/mv remove" + ChatColor.GREEN + " {WORLD}"); - this.setArgRange(1, 1); - this.addKey("mvremove"); - this.addKey("mv remove"); - this.addCommandExample("/mv remove " + ChatColor.GREEN + "MyWorld"); - this.setPermission("multiverse.core.remove", - "Unloads a world from Multiverse and removes it from worlds.yml, this does NOT DELETE the world folder.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (this.plugin.getMVWorldManager().removeWorldFromConfig(args.get(0))) { - sender.sendMessage("World removed from config!"); - } else { - sender.sendMessage("Error trying to remove world from config!"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/ScriptCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/ScriptCommand.java deleted file mode 100644 index 665f6aa71..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/ScriptCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.io.File; -import java.util.List; - -/** - * States who is in what world. - */ -public class ScriptCommand extends MultiverseCommand { - - public ScriptCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Runs a script."); - this.setCommandUsage("/mv script" + ChatColor.GOLD + " {script} [target]"); - this.setArgRange(1, 2); - this.addKey("mv script"); - this.addKey("mvscript"); - this.addCommandExample(String.format("/mv script %sscript.txt", ChatColor.GOLD)); - this.addCommandExample(String.format("/mv script %stest.txt %ssomeplayer", ChatColor.GOLD, ChatColor.GREEN)); - this.setPermission("multiverse.core.script", "Runs a script.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (plugin.getScriptAPI() == null) { - sender.sendMessage("Buscript failed to load while the server was starting. Scripts cannot be run."); - return; - } - File file = new File(plugin.getScriptAPI().getScriptFolder(), args.get(0)); - if (!file.exists()) { - sender.sendMessage("That script file does not exist in the Multiverse-Core scripts directory!"); - return; - } - Player player = null; - if (sender instanceof Player) { - player = (Player) sender; - } - String target = null; - if (args.size() == 2) { - target = args.get(1); - } - plugin.getScriptAPI().executeScript(file, target, player); - sender.sendMessage(String.format("Script '%s%s%s' finished!", ChatColor.GOLD, file.getName(), ChatColor.WHITE)); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/SetSpawnCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/SetSpawnCommand.java deleted file mode 100644 index 26dad442c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/SetSpawnCommand.java +++ /dev/null @@ -1,148 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.BlockSafety; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; -import org.bukkit.Bukkit; - -/** - * Sets the spawn for a world. - */ -public class SetSpawnCommand extends MultiverseCommand { - public SetSpawnCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Set World Spawn"); - this.setCommandUsage("/mv setspawn"); - this.setArgRange(0, 6); - this.addKey("mvsetspawn"); - this.addKey("mvsets"); - this.addKey("mvss"); - this.addKey("mv set spawn"); - this.addKey("mv setspawn"); - this.addKey("mvset spawn"); - this.addCommandExample("/mv set spawn"); - this.setPermission("multiverse.core.spawn.set", "Sets the spawn for the current world.", PermissionDefault.OP); - } - - /** - * Dispatches the user's command depending on the number of parameters - * @param sender The player who executes the command, may be console as well. - * @param args Command line parameters - */ - @Override - public void runCommand(CommandSender sender, List args) { - if (args.isEmpty()) { - setWorldSpawn(sender); - } else if (args.size() == 4) { - setWorldSpawn(sender, args.get(0), args.get(1), args.get(2), args.get(3)); - } else if (args.size() == 6) { - setWorldSpawn(sender, args.get(0), args.get(1), args.get(2), args.get(3), args.get(4), args.get(5)); - } else { - sender.sendMessage("Use no arguments for your current location, or world/x/y/z, or world/x/y/z/yaw/pitch!"); - } - } - - /** - * Set the world spawn when no parameters are given - * @param sender The {@link CommandSender} who executes the command. - * Everything not a {@link Player}, e.g. console, gets rejected, as we can't get coordinates from there. - */ - protected void setWorldSpawn(CommandSender sender) { - if (sender instanceof Player) { - Player p = (Player) sender; - Location l = p.getLocation(); - World w = p.getWorld(); - setWorldSpawn(sender, w, l); - } else { - sender.sendMessage("You need to give coordinates to use this command from the console!"); - } - } - - /** - * Set the world spawn when 4 parameters are given - * @param sender The {@link CommandSender} who executes the command - * @param world The world to set the spawn in - * @param x X-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - * @param y Y-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - * @param z Z-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - */ - protected void setWorldSpawn(CommandSender sender, String world, String x, String y, String z) { - setWorldSpawn(sender, world, x, y, z, "0", "0"); - } - - /** - * Set the world spawn when 6 parameters are given - * @param sender The {@link CommandSender} who executes the command - * @param world The world to set the spawn in - * @param x X-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - * @param y Y-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - * @param z Z-coordinate to set the spawn to (as a {@link String} as it's from the command line, gets parsed into a double) - * @param yaw Yaw a newly spawned player should look at (as a {@link String} as it's from the command line, gets parsed into a float) - * @param pitch Pitch a newly spawned player should look at (as a {@link String} as it's from the command line, gets parsed into a float) - */ - protected void setWorldSpawn(CommandSender sender, String world, String x, String y, String z, String yaw, String pitch) { - double dx, dy, dz; - float fpitch, fyaw; - World bukkitWorld = Bukkit.getWorld(world); - if (bukkitWorld == null) { - sender.sendMessage("World " + world + " is unknown!"); - return; - } - try { - dx = Double.parseDouble(x); - dy = Double.parseDouble(y); - dz = Double.parseDouble(z); - fpitch = Float.parseFloat(pitch); - fyaw = Float.parseFloat(yaw); - } catch (NumberFormatException ex) { - sender.sendMessage("All coordinates must be numeric"); - return; - } - Location l = new Location(bukkitWorld, dx, dy, dz, fyaw, fpitch); - setWorldSpawn(sender, bukkitWorld, l); - } - - /** - * Does the actual spawn-setting-work. - * - * @param sender The {@link CommandSender} that's setting the spawn. - * @param w The {@link World} to set the spawn in - * @param l The {@link Location} to set the spawn to - */ - private void setWorldSpawn(CommandSender sender, World w, Location l) { - MultiverseWorld foundWorld = this.plugin.getMVWorldManager().getMVWorld(w.getName()); - if (foundWorld != null) { - foundWorld.setSpawnLocation(l); - BlockSafety bs = this.plugin.getBlockSafety(); - if (!bs.playerCanSpawnHereSafely(l) && foundWorld.getAdjustSpawn()) { - sender.sendMessage("It looks like that location would normally be unsafe. But I trust you."); - sender.sendMessage("I'm turning off the Safe-T-Teleporter for spawns to this world."); - sender.sendMessage("If you want this turned back on just do:"); - sender.sendMessage(ChatColor.AQUA + "/mvm set adjustspawn true " + foundWorld.getAlias()); - foundWorld.setAdjustSpawn(false); - } - sender.sendMessage("Spawn was set to: " + plugin.getLocationManipulation().strCoords(l)); - if (!plugin.saveWorldConfig()) { - sender.sendMessage(ChatColor.RED + "There was an issue saving worlds.yml! Your changes will only be temporary!"); - } - } else { - w.setSpawnLocation(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - sender.sendMessage("Multiverse does not know about this world, only X,Y and Z set. Please import it to set the spawn fully (Pitch/Yaws)."); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/SilentCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/SilentCommand.java deleted file mode 100644 index 4d0bfd735..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/SilentCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Enables debug-information. - */ -public class SilentCommand extends MultiverseCommand { - - public SilentCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Turn silent mode on/off?"); - this.setCommandUsage("/mv silent" + ChatColor.GOLD + " [true|false|on|off]"); - this.setArgRange(0, 1); - this.addKey("mv silent"); - this.addKey("mvsilent"); - this.addCommandExample("/mv silent " + ChatColor.GOLD + "true"); - this.setPermission("multiverse.core.silent", "Reduces the amount of startup messages.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (args.size() == 1) { - if (args.get(0).equalsIgnoreCase("on")) { - args.set(0, "true"); - } - plugin.getMVConfig().setSilentStart(Boolean.valueOf(args.get(0))); - plugin.saveMVConfigs(); - } - this.displaySilentMode(sender); - } - - private void displaySilentMode(CommandSender sender) { - if (plugin.getMVConfig().getSilentStart()) { - sender.sendMessage("Multiverse Silent Start mode is " + ChatColor.GREEN + "ON"); - } else { - sender.sendMessage("Multiverse Silent Start mode is " + ChatColor.RED + "OFF"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/SleepCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/SleepCommand.java deleted file mode 100644 index 08156d200..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/SleepCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Takes the player to the latest bed he's slept in. - */ -public class SleepCommand extends MultiverseCommand { - - public SleepCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Go to Sleep"); - this.setCommandUsage("/mv sleep"); - this.setArgRange(0, 0); - this.addKey("mv sleep"); - this.setPermission("multiverse.core.sleep", "Takes you the latest bed you've slept in (Currently BROKEN).", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - Player p = null; - if (sender instanceof Player) { - p = (Player) sender; - } - - if (p == null) { - return; - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/SpawnCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/SpawnCommand.java deleted file mode 100644 index 8e865545c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/SpawnCommand.java +++ /dev/null @@ -1,91 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.utils.PlayerFinder; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Teleports a player to the spawn. - */ -public class SpawnCommand extends MultiverseCommand { - - public SpawnCommand(MultiverseCore plugin) { - super(plugin); - Permission otherPerm = new Permission("multiverse.core.spawn.other", - "Teleports another player to the spawn of the world they are in.", PermissionDefault.OP); - this.setName("Spawn"); - this.setCommandUsage("/mv spawn" + ChatColor.GOLD + " [PLAYER]"); - this.setArgRange(0, 1); - this.addKey("mvspawn"); - this.addKey("mv spawn"); - this.addKey("mvs"); - this.setPermission("multiverse.core.spawn.self", "Teleports you to the Spawn Point of the world you are in.", PermissionDefault.OP); - this.addAdditonalPermission(otherPerm); - - } - - @Override - public void runCommand(CommandSender sender, List args) { - Player player = null; - if (sender instanceof Player) { - player = (Player) sender; - } - // If a persons name was passed in, you must be A. the console, or B have permissions - if (args.size() == 1) { - if (player != null && !this.plugin.getMVPerms().hasPermission(player, "multiverse.core.spawn.other", true)) { - sender.sendMessage("You don't have permission to teleport another player to spawn. (multiverse.core.spawn.other)"); - return; - } - Player target = PlayerFinder.get(args.get(0), sender); - if (target != null) { - target.sendMessage("Teleporting to this world's spawn..."); - spawnAccurately(target); - - if (player != null) { - target.sendMessage("You were teleported by: " + ChatColor.YELLOW + player.getName()); - } else { - target.sendMessage("You were teleported by: " + ChatColor.LIGHT_PURPLE + "the console"); - } - } else { - sender.sendMessage(args.get(0) + " is not logged on right now!"); - } - } else { - if (player != null && !this.plugin.getMVPerms().hasPermission(player, "multiverse.core.spawn.self", true)) { - sender.sendMessage("You don't have permission to teleport yourself to spawn. (multiverse.core.spawn.self)"); - return; - } - if (player != null) { - player.sendMessage("Teleporting to this world's spawn..."); - spawnAccurately(player); - } else { - sender.sendMessage("From the console, you must provide a PLAYER."); - } - } - } - - private void spawnAccurately(Player player) { - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(player.getWorld().getName()); - Location spawnLocation; - if (world != null) { - spawnLocation = world.getSpawnLocation(); - } else { - spawnLocation = player.getWorld().getSpawnLocation(); - } - this.plugin.getSafeTTeleporter().safelyTeleport(player, player, spawnLocation, false); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/TeleportCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/TeleportCommand.java deleted file mode 100644 index 9507236cd..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/TeleportCommand.java +++ /dev/null @@ -1,199 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -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; -import com.onarandombox.MultiverseCore.destination.WorldDestination; -import com.onarandombox.MultiverseCore.enums.TeleportResult; -import com.onarandombox.MultiverseCore.event.MVTeleportEvent; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; -import com.onarandombox.MultiverseCore.utils.PlayerFinder; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -import java.util.ArrayList; -import java.util.List; - -/** - * Used to teleport players. - */ -public class TeleportCommand extends MultiverseCommand { - private SafeTTeleporter playerTeleporter; - - public TeleportCommand(MultiverseCore plugin) { - super(plugin); - Permission menu = new Permission("multiverse.teleport.*", "Allows you to display the teleport menu.", PermissionDefault.OP); - - this.setName("Teleport"); - this.setCommandUsage("/mv tp " + ChatColor.GOLD + "[PLAYER]" + ChatColor.GREEN + " {DESTINATION}"); - this.setArgRange(1, 2); - this.addKey("mvtp"); - this.addKey("mv tp"); - this.playerTeleporter = this.plugin.getSafeTTeleporter(); - this.setPermission(menu); - } - - private static final int UNSAFE_TELEPORT_EXPIRE_DELAY = 15; - - @Override - public void runCommand(CommandSender sender, List args) { - CommandSender teleporter = sender; - Player teleportee = null; - - String destinationName; - - if (args.size() == 2) { - teleportee = PlayerFinder.get(args.get(0), sender); - if (teleportee == null) { - this.messaging.sendMessage(sender, String.format("Sorry, I couldn't find player: %s%s", - ChatColor.GOLD, args.get(0)), false); - return; - } - destinationName = args.get(1); - - } else { - destinationName = args.get(0); - if (!(sender instanceof Player)) { - this.messaging.sendMessage(sender, String.format("From the console, you must specify a player to teleport"), false); - return; - } - teleportee = (Player) sender; - } - - DestinationFactory df = this.plugin.getDestFactory(); - MVDestination d = df.getPlayerAwareDestination(teleportee, destinationName); - - MVTeleportEvent teleportEvent = new MVTeleportEvent(d, teleportee, teleporter, true); - this.plugin.getServer().getPluginManager().callEvent(teleportEvent); - if (teleportEvent.isCancelled()) { - Logging.fine("Someone else cancelled the MVTeleport Event!!!"); - return; - } - - if (d != null && d instanceof InvalidDestination) { - this.messaging.sendMessage(sender, String.format("Multiverse does not know how to take you to %s%s", - ChatColor.RED, destinationName), false); - return; - } - - if (!this.checkSendPermissions(teleporter, teleportee, d)) { - return; - } - - if (plugin.getMVConfig().getEnforceAccess() && teleporter != null && !this.plugin.getMVPerms().canEnterDestination(teleporter, d)) { - if (teleportee.equals(teleporter)) { - teleporter.sendMessage("Doesn't look like you're allowed to go " + ChatColor.RED + "there..."); - } else { - teleporter.sendMessage("Doesn't look like you're allowed to send " + ChatColor.GOLD - + teleportee.getName() + ChatColor.WHITE + " to " + ChatColor.RED + "there..."); - } - return; - } else if (teleporter != null && !this.plugin.getMVPerms().canTravelFromLocation(teleporter, d.getLocation(teleportee))) { - if (teleportee.equals(teleporter)) { - this.messaging.sendMessage(teleporter, String.format("DOH! Doesn't look like you can get to %s%s %sfrom where you are...", - ChatColor.GREEN, d.toString(), ChatColor.WHITE), false); - } else { - this.messaging.sendMessage(teleporter, String.format("DOH! Doesn't look like %s%s %scan get to %sTHERE from where they are...", - ChatColor.GREEN, ((Player) teleporter).getWorld().getName(), ChatColor.WHITE, ChatColor.RED), false); - } - return; - } - - // Special check to verify if players are tryint to teleport to the same - // WORLDDestination as the world they're in, that they ALSO have multiverse.core.spawn.self - - if (d instanceof WorldDestination) { - World w = d.getLocation(teleportee).getWorld(); - if (teleportee.getWorld().equals(w)) { - if (teleporter.equals(teleportee)) { - if (!this.plugin.getMVPerms().hasPermission(teleporter, "multiverse.core.spawn.self", true)) { - this.messaging.sendMessages(teleporter, new String[]{ - String.format("Sorry you don't have permission to go to the world spawn!"), - String.format("%s (multiverse.core.spawn.self)", - ChatColor.RED) }, false); - return; - } - } else { - if (!this.plugin.getMVPerms().hasPermission(teleporter, "multiverse.core.spawn.other", true)) { - this.messaging.sendMessages(teleporter, new String[]{ - String.format("Sorry you don't have permission to send %s to the world spawn!", - teleportee.getDisplayName()), - String.format("%s (multiverse.core.spawn.other)", - ChatColor.RED) }, false); - return; - } - } - } - } - - if (d.getLocation(teleportee) == null) { - this.messaging.sendMessage(teleporter, "Sorry Boss, I tried everything, but just couldn't teleport ya there!", false); - return; - } - Teleporter teleportObject = (d instanceof CustomTeleporterDestination) ? - ((CustomTeleporterDestination)d).getTeleporter() : this.playerTeleporter; - TeleportResult result = teleportObject.teleport(teleporter, teleportee, d); - if (result == TeleportResult.FAIL_UNSAFE) { - Logging.fine("Could not teleport " + teleportee.getName() - + " to " + plugin.getLocationManipulation().strCoordsRaw(d.getLocation(teleportee))); - - String player = "you"; - if (!teleportee.equals(teleporter)) { - player = teleportee.getName(); - } - - 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)) { - this.messaging.sendMessages(teleporter, new String[]{ - String.format("%sYou don't have permission to teleport %syourself %sto a %s%s %sDestination", - ChatColor.WHITE, ChatColor.AQUA, ChatColor.WHITE, ChatColor.RED, destination.getType(), ChatColor.WHITE), - String.format("%s (multiverse.teleport.self.%s)", - ChatColor.RED, destination.getIdentifier()) }, false); - return false; - } - } else { - if (!this.plugin.getMVPerms().hasPermission(teleporter, "multiverse.teleport.other." + destination.getIdentifier(), true)) { - this.messaging.sendMessages(teleporter, new String[]{ - String.format("You don't have permission to teleport another player to a %s%s Destination.", - ChatColor.GREEN, destination.getType()), - String.format("%s(multiverse.teleport.other.%s)", - ChatColor.RED, destination.getIdentifier()) }, false); - return false; - } - } - return true; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/UnloadCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/UnloadCommand.java deleted file mode 100644 index 52f92c6ca..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/UnloadCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; - -/** - * Unloads worlds from Multiverse. - */ -public class UnloadCommand extends MultiverseCommand { - - public UnloadCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Unload World"); - this.setCommandUsage("/mv unload" + ChatColor.GREEN + " {WORLD}"); - this.setArgRange(1, 1); - this.addKey("mvunload"); - this.addKey("mv unload"); - this.setPermission("multiverse.core.unload", - "Unloads a world from Multiverse. This does NOT remove the world folder. This does NOT remove it from the config file.", PermissionDefault.OP); - } - - @Override - public void runCommand(CommandSender sender, List args) { - if (this.plugin.getMVWorldManager().unloadWorld(args.get(0))) { - Command.broadcastCommandMessage(sender, "Unloaded world '" + args.get(0) + "'!"); - } else { - sender.sendMessage("Error trying to unload world '" + args.get(0) + "'!"); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java deleted file mode 100644 index f032fd20c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java +++ /dev/null @@ -1,225 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.event.MVVersionEvent; -import com.onarandombox.MultiverseCore.utils.webpaste.PasteFailedException; -import com.onarandombox.MultiverseCore.utils.webpaste.PasteService; -import com.onarandombox.MultiverseCore.utils.webpaste.PasteServiceFactory; -import com.onarandombox.MultiverseCore.utils.webpaste.PasteServiceType; -import com.onarandombox.MultiverseCore.utils.webpaste.URLShortener; -import com.onarandombox.MultiverseCore.utils.webpaste.URLShortenerFactory; -import com.onarandombox.MultiverseCore.utils.webpaste.URLShortenerType; -import com.pneumaticraft.commandhandler.CommandHandler; -import org.apache.commons.lang.StringUtils; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.scheduler.BukkitRunnable; - -import java.io.File; -import java.util.List; -import java.util.Map; - -/** - * Dumps version info to the console. - */ -public class VersionCommand extends MultiverseCommand { - private static final URLShortener SHORTENER = URLShortenerFactory.getService(URLShortenerType.BITLY); - - public VersionCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Multiverse Version"); - this.setCommandUsage("/mv version " + ChatColor.GOLD + "[-b|-h|-p] [--include-plugin-list]"); - this.setArgRange(0, 2); - this.addKey("mv version"); - 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.OP) - ; - } - - private String getLegacyString() { - return "[Multiverse-Core] Multiverse-Core Version: " + this.plugin.getDescription().getVersion() + '\n' - + "[Multiverse-Core] Bukkit Version: " + this.plugin.getServer().getVersion() + '\n' - + "[Multiverse-Core] Loaded Worlds: " + this.plugin.getMVWorldManager().getMVWorlds() + '\n' - + "[Multiverse-Core] Multiverse Plugins Loaded: " + this.plugin.getPluginCount() + '\n' - +"[Multiverse-Core] Economy being used: " + plugin.getEconomist().getEconomyName() + '\n' - + "[Multiverse-Core] Permissions Plugin: " + this.plugin.getMVPerms().getType() + '\n' - + "[Multiverse-Core] Dumping Config Values: (version " + this.plugin.getMVConfig().getVersion() + ")" + '\n' - + "[Multiverse-Core] enforceaccess: " + plugin.getMVConfig().getEnforceAccess() + '\n' - + "[Multiverse-Core] prefixchat: " + plugin.getMVConfig().getPrefixChat() + '\n' - + "[Multiverse-Core] prefixchatformat: " + plugin.getMVConfig().getPrefixChatFormat() + '\n' - + "[Multiverse-Core] useasyncchat: " + plugin.getMVConfig().getUseAsyncChat() + '\n' - + "[Multiverse-Core] teleportintercept: " + plugin.getMVConfig().getTeleportIntercept() + '\n' - + "[Multiverse-Core] firstspawnoverride: " + plugin.getMVConfig().getFirstSpawnOverride() + '\n' - + "[Multiverse-Core] displaypermerrors: " + plugin.getMVConfig().getDisplayPermErrors() + '\n' - + "[Multiverse-Core] enablebuscript: " + plugin.getMVConfig().getEnableBuscript() + '\n' - + "[Multiverse-Core] globaldebug: " + plugin.getMVConfig().getGlobalDebug() + '\n' - + "[Multiverse-Core] silentstart: " + plugin.getMVConfig().getSilentStart() + '\n' - + "[Multiverse-Core] messagecooldown: " + plugin.getMessaging().getCooldown() + '\n' - + "[Multiverse-Core] version: " + plugin.getMVConfig().getVersion() + '\n' - + "[Multiverse-Core] firstspawnworld: " + plugin.getMVConfig().getFirstSpawnWorld() + '\n' - + "[Multiverse-Core] teleportcooldown: " + plugin.getMVConfig().getTeleportCooldown() + '\n' - + "[Multiverse-Core] defaultportalsearch: " + plugin.getMVConfig().isUsingDefaultPortalSearch() + '\n' - + "[Multiverse-Core] portalsearchradius: " + plugin.getMVConfig().getPortalSearchRadius() + '\n' - + "[Multiverse-Core] autopurge: " + plugin.getMVConfig().isAutoPurgeEnabled() + '\n' - + "[Multiverse-Core] Special Code: FRN002" + '\n'; - } - - private String getMarkdownString() { - return "# Multiverse-Core" + '\n' - + "## Overview" + '\n' - + "| Name | Value |" + '\n' - + "| --- | --- |" + '\n' - + "| Multiverse-Core Version | `" + this.plugin.getDescription().getVersion() + "` |" + '\n' - + "| Bukkit Version | `" + this.plugin.getServer().getVersion() + "` |" + '\n' - + "| Loaded Worlds | `" + this.plugin.getMVWorldManager().getMVWorlds() + "` |" + '\n' - + "| Multiverse Plugins Loaded | `" + this.plugin.getPluginCount() + "` |" + '\n' - + "| Economy being used | `" + plugin.getEconomist().getEconomyName() + "` |" + '\n' - + "| Permissions Plugin | `" + this.plugin.getMVPerms().getType() + "` |" + '\n' - + "## Parsed Config" + '\n' - + "These are what Multiverse thought the in-memory values of the config were." + "\n\n" - + "| Config Key | Value |" + '\n' - + "| --- | --- |" + '\n' - + "| version | `" + this.plugin.getMVConfig().getVersion() + "` |" + '\n' - + "| messagecooldown | `" + plugin.getMessaging().getCooldown() + "` |" + '\n' - + "| teleportcooldown | `" + plugin.getMVConfig().getTeleportCooldown() + "` |" + '\n' - + "| worldnameprefix | `" + plugin.getMVConfig().getPrefixChat() + "` |" + '\n' - + "| worldnameprefixFormat | `" + plugin.getMVConfig().getPrefixChatFormat() + "` |" + '\n' - + "| enforceaccess | `" + plugin.getMVConfig().getEnforceAccess() + "` |" + '\n' - + "| displaypermerrors | `" + plugin.getMVConfig().getDisplayPermErrors() + "` |" + '\n' - + "| teleportintercept | `" + plugin.getMVConfig().getTeleportIntercept() + "` |" + '\n' - + "| firstspawnoverride | `" + plugin.getMVConfig().getFirstSpawnOverride() + "` |" + '\n' - + "| firstspawnworld | `" + plugin.getMVConfig().getFirstSpawnWorld() + "` |" + '\n' - + "| debug | `" + plugin.getMVConfig().getGlobalDebug() + "` |" + '\n'; - } - - private void addVersionInfoToEvent(MVVersionEvent event) { - // add the legacy version info - event.appendVersionInfo(this.getLegacyString()); - - // add the legacy file, but as markdown so it's readable - // TODO Readd this in 5.0.0 - // event.putDetailedVersionInfo("version.md", this.getMarkdownString()); - - // add config.yml - File configFile = new File(this.plugin.getDataFolder(), "config.yml"); - event.putDetailedVersionInfo("multiverse-core/config.yml", configFile); - - // add worlds.yml - File worldsFile = new File(this.plugin.getDataFolder(), "worlds.yml"); - event.putDetailedVersionInfo("multiverse-core/worlds.yml", worldsFile); - } - - @Override - public void runCommand(final CommandSender sender, final List args) { - // Check if the command was sent from a Player. - if (sender instanceof Player) { - sender.sendMessage("Version info dumped to console. Please check your server logs."); - } - - MVVersionEvent versionEvent = new MVVersionEvent(); - - this.addVersionInfoToEvent(versionEvent); - this.plugin.getServer().getPluginManager().callEvent(versionEvent); - - if (CommandHandler.hasFlag("--include-plugin-list", args)) { - versionEvent.appendVersionInfo('\n' + "Plugins: " + getPluginList()); - versionEvent.putDetailedVersionInfo("plugins.txt", "Plugins: " + getPluginList()); - } - - final String versionInfo = versionEvent.getVersionInfo(); - versionEvent.putDetailedVersionInfo("version.txt", versionInfo); - - final Map files = versionEvent.getDetailedVersionInfo(); - - // log to console - String[] lines = versionInfo.split("\\r?\\n"); - for (String line : lines) { - if (!line.isEmpty()) { - this.plugin.getServer().getLogger().info(line); - } - } - - BukkitRunnable logPoster = new BukkitRunnable() { - @Override - public void run() { - if (args.size() > 0) { - String pasteUrl; - if (CommandHandler.hasFlag("-b", args)) { - // private post to pastebin - pasteUrl = postToService(PasteServiceType.PASTEBIN, true, versionInfo, files); - } else if (CommandHandler.hasFlag("-g", args)) { - // private post to github - pasteUrl = postToService(PasteServiceType.GITHUB, true, versionInfo, files); - } else if (CommandHandler.hasFlag("-h", args)) { - // private post to hastebin - pasteUrl = postToService(PasteServiceType.HASTEBIN, true, versionInfo, files); - } else if (CommandHandler.hasFlag("-p", args)) { - // private post to paste.gg - pasteUrl = postToService(PasteServiceType.PASTEGG, true, versionInfo, files); - } else { - return; - } - - if (!(sender instanceof ConsoleCommandSender)) { - sender.sendMessage("Version info dumped here: " + ChatColor.GREEN + pasteUrl); - } - Logging.info("Version info dumped here: %s", pasteUrl); - } - } - }; - - // Run the log posting operation asynchronously, since we don't know how long it will take. - logPoster.runTaskAsynchronously(this.plugin); - } - - /** - * Send the current contents of this.pasteBinBuffer to a web service. - * - * @param type Service type to send paste data to. - * @param isPrivate Should the paste be marked as private. - * @param pasteData Legacy string only data to post to a service. - * @param pasteFiles Map of filenames/contents of debug info. - * @return URL of visible paste - */ - private static String postToService(PasteServiceType type, boolean isPrivate, String pasteData, Map pasteFiles) { - PasteService ps = PasteServiceFactory.getService(type, isPrivate); - - try { - String result; - if (ps.supportsMultiFile()) { - result = ps.postData(pasteFiles); - } else { - result = ps.postData(pasteData); - } - - if (SHORTENER != null) return SHORTENER.shorten(result); - return result; - } catch (PasteFailedException e) { - e.printStackTrace(); - return "Error posting to service."; - } catch (NullPointerException e) { - e.printStackTrace(); - return "That service isn't supported yet."; - } - } - - private String getPluginList() { - return StringUtils.join(plugin.getServer().getPluginManager().getPlugins(), ", "); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/WhoCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/WhoCommand.java deleted file mode 100644 index 1a03df281..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/WhoCommand.java +++ /dev/null @@ -1,118 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.commands; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.PermissionDefault; - -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -/** - * States who is in what world. - */ -public class WhoCommand extends MultiverseCommand { - - private MVWorldManager worldManager; - - public WhoCommand(MultiverseCore plugin) { - super(plugin); - this.setName("Who?"); - this.setCommandUsage("/mv who" + ChatColor.GOLD + " [WORLD|--all]"); - this.setArgRange(0, 1); - this.addKey("mv who"); - this.addKey("mvw"); - this.addKey("mvwho"); - this.addCommandExample("/mv who"); - this.addCommandExample(String.format("/mv who %s--all", ChatColor.GREEN)); - this.addCommandExample(String.format("/mv who %smyworld", ChatColor.GOLD)); - this.setPermission("multiverse.core.list.who", "States who is in what world.", PermissionDefault.OP); - this.worldManager = this.plugin.getMVWorldManager(); - } - - @Override - public void runCommand(CommandSender sender, List args) { - // If this command was sent from a Player then we need to check Permissions - Player p = null; - // By default, show all from the console - boolean showAll = true; - if (sender instanceof Player) { - p = (Player) sender; - showAll = false; - } - - final Collection onlinePlayers = plugin.getServer().getOnlinePlayers(); - final Collection visiblePlayers = new HashSet(onlinePlayers.size()); - for (final Object player : onlinePlayers) { - if (player instanceof Player && (p == null || p.canSee((Player) player))) { - visiblePlayers.add((Player) player); - } - } - - if (args.size() == 1) { - if (args.get(0).equalsIgnoreCase("--all") || args.get(0).equalsIgnoreCase("-a")) { - showAll = true; - } else { - // single world mode - MultiverseWorld world = this.worldManager.getMVWorld(args.get(0)); - if (world == null) { - sender.sendMessage(ChatColor.RED + "That world does not exist."); - return; - } - - if (!this.plugin.getMVPerms().canEnterWorld(p, world)) { - sender.sendMessage(ChatColor.RED + "You aren't allowed to access to this world!"); - return; - } - - sender.sendMessage(String.format("%s--- Players in %s%s ---", ChatColor.AQUA, - world.getColoredWorldString(), ChatColor.AQUA)); - sender.sendMessage(this.buildPlayerString(world, p, visiblePlayers)); - return; - } - } - - // multiworld mode - sender.sendMessage(ChatColor.AQUA + "--- Worlds and their players --- " - + visiblePlayers.size() + "/" + plugin.getServer().getMaxPlayers()); - boolean shownOne = false; - for (MultiverseWorld world : this.worldManager.getMVWorlds()) { - if (this.plugin.getMVPerms().canEnterWorld(p, world)) { // only show world if the player can access it - if (showAll || !world.getCBWorld().getPlayers().isEmpty()) { // either show all or show if the world is not empty - sender.sendMessage(String.format("%s%s - %s", world.getColoredWorldString(), ChatColor.WHITE, buildPlayerString(world, p, visiblePlayers))); - shownOne = true; - } - } - } - if (!shownOne) { - sender.sendMessage("No worlds found."); - } - return; - } - - private static String buildPlayerString(MultiverseWorld world, Player viewer, final Collection visiblePlayers) { - // Retrieve the players in this world - List players = world.getCBWorld().getPlayers(); - StringBuilder playerBuilder = new StringBuilder(); - for (Player player : players) { - // If the viewer is the console or the viewier is allowed to see the player, show them. - // Make sure we're also ONLY showing online players. - // Since we already checked visible players, we'll just make sure who we're about to show is in that. - if (visiblePlayers.contains(player)) - playerBuilder.append(player.getDisplayName()).append(", "); - } - String bString = playerBuilder.toString(); - return (bString.length() == 0) ? "No players found." : bString.substring(0, bString.length() - 2); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/commands/package-info.java deleted file mode 100644 index 53d7f9b3f..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains all Commands. - */ -package com.onarandombox.MultiverseCore.commands; diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/CommandQueueManager.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/CommandQueueManager.java deleted file mode 100644 index 8f6a30238..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/CommandQueueManager.java +++ /dev/null @@ -1,161 +0,0 @@ -/****************************************************************************** - * 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; - -/** - *

Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.

- * - *

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.

- */ -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 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; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/DummyCommandBlockSender.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/DummyCommandBlockSender.java deleted file mode 100644 index 7ec3dfe03..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/DummyCommandBlockSender.java +++ /dev/null @@ -1,104 +0,0 @@ -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 getEffectivePermissions() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isOp() { - throw new UnsupportedOperationException(); - } - - @Override - public void setOp(boolean value) { - throw new UnsupportedOperationException(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/QueuedCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/QueuedCommand.java deleted file mode 100644 index 5dedc99f6..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/QueuedCommand.java +++ /dev/null @@ -1,86 +0,0 @@ -/****************************************************************************** - * 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; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/EntryFee.java b/src/main/java/com/onarandombox/MultiverseCore/configuration/EntryFee.java deleted file mode 100644 index 3fff6fe3a..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/EntryFee.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.onarandombox.MultiverseCore.configuration; - -import java.util.Map; - -import com.onarandombox.MultiverseCore.utils.MaterialConverter; -import me.main__.util.SerializationConfig.Property; -import me.main__.util.SerializationConfig.SerializationConfig; - -import me.main__.util.SerializationConfig.Serializor; -import org.bukkit.Material; -import org.bukkit.configuration.serialization.SerializableAs; -import org.jetbrains.annotations.Nullable; - -/** - * Entryfee-settings. - */ -@SerializableAs("MVEntryFee") -public class EntryFee extends SerializationConfig { - @Property - private double amount; - @Property(serializor = EntryFeeCurrencySerializor.class) - @Nullable - private Material currency; - - private final Material DISABLED_MATERIAL = Material.AIR; - - public EntryFee() { - super(); - } - - public EntryFee(Map values) { - super(values); - } - - /** - * {@inheritDoc} - */ - @Override - protected void setDefaults() { - amount = 0D; - currency = null; - } - - /** - * @return the amount - */ - public double getAmount() { - return amount; - } - - /** - * @return the currency - */ - @Nullable - public Material getCurrency() { - if (currency == null || currency.equals(DISABLED_MATERIAL)) { - return null; - } - return currency; - } - - /** - * Sets the amount. - * @param amount The new value. - */ - public void setAmount(double amount) { - this.amount = amount; - } - - /** - * Sets the currency. - * @param currency The new value. - */ - public void setCurrency(@Nullable Material currency) { - this.currency = currency; - } - - public static final class EntryFeeCurrencySerializor implements Serializor { - @Override - public String serialize(Material material) { - return material.toString(); - } - - @Override - public Material deserialize(Object o, Class aClass) { - return MaterialConverter.convertTypeString(o.toString()); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnSettings.java b/src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnSettings.java deleted file mode 100644 index 04f2cb71f..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnSettings.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.onarandombox.MultiverseCore.configuration; - -import java.util.Map; - -import me.main__.util.SerializationConfig.Property; -import me.main__.util.SerializationConfig.SerializationConfig; - -import org.bukkit.configuration.serialization.SerializableAs; - -/** - * Spawning-Settings. - */ -@SerializableAs("MVSpawnSettings") -public class SpawnSettings extends SerializationConfig { - @Property - private SubSpawnSettings animals; - @Property - private SubSpawnSettings monsters; - - public SpawnSettings() { - super(); - } - - public SpawnSettings(Map values) { - super(values); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDefaults() { - animals = new SubSpawnSettings(); - monsters = new SubSpawnSettings(); - } - - /** - * @return the animal-settings - */ - public SubSpawnSettings getAnimalSettings() { - return animals; - } - - /** - * @return the monster-settings - */ - public SubSpawnSettings getMonsterSettings() { - return monsters; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/SubSpawnSettings.java b/src/main/java/com/onarandombox/MultiverseCore/configuration/SubSpawnSettings.java deleted file mode 100644 index 5f9b1d721..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/SubSpawnSettings.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.onarandombox.MultiverseCore.configuration; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import me.main__.util.SerializationConfig.Property; -import me.main__.util.SerializationConfig.SerializationConfig; - -import org.bukkit.configuration.serialization.SerializableAs; - -/** - * SpawnSubSettings. - */ -@SerializableAs("MVSpawnSubSettings") -public class SubSpawnSettings extends SerializationConfig { - @Property - private boolean spawn; - @Property - private int spawnrate; - @Property - private List exceptions; - - public SubSpawnSettings() { - super(); - } - - public SubSpawnSettings(Map values) { - super(values); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDefaults() { - spawn = true; - exceptions = new ArrayList(); - spawnrate = -1; - } - - /** - * @return spawn - */ - public boolean doSpawn() { - return spawn; - } - - /** - * @param spawn The new value. - */ - public void setSpawn(boolean spawn) { - this.spawn = spawn; - } - - /** - * @return The exceptions - */ - public List getExceptions() { - return exceptions; - } - - /** - * @param rate The new spawn rate - */ - public void setSpawnRate(int rate) { - this.spawnrate = rate; - } - - /** - * @return The spawn rate - */ - public int getSpawnRate() { - return this.spawnrate; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/WorldPropertyValidator.java b/src/main/java/com/onarandombox/MultiverseCore/configuration/WorldPropertyValidator.java deleted file mode 100644 index b051eabd4..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/WorldPropertyValidator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.onarandombox.MultiverseCore.configuration; - -import org.bukkit.Bukkit; - -import com.onarandombox.MultiverseCore.MVWorld; -import com.onarandombox.MultiverseCore.event.MVWorldPropertyChangeEvent; - -import me.main__.util.SerializationConfig.ChangeDeniedException; -import me.main__.util.SerializationConfig.ObjectUsingValidator; - -/** - * Validates world-property-changes. - * @param The type of the property that should be validated. - */ -public class WorldPropertyValidator extends ObjectUsingValidator { - /** - * {@inheritDoc} - */ - @Override - public T validateChange(String property, T newValue, T oldValue, MVWorld object) throws ChangeDeniedException { - MVWorldPropertyChangeEvent event = new MVWorldPropertyChangeEvent(object, null, property, newValue); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) - throw new ChangeDeniedException(); - return event.getTheNewValue(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/configuration/package-info.java deleted file mode 100644 index 0f4c930b8..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains the Multiverse-configuration. - */ -package com.onarandombox.MultiverseCore.configuration; diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/AnchorDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/AnchorDestination.java deleted file mode 100644 index 0efea0173..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/AnchorDestination.java +++ /dev/null @@ -1,148 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -import java.util.Arrays; -import java.util.List; - -/** - * An anchor-{@link MVDestination}. - */ -public class AnchorDestination implements MVDestination { - private boolean isValid; - private Location location; - private MultiverseCore plugin; - private String name; - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "a"; - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return false; - } - this.plugin = (MultiverseCore) plugin; - List parsed = Arrays.asList(destination.split(":")); - // Need at least: a:name - if (!(parsed.size() == 2)) { - return false; - } - // If it's not an Anchor type - return parsed.get(0).equalsIgnoreCase("a"); - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - return this.location; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return; - } - this.plugin = (MultiverseCore) plugin; - List parsed = Arrays.asList(destination.split(":")); - // Need at least: e:world:x,y,z - // OR e:world:x,y,z:pitch:yaw - // so basically 3 or 5 - if (!(parsed.size() == 2)) { - this.isValid = false; - return; - } - this.name = parsed.get(1); - this.location = this.plugin.getAnchorManager().getAnchorLocation(parsed.get(1)); - if (this.location == null) { - this.isValid = false; - return; - } - if (!parsed.get(0).equalsIgnoreCase(this.getIdentifier())) { - this.isValid = false; - } - this.isValid = true; - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "Anchor"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "Anchor: " + this.name; - } - - @Override - public String toString() { - if (isValid) { - return "a:" + this.name; - } - return "i:Invalid Destination"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - return "multiverse.access." + this.location.getWorld().getName(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - // This is an ANCHOR destination, don't safely teleport here. - return false; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/BedDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/BedDestination.java deleted file mode 100644 index 19b6ad0aa..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/BedDestination.java +++ /dev/null @@ -1,141 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -/** - * A bed-{@link MVDestination}. - */ -public class BedDestination implements MVDestination { - public static final String OWN_BED_STRING = "playerbed"; - private String playername = ""; - private boolean isValid; - private Location knownBedLoc; - private MultiverseCore plugin; - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "b"; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - String[] split = destination.split(":"); - boolean validFormat = split.length >= 1 && split.length <= 2 && split[0].equals(this.getIdentifier()); - - OfflinePlayer p = Bukkit.getOfflinePlayer(split[1]); - boolean validPlayer = p.getName() != null && !p.getName().equals(OWN_BED_STRING); - - if (validFormat && validPlayer) this.playername = p.getName(); - - this.isValid = destination.equals("b:" + OWN_BED_STRING) || (validFormat && validPlayer); - - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity entity) { - if (entity instanceof Player) { - if (this.playername.isEmpty()) - this.knownBedLoc = this.plugin.getBlockSafety().getSafeBedSpawn(((Player) entity).getBedSpawnLocation()); - else - this.knownBedLoc = this.plugin.getBlockSafety().getSafeBedSpawn(Bukkit.getOfflinePlayer(this.playername).getBedSpawnLocation()); - - if (this.knownBedLoc == null) { - ((Player) entity).sendMessage("The bed was " + ChatColor.RED + "invalid or blocked" + ChatColor.RESET + ". Sorry."); - } - return this.knownBedLoc; - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(); - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - this.plugin = (MultiverseCore) plugin; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "Bed"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "Bed"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - if (knownBedLoc != null) { - return "multiverse.access." + knownBedLoc.getWorld().getName(); - } - return ""; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - // Bukkit should have already checked this. - return false; - } - - @Override - public String toString() { - return "b:" + (playername.isEmpty() ? OWN_BED_STRING : playername); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/CannonDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/CannonDestination.java deleted file mode 100644 index e26acec32..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/CannonDestination.java +++ /dev/null @@ -1,227 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -import java.util.Arrays; -import java.util.List; - -/** - * A cannon-{@link MVDestination}. - */ -public class CannonDestination implements MVDestination { - private final String coordRegex = "(-?[\\d]+\\.?[\\d]*),(-?[\\d]+\\.?[\\d]*),(-?[\\d]+\\.?[\\d]*)"; - private boolean isValid; - private Location location; - private double speed; - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - double pitchRadians = Math.toRadians(location.getPitch()); - double yawRadians = Math.toRadians(location.getYaw()); - double x = Math.sin(yawRadians) * speed * -1; - double y = Math.sin(pitchRadians) * speed * -1; - double z = Math.cos(yawRadians) * speed; - // Account for the angle they were pointed, and take away velocity - x = Math.cos(pitchRadians) * x; - z = Math.cos(pitchRadians) * z; - return new Vector(x, y, z); - } - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "ca"; - } - - // NEED ca:world:x,y,z:pitch:yaw:speed - // so basically 6 - private static final int SPLIT_SIZE = 6; - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return false; - } - List parsed = Arrays.asList(destination.split(":")); - if (parsed.size() != SPLIT_SIZE) { - return false; - } - // If it's not an Cannon type - if (!parsed.get(0).equalsIgnoreCase("ca")) { - return false; - } - - // If it's not a MV world - if (!((MultiverseCore) plugin).getMVWorldManager().isMVWorld(parsed.get(1))) { - return false; - } - // Verify X,Y,Z are numbers - if (!parsed.get(2).matches(coordRegex)) { - return false; - } - - try { - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - Float.parseFloat(parsed.get(3)); - Float.parseFloat(parsed.get(4)); - Float.parseFloat(parsed.get(5)); - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - } catch (NumberFormatException e) { - return false; - } - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - return this.location; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return; - } - List parsed = Arrays.asList(destination.split(":")); - - if (parsed.size() != SPLIT_SIZE) { - this.isValid = false; - return; - } - if (!parsed.get(0).equalsIgnoreCase(this.getIdentifier())) { - this.isValid = false; - return; - } - - if (!((MultiverseCore) plugin).getMVWorldManager().isMVWorld(parsed.get(1))) { - this.isValid = false; - return; - } - - this.location = new Location(((MultiverseCore) plugin).getMVWorldManager().getMVWorld(parsed.get(1)).getCBWorld(), 0, 0, 0); - - if (!parsed.get(2).matches(this.coordRegex)) { - this.isValid = false; - return; - } - double[] coords = new double[3]; - String[] coordString = parsed.get(2).split(","); - for (int i = 0; i < 3; i++) { - try { - coords[i] = Double.parseDouble(coordString[i]); - } catch (NumberFormatException e) { - this.isValid = false; - return; - } - } - this.location.setX(coords[0]); - this.location.setY(coords[1]); - this.location.setZ(coords[2]); - - try { - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - this.location.setPitch(Float.parseFloat(parsed.get(3))); - this.location.setYaw(Float.parseFloat(parsed.get(4))); - this.speed = Math.abs(Float.parseFloat(parsed.get(5))); - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - } catch (NumberFormatException e) { - this.isValid = false; - return; - } - - this.isValid = true; - - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "Cannon!"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "Cannon (" + this.location.getX() + ", " + this.location.getY() + ", " + this.location.getZ() + ":" - + this.location.getPitch() + ":" + this.location.getYaw() + ":" + this.speed + ")"; - - } - - /** - * Sets this {@link CannonDestination}. - * - * @param location The {@link Location}. - * @param speed The speed. - */ - public void setDestination(Location location, double speed) { - if (location != null) { - this.location = location; - this.speed = Math.abs(speed); - this.isValid = true; - } - this.isValid = false; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - return "multiverse.access." + this.location.getWorld().getName(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - return false; - } - - @Override - public String toString() { - if (isValid) { - return "ca:" + location.getWorld().getName() + ":" + location.getX() + "," + location.getY() - + "," + location.getZ() + ":" + location.getPitch() + ":" + location.getYaw() + ":" + this.speed; - } - return "i:Invalid Destination"; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/CustomTeleporterDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/CustomTeleporterDestination.java deleted file mode 100644 index 858c3e342..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/CustomTeleporterDestination.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.onarandombox.MultiverseCore.destination; - -import com.onarandombox.MultiverseCore.api.Teleporter; -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.util.Vector; - -public abstract class CustomTeleporterDestination implements MVDestination { - @Override - public final Location getLocation(final Entity entity) { - throw new UnsupportedOperationException(); - } - - @Override - public final Vector getVelocity() { - throw new UnsupportedOperationException(); - } - - @Override - public final boolean useSafeTeleporter() { - throw new UnsupportedOperationException(); - } - - @Override - public abstract String toString(); - - public abstract Teleporter getTeleporter(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/DestinationFactory.java b/src/main/java/com/onarandombox/MultiverseCore/destination/DestinationFactory.java deleted file mode 100644 index 402342c3d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/DestinationFactory.java +++ /dev/null @@ -1,179 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.commands.TeleportCommand; -import com.onarandombox.MultiverseCore.utils.PermissionTools; -import com.onarandombox.MultiverseCore.utils.PlayerFinder; -import com.pneumaticraft.commandhandler.Command; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -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. - */ -public class DestinationFactory { - - private static final Pattern CANNON_PATTERN = Pattern.compile("(?i)cannon-[\\d]+(\\.[\\d]+)?"); - - private MultiverseCore plugin; - private Map> destList; - private Command teleportCommand; - - public DestinationFactory(MultiverseCore plugin) { - this.plugin = plugin; - this.destList = new HashMap>(); - List cmds = this.plugin.getCommandHandler().getAllCommands(); - for (Command c : cmds) { - if (c instanceof TeleportCommand) { - this.teleportCommand = c; - } - } - } - - /** - * Parse a destination that has relation to sender, such as a cannon or player destination. - * - * @param teleportee The player that is going to be teleported. - * @param destinationName The destination to parse. - * @return A non-null MVDestination - */ - @NotNull - public MVDestination getPlayerAwareDestination(@NotNull Player teleportee, - @NotNull String destinationName) { - - // Prioritise world, in the event that a world is named after a player online. - if (Bukkit.getWorld(destinationName) != null) { - return getDestination(destinationName); - } - - Player targetPlayer = PlayerFinder.get(destinationName, teleportee); - if (targetPlayer != null) { - return getDestination("pl:" + targetPlayer.getName()); - } - - if (CANNON_PATTERN.matcher(destinationName).matches()) { - return getDestination(parseCannonDest(teleportee, destinationName)); - } - - return getDestination(destinationName); - } - - /** - * Parses a cannon destination. - * - * @param teleportee The player that is going to be teleported. - * @param destinationName The destination to parse. - * @return A destination string. - */ - @NotNull - private String parseCannonDest(@NotNull Player teleportee, - @NotNull String destinationName) { - - String[] cannonSpeed = destinationName.split("-"); - try { - double speed = Double.parseDouble(cannonSpeed[1]); - destinationName = "ca:" + teleportee.getWorld().getName() + ":" + teleportee.getLocation().getX() - + "," + teleportee.getLocation().getY() + "," + teleportee.getLocation().getZ() + ":" - + teleportee.getLocation().getPitch() + ":" + teleportee.getLocation().getYaw() + ":" + speed; - } - catch (Exception e) { - destinationName = "i:invalid"; - } - - return destinationName; - } - - /** - * Gets a new destination from a string. - * Returns a new InvalidDestination if the string could not be parsed. - * - * @param destination The destination in string format. - * - * @return A non-null MVDestination - */ - public MVDestination getDestination(String destination) { - String idenChar = ""; - if (destination.split(":").length > 1) { - idenChar = destination.split(":")[0]; - } - - if (this.destList.containsKey(idenChar)) { - Class myClass = this.destList.get(idenChar); - try { - MVDestination mydest = myClass.newInstance(); - if (!mydest.isThisType(this.plugin, destination)) { - return new InvalidDestination(); - } - mydest.setDestination(this.plugin, destination); - return mydest; - } catch (InstantiationException e) { - } catch (IllegalAccessException e) { - } - } - return new InvalidDestination(); - } - - /** - * Registers a {@link MVDestination}. - * - * @param c The {@link Class} of the {@link MVDestination} to register. - * @param identifier The {@link String}-identifier. - * @return True if the class was successfully registered. - */ - public boolean registerDestinationType(Class c, String identifier) { - if (this.destList.containsKey(identifier)) { - return false; - } - this.destList.put(identifier, c); - // Special case for world defaults: - if (identifier.equals("")) { - identifier = "w"; - } - Permission self = this.plugin.getServer().getPluginManager().getPermission("multiverse.teleport.self." + identifier); - Permission other = this.plugin.getServer().getPluginManager().getPermission("multiverse.teleport.other." + identifier); - PermissionTools pt = new PermissionTools(this.plugin); - if (self == null) { - self = new Permission("multiverse.teleport.self." + identifier, - "Permission to teleport yourself for the " + identifier + " destination.", PermissionDefault.OP); - this.plugin.getServer().getPluginManager().addPermission(self); - pt.addToParentPerms("multiverse.teleport.self." + identifier); - } - if (other == null) { - other = new Permission("multiverse.teleport.other." + identifier, - "Permission to teleport others for the " + identifier + " destination.", PermissionDefault.OP); - this.plugin.getServer().getPluginManager().addPermission(other); - pt.addToParentPerms("multiverse.teleport.other." + identifier); - } - this.teleportCommand.addAdditonalPermission(self); - this.teleportCommand.addAdditonalPermission(other); - return true; - } - - /** - * Gets all the {@link MVDestination} identifiers registered. - * - * @return A collection of destination identifiers. - */ - public Collection getRegisteredIdentifiers() { - return this.destList.keySet(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/ExactDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/ExactDestination.java deleted file mode 100644 index 081628933..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/ExactDestination.java +++ /dev/null @@ -1,263 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -import java.util.Arrays; -import java.util.List; - -/** - * An exact {@link MVDestination}. - */ -public class ExactDestination implements MVDestination { - private final String coordRegex = "(-?[\\d]+\\.?[\\d]*|~-?[\\d]+\\.?[\\d]*|~),(-?[\\d]+\\.?[\\d]*|~-?[\\d]+\\.?[\\d]*|~),(-?[\\d]+\\.?[\\d]*|~-?[\\d]+\\.?[\\d]*|~)"; - private boolean isValid; - private Location location; - private boolean relativeX, relativeY, relativeZ; - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "e"; - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return false; - } - List parsed = Arrays.asList(destination.split(":")); - // Need at least: e:world:x,y,z - // OR e:world:x,y,z:pitch:yaw - // so basically 3 or 5 - if (!(parsed.size() == 3 || parsed.size() == 5)) { // SUPPRESS CHECKSTYLE: MagicNumberCheck - return false; - } - // If it's not an Exact type - if (!parsed.get(0).equalsIgnoreCase("e")) { - return false; - } - - // If it's not a MV world - if (!((MultiverseCore) plugin).getMVWorldManager().isMVWorld(parsed.get(1))) { - return false; - } - - if (!parsed.get(2).matches(coordRegex)) { - return false; - } - // This is 1 now, because we've removed 2 - if (parsed.size() == 3) { - return true; - } - - try { - Float.parseFloat(parsed.get(3)); - Float.parseFloat(parsed.get(4)); // SUPPRESS CHECKSTYLE: MagicNumberCheck - } catch (NumberFormatException e) { - return false; - } - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - Location loc = this.location.clone(); - if (relativeX || relativeY || relativeZ) { - Location eLoc = e.getLocation(); - loc.add(relativeX ? eLoc.getX() : 0, relativeY ? eLoc.getY() : 0, relativeZ ? eLoc.getZ() : 0); - // Since the location is relative, it makes sense to use the entity's pitch and yaw unless those were - // specified in the destination. - if (loc.getPitch() == 0) { - loc.setPitch(eLoc.getPitch()); - } - if (loc.getYaw() == 0) { - loc.setYaw(eLoc.getYaw()); - } - } - return loc; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - if (!(plugin instanceof MultiverseCore)) { - return; - } - List parsed = Arrays.asList(destination.split(":")); - // Need at least: e:world:x,y,z - // OR e:world:x,y,z:pitch:yaw - // so basically 3 or 5 - if (!(parsed.size() == 3 || parsed.size() == 5)) { // SUPPRESS CHECKSTYLE: MagicNumberCheck - this.isValid = false; - return; - } - - if (!parsed.get(0).equalsIgnoreCase(this.getIdentifier())) { - this.isValid = false; - return; - } - - if (!((MultiverseCore) plugin).getMVWorldManager().isMVWorld(parsed.get(1))) { - this.isValid = false; - return; - } - this.location = new Location(((MultiverseCore) plugin).getMVWorldManager().getMVWorld(parsed.get(1)).getCBWorld(), 0, 0, 0); - - if (!parsed.get(2).matches(this.coordRegex)) { - this.isValid = false; - return; - } - double[] coords = new double[3]; - String[] coordString = parsed.get(2).split(","); - for (int i = 0; i < 3; i++) { - String[] relSplit = coordString[i].split("~"); - boolean relative = false; - if (relSplit.length == 0) { - // coord is "~" form - relative = true; - coords[i] = 0; - } else if (relSplit.length == 1) { - // coord is "123" form - try { - coords[i] = Double.parseDouble(relSplit[0]); - } catch (NumberFormatException e) { - this.isValid = false; - return; - } - } else { - // coord is "~123" form - relative = true; - try { - coords[i] = Double.parseDouble(relSplit[1]); - } catch (NumberFormatException e) { - this.isValid = false; - return; - } - } - if (relative) { - switch (i) { - case 0: - relativeX = true; - break; - case 1: - relativeY = true; - break; - case 2: - relativeZ = true; - break; - } - } - } - this.location.setX(coords[0]); - this.location.setY(coords[1]); - this.location.setZ(coords[2]); - - if (parsed.size() == 3) { - this.isValid = true; - return; - } - - try { - this.location.setPitch(Float.parseFloat(parsed.get(3))); - this.location.setYaw(Float.parseFloat(parsed.get(4))); // SUPPRESS CHECKSTYLE: MagicNumberCheck - } catch (NumberFormatException e) { - this.isValid = false; - return; - } - this.isValid = true; - - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "Exact"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "Exact (" + this.location.getX() + ", " + this.location.getY() + ", " + this.location.getZ() - + ":" + location.getPitch() + ":" + location.getYaw() + ")"; - } - - /** - * Sets this {@link ExactDestination}. - * - * @param location The {@link Location}. - */ - public void setDestination(Location location) { - this.isValid = (this.location = location) != null; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - if (isValid) { - return "e:" + location.getWorld().getName() + ":" + location.getX() + "," + location.getY() - + "," + location.getZ() + ":" + location.getPitch() + ":" + location.getYaw(); - } - return "i:Invalid Destination"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - return "multiverse.access." + this.location.getWorld().getName(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - // This is an EXACT destination, don't safely teleport here. - return false; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/InvalidDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/InvalidDestination.java deleted file mode 100644 index 85e99b65d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/InvalidDestination.java +++ /dev/null @@ -1,107 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -/** - * An invalid {@link MVDestination}. - */ -public class InvalidDestination implements MVDestination { - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "i"; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - // Nothing needed, it's invalid. - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return ChatColor.RED + "Invalid Destination"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return ChatColor.RED + "Invalid Destination"; - } - - @Override - public String toString() { - return "i:Invalid Destination"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - return false; - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/PlayerDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/PlayerDestination.java deleted file mode 100644 index 0ca08f985..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/PlayerDestination.java +++ /dev/null @@ -1,137 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -/** - * A player-{@link MVDestination}. - */ -public class PlayerDestination implements MVDestination { - private String player; - private boolean isValid; - private JavaPlugin plugin; - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "pl"; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - String[] items = destination.split(":"); - if (items.length != 2) { - return false; - } - if (!items[0].equalsIgnoreCase("pl")) { - return false; - } - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - Player p = plugin.getServer().getPlayerExact(this.player); - Player plLoc = null; - if (e instanceof Player) { - plLoc = (Player) e; - } else if (e.getPassenger() instanceof Player) { - plLoc = (Player) e.getPassenger(); - } - - if (p != null && plLoc != null) { - return p.getLocation(); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - String[] items = destination.split(":"); - if (items.length != 2) { - this.isValid = false; - } - if (!items[0].equalsIgnoreCase("pl")) { - this.isValid = false; - } - this.isValid = true; - this.player = items[1]; - this.plugin = plugin; - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "Player"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return this.player; - } - - @Override - public String toString() { - return "pl:" + this.player; - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - return ""; - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - return true; - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/WorldDestination.java b/src/main/java/com/onarandombox/MultiverseCore/destination/WorldDestination.java deleted file mode 100644 index 6cbc03510..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/WorldDestination.java +++ /dev/null @@ -1,173 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.destination; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.Core; -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.Vector; - -/** - * A world-{@link MVDestination}. - */ -public class WorldDestination implements MVDestination { - private boolean isValid; - private MultiverseWorld world; - private float yaw = -1; - private String direction = ""; - - /** - * {@inheritDoc} - */ - @Override - public String getIdentifier() { - return "w"; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isThisType(JavaPlugin plugin, String destination) { - String[] items = destination.split(":"); - if (items.length > 3) { - return false; - } - if (items.length == 1 && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[0])) { - // This case is: world - return true; - } - if (items.length == 2 && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[0])) { - // This case is: world:n - return true; - } else if (items[0].equalsIgnoreCase("w") && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[1])) { - // This case is: w:world - // and w:world:ne - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getLocation(Entity e) { - Location spawnLoc = getAcurateSpawnLocation(e, this.world); - if (this.yaw >= 0) { - // Only modify the yaw if its set. - spawnLoc.setYaw(this.yaw); - } - return spawnLoc; - } - - private static Location getAcurateSpawnLocation(Entity e, MultiverseWorld world) { - if (world != null) { - return world.getSpawnLocation(); - } else { - // add 0.5 to x and z to center people - // (spawn location is stored as int meaning that you would spawn in the corner of a block) - return e.getWorld().getSpawnLocation().add(.5, 0, .5); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDestination(JavaPlugin plugin, String destination) { - // TODO Taking a JavaPlugin here is rather useless, if we keep casting it up to MultiverseCore. - // We should change that. - Core core = (Core) plugin; - String[] items = destination.split(":"); - if (items.length > 3) { - isValid = false; - return; - } - if (items.length == 1 && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[0])) { - isValid = true; - this.world = core.getMVWorldManager().getMVWorld(items[0]); - return; - } - if (items.length == 2 && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[0])) { - this.world = core.getMVWorldManager().getMVWorld(items[0]); - this.yaw = core.getLocationManipulation().getYaw(items[1]); - return; - } - if (items[0].equalsIgnoreCase("w") && ((MultiverseCore) plugin).getMVWorldManager().isMVWorld(items[1])) { - this.world = ((MultiverseCore) plugin).getMVWorldManager().getMVWorld(items[1]); - isValid = true; - if (items.length == 3) { - this.yaw = core.getLocationManipulation().getYaw(items[2]); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public String getType() { - return "World"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return this.world.getColoredWorldString(); - } - - @Override - public String toString() { - if (direction.length() > 0 && yaw >= 0) { - return this.world.getCBWorld().getName() + ":" + this.direction; - } - return this.world.getCBWorld().getName(); - } - - /** - * {@inheritDoc} - */ - @Override - public String getRequiredPermission() { - // TODO: Potenitally replace spaces wiht tabs for friendlier yaml. - // this.world.getName().replace(" ","_"); - return "multiverse.access." + this.world.getName(); - } - - /** - * {@inheritDoc} - */ - @Override - public Vector getVelocity() { - return new Vector(0, 0, 0); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean useSafeTeleporter() { - return true; - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/ColorAlternator.java b/src/main/java/com/onarandombox/MultiverseCore/display/ColorAlternator.java deleted file mode 100644 index 561394a91..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/ColorAlternator.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import org.bukkit.ChatColor; -import org.jetbrains.annotations.NotNull; - -/** - * Helper class to switch between 2 {@link ChatColor}. - */ -public class ColorAlternator implements ColorTool { - - /** - * Creates a new {@link ColorAlternator} with 2 {@link ChatColor}s. - * - * @param colorThis The first color. - * @param colorThat The second color. - * @return The {@link ColorAlternator} created for you. - */ - public static ColorAlternator with(@NotNull ChatColor colorThis, - @NotNull ChatColor colorThat) { - - return new ColorAlternator(colorThis, colorThat); - } - - private boolean switcher; - private final ChatColor thisColor; - private final ChatColor thatColor; - - /** - * @param colorThis The first color. - * @param colorThat The second color. - */ - public ColorAlternator(@NotNull ChatColor colorThis, - @NotNull ChatColor colorThat) { - - this.thisColor = colorThis; - this.thatColor = colorThat; - } - - /** - * Gets the color. Everytime this method is called, it swaps the color that it returns. - * - * @return The color. - */ - @Override - public ChatColor get() { - return (this.switcher ^= true) ? this.thisColor : this.thatColor; - } - - /** - * @return The first color. - */ - public ChatColor getThisColor() { - return thisColor; - } - - /** - * @return The second color. - */ - public ChatColor getThatColor() { - return thatColor; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/ColorTool.java b/src/main/java/com/onarandombox/MultiverseCore/display/ColorTool.java deleted file mode 100644 index 1c4fae679..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/ColorTool.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import org.bukkit.ChatColor; - -/** - * Tools to allow customisation. - */ -@FunctionalInterface -public interface ColorTool { - - /** - * Gets a chat color. - * - * @return The color. - */ - ChatColor get(); - - /** - * Default implementation of this interface. Returns a default white color. - */ - ColorTool DEFAULT = () -> ChatColor.WHITE; -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/ContentDisplay.java b/src/main/java/com/onarandombox/MultiverseCore/display/ContentDisplay.java deleted file mode 100644 index c5d9e39c6..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/ContentDisplay.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import com.onarandombox.MultiverseCore.display.settings.DisplaySetting; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.Map; -import java.util.Objects; -import java.util.WeakHashMap; - -/** - * Helps to display contents such as list and maps in a nicely formatted fashion. - * - * @param Type of content to display. - */ -public class ContentDisplay { - - public static final String LINE_BREAK = "%br%"; - - /** - * Creates a ContentDisplay.Builder for the given content. - * - * @param content The content to be displayed. - * @param The type of the content which can be inferred. - * @return A new Builder. - */ - public static Builder forContent(T content) { - return new Builder<>(content); - } - - /** - * Creates a ContentDisplay.Builder for the given collection of content. - * - * @param content The content to be displayed. - * @return A new Builder. - */ - public static Builder> forContent(Collection content) { - return new Builder<>(content).displayHandler(DisplayHandlers.LIST); - } - - /** - * Creates a ContentDisplay.Builder for the given map of content. - * - * @param content The content to be displayed. - * @return A new Builder. - */ - public static Builder> forContent(Map content) { - return new Builder<>(content).displayHandler(DisplayHandlers.INLINE_MAP); - } - - private final T contents; - - private String header; - private String emptyMessage = "No matching content to display."; - private DisplayHandler displayHandler; - private ColorTool colorTool = ColorTool.DEFAULT; - private ContentFilter filter = ContentFilter.DEFAULT; - private final Map, Object> settingsMap = new WeakHashMap<>(); - - private ContentDisplay(T contents) { - this.contents = contents; - } - - /** - * Do the actual displaying of contents to the sender. - * - * @param sender The CommandSender to show the display to. - */ - public void show(@NotNull CommandSender sender) { - Collection formattedContent; - try { - formattedContent = (this.contents == null) ? null : this.displayHandler.format(sender, this); - } catch (DisplayFormatException e) { - sender.sendMessage(String.format("%sError: %s", ChatColor.RED, e.getMessage())); - return; - } - this.displayHandler.sendHeader(sender, this); - this.displayHandler.sendSubHeader(sender, this); - this.displayHandler.sendBody(sender, this, formattedContent); - } - - /** - * @return Gets the header to display. - */ - public String getHeader() { - return header; - } - - /** - * Sets the header text. - */ - public void setHeader(@NotNull String header) { - this.header = header; - } - - /** - * @return Gets the contents to display. - */ - public T getContents() { - return contents; - } - - /** - * @return Gets the message to display when no content is shown. - */ - @NotNull - public String getEmptyMessage() { - return emptyMessage; - } - - /** - * @return Gets the display handler that formats and sends content to sender. - */ - @NotNull - public DisplayHandler getDisplayHandler() { - return displayHandler; - } - - /** - * @return Gets the color tool used. - */ - @NotNull - public ColorTool getColorTool() { - return colorTool; - } - - /** - * @return Gets the filter used. - */ - @NotNull - public ContentFilter getFilter() { - return filter; - } - - /** - * Gets the value for a given setting option. - * - * @param setting The setting option. - * @param The setting type. - * @return Value set for the given setting. - */ - public S getSetting(@NotNull DisplaySetting setting) { - return (S) settingsMap.getOrDefault(setting, setting.defaultValue()); - } - - /** - * Sets other specific settings that may be used by the {@link DisplayHandler}. - * - * @param setting The settings option. - * @param value The value to set. - * @param The type of setting. - */ - public void setSetting(@NotNull DisplaySetting setting, S value) { - this.settingsMap.put(setting, value); - } - - /** - * Builds a {@link ContentDisplay}. - * - * @param Type of content to display. - */ - public static class Builder { - - private final ContentDisplay display; - - private Builder(T content) { - this.display = new ContentDisplay<>(content); - } - - /** - * Sets header to be displayed. - * - * @param header The header text. - * @param replacements String formatting replacements. - * @return The builder. - */ - @NotNull - public Builder header(@NotNull String header, Object...replacements) { - this.display.header = String.format(header, replacements); - return this; - } - - /** - * Sets the message to show when no content is available for display. - * - * @param emptyMessage The message text. - * @param replacements String formatting replacements. - * @return The builder. - */ - @NotNull - public Builder emptyMessage(@NotNull String emptyMessage, Object...replacements) { - this.display.emptyMessage = String.format(emptyMessage, replacements); - return this; - } - - /** - * Sets the display handler that does the formatting and sending of content. Required. - * - * @param displayHandler The display handler for the given content type. - * @return The builder. - */ - @NotNull - public Builder displayHandler(@NotNull DisplayHandler displayHandler) { - this.display.displayHandler = displayHandler; - return this; - } - - /** - * Sets the color tool used to make messages more colourful. - * - * @param colorTool The color tool to use. - * @return The builder. - */ - @NotNull - public Builder colorTool(@NotNull ColorTool colorTool) { - this.display.colorTool = colorTool; - return this; - } - - /** - * Sets content filter used to match specific content to be displayed. - * - * @param filter The filter to use. - * @return The builder. - */ - @NotNull - public Builder filter(@NotNull ContentFilter filter) { - this.display.filter = filter; - return this; - } - - /** - * Sets other specific settings that may be used by the {@link DisplayHandler}. - * - * @param setting The settings option. - * @param value The value to set. - * @param The type of setting. - * @return The builder. - */ - @NotNull - public Builder setting(@NotNull DisplaySetting setting, S value) { - this.display.settingsMap.put(setting, value); - return this; - } - - /** - * Validates and build the content display. - * - * @return The content display. - */ - @NotNull - public ContentDisplay build() { - Objects.requireNonNull(this.display.displayHandler); - return this.display; - } - - /** - * Build and show the content to the sender. - * - * @param sender The CommandSender to show the display to. - */ - public void show(CommandSender sender) { - this.build().show(sender); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/ContentFilter.java b/src/main/java/com/onarandombox/MultiverseCore/display/ContentFilter.java deleted file mode 100644 index cd0bed8cc..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/ContentFilter.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import com.dumptruckman.minecraft.util.Logging; -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.

- * - *

Compile regex pattern based on {@link ContentFilter#filterString}. When prefixed with 'r=', - * use {@link ContentFilter#filterString} as the full regex pattern. Else, set to any match that - * contains the {@link ContentFilter#filterString}.

- */ -public class ContentFilter { - - public static final ContentFilter DEFAULT = new ContentFilter(); - private static final Pattern REGEX_SPECIAL_CHARS = Pattern.compile("[.+*?\\[^\\]$(){}=!<>|:-\\\\]"); - - private String filterString; - private Pattern filterPattern; - private boolean exactMatch; - - private ContentFilter() { - } - - /** - * @param filterString The text to do matching, either plaintext or regex. - */ - public ContentFilter(@NotNull String filterString) { - this(filterString, false); - } - - /** - * @param filterString The text to do matching, else plaintext or regex. - * @param exactMatch Should check for exact match when doing regex matching. - */ - public ContentFilter(@NotNull String filterString, - boolean exactMatch) { - - this.filterString = filterString; - this.exactMatch = exactMatch; - parseFilter(); - } - - private void parseFilter() { - if (filterString == null) { - return; - } - if (filterString.startsWith("r=")) { - convertToMatcher(filterString.substring(2)); - return; - } - String cleanedFilter = REGEX_SPECIAL_CHARS.matcher(filterString.toLowerCase()).replaceAll("\\\\$0"); - convertToMatcher("(?i).*" + cleanedFilter + ".*"); - } - - /** - * Compile and store the regex into a {@link Pattern}. - * - * @param regex The regex text. - */ - private void convertToMatcher(@NotNull String regex) { - try { - this.filterPattern = Pattern.compile(regex); - Logging.finest("Parsed regex pattern: %s", this.filterPattern.toString()); - } - catch (PatternSyntaxException ignored) { - Logging.warning("Error parsing regex: %s", filterString); - } - } - - /** - * Do regex matching. - * - * @param text String to check regex on. - * @return True of matches regex pattern, false otherwise. - */ - public boolean checkMatch(@Nullable Object text) { - if (!hasFilter()) { - return true; - } - if (text == null || !hasValidPattern()) { - return false; - } - text = ChatColor.stripColor(String.valueOf(text)); - return (exactMatch) - ? filterPattern.matcher((CharSequence) text).matches() - : filterPattern.matcher((CharSequence) text).find(); - } - - /** - * Checks if a filter string is present. - * - * @return True if there is a filter string, else false. - */ - public boolean hasFilter() { - return filterString != null; - } - - /** - * Checks if regex pattern syntax is valid. - * - * @return True if valid, else false. - */ - public boolean hasValidPattern() { - return filterPattern != null; - } - - /** - * @return The filter string. - */ - @Nullable - public String getString() { - return filterString; - } - - /** - * @return The regex pattern. - */ - @Nullable - public Pattern getPattern() { - return filterPattern; - } - - /** - * @return True if filter is set to do exact matching, else false. - */ - public boolean isExactMatch() { - return exactMatch; - } - - /** - * Nicely format the filter string to be used for showing the sender. - * - * @return The formatted filter string. - */ - public @NotNull String getFormattedString() { - return String.format("%sFilter: '%s'", ChatColor.ITALIC, filterString); - } - - @Override - public String toString() { - return "ContentFilter{" + - "filterString='" + filterString + '\'' + - ", filterPattern=" + filterPattern + - ", exactMatch=" + exactMatch + - '}'; - } -} \ No newline at end of file diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayFormatException.java b/src/main/java/com/onarandombox/MultiverseCore/display/DisplayFormatException.java deleted file mode 100644 index 604d6acdc..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayFormatException.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -/** - * Thrown when an issue occur while formatting content. - */ -public class DisplayFormatException extends Exception { - public DisplayFormatException() { - } - - public DisplayFormatException(String message) { - super(message); - } - - public DisplayFormatException(String message, Throwable cause) { - super(message, cause); - } - - public DisplayFormatException(Throwable cause) { - super(cause); - } - - public DisplayFormatException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandler.java b/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandler.java deleted file mode 100644 index 024f05f6a..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import com.google.common.base.Strings; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -/** - * Handles the formatting and sending of all content by the {@link ContentDisplay}. - * - * @param Type of content to display. - */ -@FunctionalInterface -public interface DisplayHandler { - - /** - * Formats the raw content into a {@link Collection} for displaying to the given sender. - * - * @param sender The {@link CommandSender} who will the content will be displayed to. - * @param display The responsible {@link ContentDisplay}. - * @return The formatted content. - * @throws DisplayFormatException Issue occurred while formatting content. E.g. invalid page. - */ - Collection format(@NotNull CommandSender sender, @NotNull ContentDisplay display) - throws DisplayFormatException; - - /** - * Sends the header. - * - * @param sender The {@link CommandSender} who will the header will be displayed to. - * @param display The responsible {@link ContentDisplay}. - */ - default void sendHeader(@NotNull CommandSender sender, @NotNull ContentDisplay display) { - if (!Strings.isNullOrEmpty(display.getHeader())) { - sender.sendMessage(display.getHeader()); - } - } - - /** - * Sends info such as filter and page. - * - * @param sender The {@link CommandSender} who will the sub header will be displayed to. - * @param display The responsible {@link ContentDisplay}. - */ - default void sendSubHeader(@NotNull CommandSender sender, @NotNull ContentDisplay display) { - if (display.getFilter().hasFilter()) { - sender.sendMessage(String.format("%s[ %s ]", ChatColor.GRAY, display.getFilter().getFormattedString())); - } - } - - /** - * Sends the content. - * - * @param sender The {@link CommandSender} who will the body will be displayed to. - * @param display The responsible {@link ContentDisplay}. - * @param formattedContent The content after being formatted by {@link #format(CommandSender, ContentDisplay)} - */ - default void sendBody(@NotNull CommandSender sender, @NotNull ContentDisplay display, - Collection formattedContent) { - if (formattedContent == null || formattedContent.size() == 0) { - sender.sendMessage(display.getEmptyMessage()); - return; - } - sender.sendMessage(formattedContent.toArray(new String[0])); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandlers.java b/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandlers.java deleted file mode 100644 index 7343c6ad5..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/DisplayHandlers.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.onarandombox.MultiverseCore.display; - -import com.onarandombox.MultiverseCore.display.handlers.InlineListDisplayHandler; -import com.onarandombox.MultiverseCore.display.handlers.InlineMapDisplayHandler; -import com.onarandombox.MultiverseCore.display.handlers.ListDisplayHandler; -import com.onarandombox.MultiverseCore.display.handlers.PagedListDisplayHandler; -import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings; -import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings; -import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings; - -import java.util.Collection; -import java.util.Map; - -/** - * Various implementations of {@link DisplayHandler}. - */ -public class DisplayHandlers { - - /** - * Standard list display. - * - * Supported settings: none. - */ - public static final DisplayHandler> LIST = new ListDisplayHandler(); - - /** - * List display with paging. - * - * Supported settings: {@link PagedDisplaySettings#SHOW_PAGE}, {@link PagedDisplaySettings#LINES_PER_PAGE}, - * {@link PagedDisplaySettings#PAGE_IN_CONSOLE}, {@link PagedDisplaySettings#DO_END_PADDING}. - */ - public static final DisplayHandler> PAGE_LIST = new PagedListDisplayHandler(); - - /** - * Display a list inline. - * - * Supported settings: {@link InlineDisplaySettings#SEPARATOR}. - */ - public static final DisplayHandler> INLINE_LIST = new InlineListDisplayHandler(); - - /** - * Display key value pair inline. - * - * Supported settings: {@link InlineDisplaySettings#SEPARATOR}, {@link MapDisplaySettings#OPERATOR}. - */ - public static final DisplayHandler> INLINE_MAP = new InlineMapDisplayHandler(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineListDisplayHandler.java b/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineListDisplayHandler.java deleted file mode 100644 index 53aa196b4..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineListDisplayHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.onarandombox.MultiverseCore.display.handlers; - -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.DisplayFormatException; -import com.onarandombox.MultiverseCore.display.DisplayHandler; -import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; - -public class InlineListDisplayHandler implements DisplayHandler> { - - @Override - public Collection format(@NotNull CommandSender sender, @NotNull ContentDisplay> display) - throws DisplayFormatException { - StringBuilder builder = new StringBuilder(); - String separator = display.getSetting(InlineDisplaySettings.SEPARATOR); - - for (Iterator iterator = display.getContents().iterator(); iterator.hasNext(); ) { - String content = iterator.next(); - if (!display.getFilter().checkMatch(content)) { - continue; - } - builder.append(display.getColorTool().get()).append(content); - if (iterator.hasNext()) { - builder.append(separator); - } - } - return (builder.length() == 0) - ? Collections.singletonList(display.getEmptyMessage()) - : Collections.singleton(builder.toString()); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineMapDisplayHandler.java b/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineMapDisplayHandler.java deleted file mode 100644 index 312ccb86c..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/InlineMapDisplayHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.onarandombox.MultiverseCore.display.handlers; - -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.DisplayFormatException; -import com.onarandombox.MultiverseCore.display.DisplayHandler; -import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings; -import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; - -public class InlineMapDisplayHandler implements DisplayHandler> { - - @Override - public Collection format(@NotNull CommandSender sender, - @NotNull ContentDisplay> display) - throws DisplayFormatException { - StringBuilder builder = new StringBuilder(); - String separator = display.getSetting(InlineDisplaySettings.SEPARATOR); - String operator = display.getSetting(MapDisplaySettings.OPERATOR); - - for (Iterator> iterator = display.getContents().entrySet().iterator(); iterator.hasNext(); ) { - Map.Entry entry = iterator.next(); - if (!display.getFilter().checkMatch(entry.getKey()) && !display.getFilter().checkMatch(entry.getValue())) { - continue; - } - builder.append(display.getColorTool().get()) - .append(entry.getKey()) - .append(operator) - .append(display.getColorTool().get()) - .append(entry.getValue()); - if (iterator.hasNext()) { - builder.append(separator); - } - } - return (builder.length() == 0) - ? Collections.singletonList(display.getEmptyMessage()) - : Collections.singleton(builder.toString()); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/ListDisplayHandler.java b/src/main/java/com/onarandombox/MultiverseCore/display/handlers/ListDisplayHandler.java deleted file mode 100644 index c2b0979fd..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/ListDisplayHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.onarandombox.MultiverseCore.display.handlers; - -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.DisplayFormatException; -import com.onarandombox.MultiverseCore.display.DisplayHandler; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.stream.Collectors; - -public class ListDisplayHandler implements DisplayHandler> { - - @Override - public Collection format(@NotNull CommandSender sender, @NotNull ContentDisplay> display) - throws DisplayFormatException { - return display.getContents().stream() - .filter(display.getFilter()::checkMatch) - .map(s -> (ContentDisplay.LINE_BREAK.equals(s)) ? "" : display.getColorTool().get() + s) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/PagedListDisplayHandler.java b/src/main/java/com/onarandombox/MultiverseCore/display/handlers/PagedListDisplayHandler.java deleted file mode 100644 index 17f0825e5..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/handlers/PagedListDisplayHandler.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.onarandombox.MultiverseCore.display.handlers; - -import com.onarandombox.MultiverseCore.display.ContentDisplay; -import com.onarandombox.MultiverseCore.display.DisplayFormatException; -import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.IntStream; - -public class PagedListDisplayHandler extends ListDisplayHandler { - - @Override - public Collection format(@NotNull CommandSender sender, @NotNull ContentDisplay> display) - throws DisplayFormatException { - if (dontNeedPaging(sender, display)) { - return super.format(sender, display); - } - - int pages = 1; - int currentLength = 0; - int targetPage = display.getSetting(PagedDisplaySettings.SHOW_PAGE); - int linesPerPage = display.getSetting(PagedDisplaySettings.LINES_PER_PAGE); - List content = new ArrayList<>(linesPerPage); - - // Calculate the paging. - for (String line : display.getContents()) { - if (!display.getFilter().checkMatch(line)) { - continue; - } - // When it's the next page. - boolean isLineBreak = ContentDisplay.LINE_BREAK.equals(line); - if (isLineBreak || ++currentLength > linesPerPage) { - pages++; - currentLength = 0; - if (isLineBreak) { - continue; - } - } - if (pages == targetPage) { - // Let first line be the header when no header is defined. - if (display.getHeader() == null) { - display.setHeader(line); - currentLength--; - continue; - } - content.add(display.getColorTool().get() + line); - } - } - - // Page out of range. - if (targetPage < 1 || targetPage > pages) { - if (pages == 1) { - throw new DisplayFormatException("There is only 1 page!"); - } - throw new DisplayFormatException("Please enter a page from 1 to " + pages + "."); - } - - // No content - if (content.size() == 0) { - content.add(display.getEmptyMessage()); - } - - // Add empty lines to make output length consistent. - if (display.getSetting(PagedDisplaySettings.DO_END_PADDING)) { - IntStream.range(0, linesPerPage - content.size()).forEach(i -> content.add("")); - } - display.setSetting(PagedDisplaySettings.TOTAL_PAGE, pages); - - return content; - } - - @Override - public void sendSubHeader(@NotNull CommandSender sender, @NotNull ContentDisplay> display) { - if (dontNeedPaging(sender, display)) { - super.sendSubHeader(sender, display); - return; - } - - if (display.getFilter().hasFilter()) { - sender.sendMessage(String.format("%s[ Page %s of %s, %s ]", - ChatColor.GRAY, - display.getSetting(PagedDisplaySettings.SHOW_PAGE), - display.getSetting(PagedDisplaySettings.TOTAL_PAGE), - display.getFilter().getFormattedString()) - ); - return; - } - sender.sendMessage(String.format("%s[ Page %s of %s ]", - ChatColor.GRAY, - display.getSetting(PagedDisplaySettings.SHOW_PAGE), - display.getSetting(PagedDisplaySettings.TOTAL_PAGE)) - ); - } - - private boolean dontNeedPaging(CommandSender sender, ContentDisplay> display) { - return sender instanceof ConsoleCommandSender - && !display.getSetting(PagedDisplaySettings.PAGE_IN_CONSOLE); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/settings/DisplaySetting.java b/src/main/java/com/onarandombox/MultiverseCore/display/settings/DisplaySetting.java deleted file mode 100644 index 232e432ba..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/settings/DisplaySetting.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.onarandombox.MultiverseCore.display.settings; - -import com.onarandombox.MultiverseCore.display.DisplayHandler; - -/** - * Represents a setting option that can be used by {@link DisplayHandler}. - * - * @param - */ -@FunctionalInterface -public interface DisplaySetting { - - /** - * Gets the default value of this Display Setting. - * - * @return The default value. - */ - T defaultValue(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/settings/InlineDisplaySettings.java b/src/main/java/com/onarandombox/MultiverseCore/display/settings/InlineDisplaySettings.java deleted file mode 100644 index 25661d121..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/settings/InlineDisplaySettings.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.onarandombox.MultiverseCore.display.settings; - -import com.onarandombox.MultiverseCore.display.DisplayHandler; -import org.bukkit.ChatColor; - -/** - * Collection of {@link DisplaySetting} that are used by various {@link DisplayHandler}. - */ -public class InlineDisplaySettings { - - /** - * Inline separator. E.g. '1, 2, 3' - */ - public static final DisplaySetting SEPARATOR = () -> ChatColor.WHITE + ", "; -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/settings/MapDisplaySettings.java b/src/main/java/com/onarandombox/MultiverseCore/display/settings/MapDisplaySettings.java deleted file mode 100644 index a78473955..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/settings/MapDisplaySettings.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.onarandombox.MultiverseCore.display.settings; - -import com.onarandombox.MultiverseCore.display.DisplayHandler; -import org.bukkit.ChatColor; - -/** - * Collection of {@link DisplaySetting} that are used by various {@link DisplayHandler}. - */ -public class MapDisplaySettings { - - /** - * The thing between a key value pair. E.g. 'Me = Smart' - */ - public static final DisplaySetting OPERATOR = () -> ChatColor.WHITE + " = "; -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/display/settings/PagedDisplaySettings.java b/src/main/java/com/onarandombox/MultiverseCore/display/settings/PagedDisplaySettings.java deleted file mode 100644 index bdc12f9ff..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/display/settings/PagedDisplaySettings.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.onarandombox.MultiverseCore.display.settings; - -public class PagedDisplaySettings { - - /** - * Page to display. - */ - public static final DisplaySetting SHOW_PAGE = () -> 1; - - /** - * Total pages available to display. - */ - public static final DisplaySetting TOTAL_PAGE = () -> 1; - - /** - * The max number of lines per page. This excludes header. - */ - public static final DisplaySetting LINES_PER_PAGE = () -> 8; - - /** - * Should add empty lines if content lines shown is less that {@link #LINES_PER_PAGE}. - */ - public static final DisplaySetting DO_END_PADDING = () -> true; - - /** - * Should display with paging when it's sent to console. - */ - public static final DisplaySetting PAGE_IN_CONSOLE = () -> false; - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/Action.java b/src/main/java/com/onarandombox/MultiverseCore/enums/Action.java deleted file mode 100644 index fa0569c47..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/Action.java +++ /dev/null @@ -1,30 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.enums; - -/** - * A enum containing all actions that can be used to modify world-properties. - */ -public enum Action { - /** - * Setting a property. - */ - Set, - /** - * Adding something to a list-property. - */ - Add, - /** - * Removing something from a list-property. - */ - Remove, - /** - * Clearing a list-property. - */ - Clear -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/AddProperties.java b/src/main/java/com/onarandombox/MultiverseCore/enums/AddProperties.java deleted file mode 100644 index 9f08418dc..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/AddProperties.java +++ /dev/null @@ -1,26 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.enums; - -/** - * An enum containing all list-properties. - */ -public enum AddProperties { - /** - * Worlds that people cannot go to from a world. - */ - worldblacklist, - /** - * Animal-exceptions. - */ - animals, - /** - * Monster-exceptions. - */ - monsters -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatColor.java b/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatColor.java deleted file mode 100644 index 87121a55e..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatColor.java +++ /dev/null @@ -1,95 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.enums; - -import org.bukkit.ChatColor; - -/** - * A regular {@link ChatColor} represented by an english string. - * @see ChatColor - */ -public enum EnglishChatColor { - // BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable - AQUA(ChatColor.AQUA), - BLACK(ChatColor.BLACK), - BLUE(ChatColor.BLUE), - DARKAQUA(ChatColor.DARK_AQUA), - DARKBLUE(ChatColor.DARK_BLUE), - DARKGRAY(ChatColor.DARK_GRAY), - DARKGREEN(ChatColor.DARK_GREEN), - DARKPURPLE(ChatColor.DARK_PURPLE), - DARKRED(ChatColor.DARK_RED), - GOLD(ChatColor.GOLD), - GRAY(ChatColor.GRAY), - GREEN(ChatColor.GREEN), - LIGHTPURPLE(ChatColor.LIGHT_PURPLE), - RED(ChatColor.RED), - YELLOW(ChatColor.YELLOW), - WHITE(ChatColor.WHITE); - // END CHECKSTYLE-SUPPRESSION: JavadocVariable - - private final ChatColor color; - //private final String text; - - EnglishChatColor(ChatColor color) { - this.color = color; - } - - /** - * Gets the text. - * @return The text. - */ - public String getText() { - return this.name(); - } - - /** - * Gets the color. - * @return The color as {@link ChatColor}. - */ - public ChatColor getColor() { - return this.color; - } - - /** - * Constructs a string containing all available colors. - * @return That {@link String}. - */ - public static String getAllColors() { - String buffer = ""; - for (EnglishChatColor c : EnglishChatColor.values()) { - buffer += c.getColor() + c.getText() + " "; - } - return buffer; - } - - /** - * Constructs an {@link EnglishChatColor} from a {@link String}. - * @param text The {@link String}. - * @return The {@link EnglishChatColor}. - */ - public static EnglishChatColor fromString(String text) { - if (text != null) { - for (EnglishChatColor c : EnglishChatColor.values()) { - if (text.equalsIgnoreCase(c.name())) { - return c; - } - } - } - return null; - } - - /** - * Looks if the given-color name is a valid color. - * @param aliasColor A color-name. - * @return True if the name is a valid color, false if it isn't. - */ - public static boolean isValidAliasColor(String aliasColor) { - return (EnglishChatColor.fromString(aliasColor) != null); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatStyle.java b/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatStyle.java deleted file mode 100644 index e1bdf1cae..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/EnglishChatStyle.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.onarandombox.MultiverseCore.enums; - -import org.bukkit.ChatColor; - -/** - * A regular {@link ChatColor} represented by an english string. - * @see ChatColor - */ -public enum EnglishChatStyle { - // BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable - /** No style. */ - NORMAL(null), - MAGIC(ChatColor.MAGIC), - BOLD(ChatColor.BOLD), - STRIKETHROUGH(ChatColor.STRIKETHROUGH), - UNDERLINE(ChatColor.UNDERLINE), - ITALIC(ChatColor.ITALIC); - // END CHECKSTYLE-SUPPRESSION: JavadocVariable - - private final ChatColor color; - - EnglishChatStyle(ChatColor color) { - this.color = color; - } - - /** - * Gets the color. - * @return The color as {@link ChatColor}. - */ - public ChatColor getColor() { - return color; - } - - /** - * Constructs an {@link EnglishChatStyle} from a {@link String}. - * @param text The {@link String}. - * @return The {@link EnglishChatStyle}. - */ - public static EnglishChatStyle fromString(String text) { - if (text != null) { - for (EnglishChatStyle c : EnglishChatStyle.values()) { - if (text.equalsIgnoreCase(c.name())) { - return c; - } - } - } - return null; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/RespawnType.java b/src/main/java/com/onarandombox/MultiverseCore/enums/RespawnType.java deleted file mode 100644 index 889f0e43d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/RespawnType.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.onarandombox.MultiverseCore.enums; - -public enum RespawnType { - BED, - ANCHOR, - OTHER -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/TeleportResult.java b/src/main/java/com/onarandombox/MultiverseCore/enums/TeleportResult.java deleted file mode 100644 index 29df2b3c6..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/TeleportResult.java +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.enums; - -/** - * An enum containing possible teleport-results. - */ -public enum TeleportResult { - /** - * Insufficient permissions. - */ - FAIL_PERMISSION, - /** - * The teleport was unsafe. - */ - FAIL_UNSAFE, - /** - * The player was to poor. - */ - FAIL_TOO_POOR, - /** - * The teleport was invalid. - */ - FAIL_INVALID, - /** - * Unknown reason. - */ - FAIL_OTHER, - /** - * The player was successfully teleported. - */ - SUCCESS -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/enums/package-info.java deleted file mode 100644 index a34f2197b..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains all enums. - */ -package com.onarandombox.MultiverseCore.enums; diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVWorldPropertyChangeEvent.java b/src/main/java/com/onarandombox/MultiverseCore/event/MVWorldPropertyChangeEvent.java deleted file mode 100644 index d85e104a7..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVWorldPropertyChangeEvent.java +++ /dev/null @@ -1,133 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.event; - -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.command.CommandSender; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -/** - * This event is fired *before* the property is actually changed. - *

- * If it is cancelled, no change will happen. - *

- * If you want to get the values of the world before the change, query the world. - * To get the name of the property that was changed, use {@link #getPropertyName()}. - * To get the new value, use {@link #getTheNewValue()}. To change it, use {@link #setTheNewValue(Object)}. - * @param The type of the property that was set. - */ -public class MVWorldPropertyChangeEvent extends Event implements Cancellable { - private MultiverseWorld world; - private CommandSender changer; - private boolean isCancelled = false; - private String name; - private T value; - - public MVWorldPropertyChangeEvent(MultiverseWorld world, CommandSender changer, String name, T value) { - this.world = world; - this.changer = changer; - this.name = name; - this.value = value; - } - - private static final HandlerList HANDLERS = new HandlerList(); - - /** - * {@inheritDoc} - */ - @Override - public HandlerList getHandlers() { - return HANDLERS; - } - - /** - * Gets the handler list. This is required by the event system. - * @return A list of handlers. - */ - public static HandlerList getHandlerList() { - return HANDLERS; - } - - /** - * Gets the changed world property's name. - * @return The changed world property's name. - */ - public String getPropertyName() { - return this.name; - } - - /** - * Gets the new value. - * @return The new value. - * @deprecated Use {@link #getTheNewValue()} instead. - */ - @Deprecated - public String getNewValue() { - return this.value.toString(); - } - - /** - * Gets the new value. - * @return The new value. - */ - public T getTheNewValue() { - return this.value; - } - - /** - * Sets the new value. - *

- * This method is only a stub, it'll always throw an {@link UnsupportedOperationException}! - * @param value The new new value. - * @deprecated Use {@link #setTheNewValue(Object)} instead. - */ - @Deprecated - public void setNewValue(String value) { - throw new UnsupportedOperationException(); - } - - /** - * Sets the new value. - * @param value The new value. - */ - public void setTheNewValue(T value) { - this.value = value; - } - - /** - * Get the world targeted because of this change. - * - * @return A valid MultiverseWorld. - */ - public MultiverseWorld getWorld() { - return this.world; - } - - /** - * Gets the person (or console) who was responsible for the change. - *

- * This may be null! - * - * @return The person (or console) who was responsible for the change. - */ - public CommandSender getCommandSender() { - return this.changer; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean cancelled) { - this.isCancelled = cancelled; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/event/package-info.java deleted file mode 100644 index d3a9daeb2..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/event/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains all events. - */ -package com.onarandombox.MultiverseCore.event; diff --git a/src/main/java/com/onarandombox/MultiverseCore/exceptions/PropertyDoesNotExistException.java b/src/main/java/com/onarandombox/MultiverseCore/exceptions/PropertyDoesNotExistException.java deleted file mode 100644 index 95a241693..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/exceptions/PropertyDoesNotExistException.java +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.exceptions; - -/** - * Thrown when a world-property doesn't exist. - */ -public class PropertyDoesNotExistException extends Exception { - public PropertyDoesNotExistException(String name) { - super(name); - } - - public PropertyDoesNotExistException(String name, Throwable cause) { - super(name, cause); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/exceptions/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/exceptions/package-info.java deleted file mode 100644 index cd84cf4ac..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/exceptions/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains all exceptions. - */ -package com.onarandombox.MultiverseCore.exceptions; diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/AsyncChatEvent.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/AsyncChatEvent.java deleted file mode 100644 index 38d48bef6..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/AsyncChatEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.onarandombox.MultiverseCore.listeners; - -import org.bukkit.entity.Player; -import org.bukkit.event.player.AsyncPlayerChatEvent; - -/** - * A wrapper for the {@link AsyncPlayerChatEvent}. - */ -public class AsyncChatEvent implements ChatEvent { - private final AsyncPlayerChatEvent event; - - public AsyncChatEvent(AsyncPlayerChatEvent event) { - this.event = event; - } - - @Override - public boolean isCancelled() { - return event.isCancelled(); - } - - @Override - public String getFormat() { - return event.getFormat(); - } - - @Override - public void setFormat(String s) { - event.setFormat(s); - } - - @Override - public Player getPlayer() { - return event.getPlayer(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/ChatEvent.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/ChatEvent.java deleted file mode 100644 index 16cafb81a..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/ChatEvent.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.onarandombox.MultiverseCore.listeners; - -import org.bukkit.entity.Player; - -/** - * A wrapper for the two chat-events in Bukkit. - */ -public interface ChatEvent { - /** - * @return Whether this event is cancelled. - */ - boolean isCancelled(); - - /** - * @return The format. - */ - String getFormat(); - - /** - * Sets the format. - * @param s The new format. - */ - void setFormat(String s); - - /** - * @return The player. - */ - Player getPlayer(); -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVAsyncPlayerChatListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVAsyncPlayerChatListener.java deleted file mode 100644 index c62102538..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVAsyncPlayerChatListener.java +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import java.util.logging.Level; - -import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.AsyncPlayerChatEvent; - -import com.onarandombox.MultiverseCore.MultiverseCore; - -/** - * Multiverse's {@link org.bukkit.event.Listener} for players. - */ -public class MVAsyncPlayerChatListener extends MVChatListener { - public MVAsyncPlayerChatListener(MultiverseCore plugin, MVPlayerListener playerListener) { - super(plugin, playerListener); - Logging.fine("Created AsyncPlayerChatEvent listener."); - } - - /** - * This method is called when a player wants to chat. - * @param event The Event that was fired. - */ - @EventHandler - public void playerChat(AsyncPlayerChatEvent event) { - this.playerChat(new AsyncChatEvent(event)); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVChatListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVChatListener.java deleted file mode 100644 index aaa05ab54..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVChatListener.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.onarandombox.MultiverseCore.listeners; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; - -import org.bukkit.ChatColor; -import org.bukkit.event.Listener; - -/** - * Multiverse's {@link org.bukkit.event.Listener} for players. - */ -public abstract class MVChatListener implements Listener { - private final MultiverseCore plugin; - private final MVWorldManager worldManager; - private final MVPlayerListener playerListener; - - public MVChatListener(MultiverseCore plugin, MVPlayerListener playerListener) { - this.plugin = plugin; - this.worldManager = plugin.getMVWorldManager(); - this.playerListener = playerListener; - } - - /** - * This handles a {@link ChatEvent}. - * @param event The {@link ChatEvent}. - */ - public void playerChat(ChatEvent event) { - if (event.isCancelled()) { - return; - } - // Check whether the Server is set to prefix the chat with the World name. - // If not we do nothing, if so we need to check if the World has an Alias. - if (plugin.getMVConfig().getPrefixChat()) { - String world = playerListener.getPlayerWorld().get(event.getPlayer().getName()); - if (world == null) { - world = event.getPlayer().getWorld().getName(); - playerListener.getPlayerWorld().put(event.getPlayer().getName(), world); - } - String prefix = ""; - // If we're not a MV world, don't do anything - if (!this.worldManager.isMVWorld(world)) { - return; - } - MultiverseWorld mvworld = this.worldManager.getMVWorld(world); - if (mvworld.isHidden()) { - return; - } - prefix = mvworld.getColoredWorldString(); - String chat = event.getFormat(); - - String prefixChatFormat = plugin.getMVConfig().getPrefixChatFormat(); - prefixChatFormat = prefixChatFormat.replace("%world%", prefix).replace("%chat%", chat); - prefixChatFormat = ChatColor.translateAlternateColorCodes('&', prefixChatFormat); - - event.setFormat(prefixChatFormat); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVEntityListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVEntityListener.java deleted file mode 100644 index fd81e6948..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVEntityListener.java +++ /dev/null @@ -1,125 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -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; -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; - -import java.util.logging.Level; - -/** - * Multiverse's Entity {@link Listener}. - */ -public class MVEntityListener implements Listener { - private MultiverseCore plugin; - private MVWorldManager worldManager; - - public MVEntityListener(MultiverseCore plugin) { - this.plugin = plugin; - this.worldManager = plugin.getMVWorldManager(); - } - - /** - * This method is called when an entity's food level goes higher or lower. - * @param event The Event that was fired. - */ - @EventHandler - public void foodLevelChange(FoodLevelChangeEvent event) { - if (event.isCancelled()) { - return; - } - if (event.getEntity() instanceof Player) { - Player p = (Player) event.getEntity(); - MultiverseWorld w = this.plugin.getMVWorldManager().getMVWorld(p.getWorld().getName()); - if (w != null && !w.getHunger()) { - // If the world has hunger set to false, do not let the level go down - if (event.getFoodLevel() < ((Player) event.getEntity()).getFoodLevel()) { - event.setCancelled(true); - } - } - } - } - - /** - * This method is called when an entity's health goes up or down. - * @param event The Event that was fired. - */ - @EventHandler - public void entityRegainHealth(EntityRegainHealthEvent event) { - if (event.isCancelled()) { - return; - } - RegainReason reason = event.getRegainReason(); - MultiverseWorld world = this.worldManager.getMVWorld(event.getEntity().getLocation().getWorld()); - if (world != null && reason == RegainReason.REGEN && !world.getAutoHeal()) { - event.setCancelled(true); - } - } - - /** - * Handle Animal/Monster Spawn settings, seems like a more concrete method than using CraftBukkit. - * @param event The event. - */ - @EventHandler - public void creatureSpawn(CreatureSpawnEvent event) { - // Check to see if the Creature is spawned by a plugin, we don't want to prevent this behaviour. - // TODO: Allow the egg thing to be a config param. Doubt this will be per world; seems silly. - if (event.getSpawnReason() == SpawnReason.CUSTOM || event.getSpawnReason() == SpawnReason.SPAWNER_EGG - || event.getSpawnReason() == SpawnReason.BREEDING) { - return; - } - - World world = event.getEntity().getWorld(); - if (event.isCancelled()) - return; - - // Check if it's a world which we are meant to be managing. - if (!(this.worldManager.isMVWorld(world.getName()))) - return; - - EntityType type = event.getEntityType(); - /** - * Handle people with non-standard animals: ie a patched craftbukkit. - */ - if (type == null || type.getName() == null) { - Logging.finer("Found a null typed creature."); - return; - } - - MultiverseWorld mvworld = this.worldManager.getMVWorld(world.getName()); - 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()); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVMapListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVMapListener.java deleted file mode 100644 index 2443337a3..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVMapListener.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.onarandombox.MultiverseCore.listeners; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.server.MapInitializeEvent; -import org.bukkit.map.MapView; - -/** - * A listener for bukkit map events. - */ -public class MVMapListener implements Listener { - - private final MultiverseCore plugin; - - public MVMapListener(final MultiverseCore plugin) { - this.plugin = plugin; - } - - /** - * This method is called when a map is initialized. - * @param event The event that was fired. - */ - @EventHandler - public void mapInitialize(final MapInitializeEvent event) { - for (final Player player : Bukkit.getOnlinePlayers()) { - if ((player.getItemInHand().getType() == Material.MAP - || player.getItemInHand().getType() == Material.FILLED_MAP) - && player.getItemInHand().getDurability() == event.getMap().getId()) { - final Location playerLoc = player.getLocation(); - final MapView map = event.getMap(); - map.setCenterX(playerLoc.getBlockX()); - map.setCenterZ(playerLoc.getBlockZ()); - map.setWorld(playerLoc.getWorld()); - return; - } - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerChatListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerChatListener.java deleted file mode 100644 index fff535dac..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerChatListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import java.util.logging.Level; - -import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerChatEvent; - -import com.onarandombox.MultiverseCore.MultiverseCore; - -/** - * Multiverse's {@link org.bukkit.event.Listener} for players. - */ -@SuppressWarnings("deprecation") // this exists only for downwards compatibility -public class MVPlayerChatListener extends MVChatListener { - public MVPlayerChatListener(MultiverseCore plugin, MVPlayerListener playerListener) { - super(plugin, playerListener); - Logging.fine("Registered PlayerChatEvent listener."); - } - - /** - * This method is called when a player wants to chat. - * @param event The Event that was fired. - */ - @EventHandler - public void playerChat(PlayerChatEvent event) { - this.playerChat(new NormalChatEvent(event)); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerListener.java deleted file mode 100644 index 0dbe76a32..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPlayerListener.java +++ /dev/null @@ -1,389 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -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.enums.RespawnType; -import com.onarandombox.MultiverseCore.event.MVRespawnEvent; -import com.onarandombox.MultiverseCore.utils.CompatibilityLayer; -import com.onarandombox.MultiverseCore.utils.PermissionTools; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerPortalEvent; -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. - */ -public class MVPlayerListener implements Listener { - private final MultiverseCore plugin; - private final MVWorldManager worldManager; - private final PermissionTools pt; - - private final Map playerWorld = new ConcurrentHashMap(); - - public MVPlayerListener(MultiverseCore plugin) { - this.plugin = plugin; - worldManager = plugin.getMVWorldManager(); - pt = new PermissionTools(plugin); - } - - /** - * @return the playerWorld-map - */ - public Map getPlayerWorld() { - return playerWorld; - } - - /** - * This method is called when a player respawns. - * @param event The Event that was fired. - */ - @EventHandler(priority = EventPriority.LOW) - public void playerRespawn(PlayerRespawnEvent event) { - World world = event.getPlayer().getWorld(); - MultiverseWorld mvWorld = this.worldManager.getMVWorld(world.getName()); - // If it's not a World MV manages we stop. - if (mvWorld == null) { - return; - } - - RespawnType respawnType = RespawnType.OTHER; - if (event.isBedSpawn()) { - respawnType = RespawnType.BED; - } - if (CompatibilityLayer.isAnchorSpawn(event)) { - respawnType = RespawnType.ANCHOR; - } - - if (mvWorld.getBedRespawn() && (respawnType == RespawnType.BED || respawnType == RespawnType.ANCHOR)) { - Logging.fine("Spawning %s at their %s", event.getPlayer().getName(), respawnType); - return; - } - - // Get the instance of the World the player should respawn at. - MultiverseWorld respawnWorld = null; - if (this.worldManager.isMVWorld(mvWorld.getRespawnToWorld())) { - respawnWorld = this.worldManager.getMVWorld(mvWorld.getRespawnToWorld()); - } - - // If it's null then it either means the World doesn't exist or the value is blank, so we don't handle it. - // NOW: We'll always handle it to get more accurate spawns - if (respawnWorld != null) { - world = respawnWorld.getCBWorld(); - } - // World has been set to the appropriate world - Location respawnLocation = getMostAccurateRespawnLocation(world); - - MVRespawnEvent respawnEvent = new MVRespawnEvent(respawnLocation, event.getPlayer(), "compatability"); - this.plugin.getServer().getPluginManager().callEvent(respawnEvent); - event.setRespawnLocation(respawnEvent.getPlayersRespawnLocation()); - } - - private Location getMostAccurateRespawnLocation(World w) { - MultiverseWorld mvw = this.worldManager.getMVWorld(w.getName()); - if (mvw != null) { - return mvw.getSpawnLocation(); - } - return w.getSpawnLocation(); - } - - /** - * This method is called when a player joins the server. - * @param event The Event that was fired. - */ - @EventHandler - public void playerJoin(PlayerJoinEvent event) { - Player p = event.getPlayer(); - if (!p.hasPlayedBefore()) { - Logging.finer("Player joined for the FIRST time!"); - if (plugin.getMVConfig().getFirstSpawnOverride()) { - Logging.fine("Moving NEW player to(firstspawnoverride): " - + worldManager.getFirstSpawnWorld().getSpawnLocation()); - this.sendPlayerToDefaultWorld(p); - } - return; - } else { - Logging.finer("Player joined AGAIN!"); - if (this.plugin.getMVConfig().getEnforceAccess() // check this only if we're enforcing access! - && !this.plugin.getMVPerms().hasPermission(p, "multiverse.access." + p.getWorld().getName(), false)) { - p.sendMessage("[MV] - Sorry you can't be in this world anymore!"); - this.sendPlayerToDefaultWorld(p); - } - } - // Handle the Players GameMode setting for the new world. - this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld()); - playerWorld.put(p.getName(), p.getWorld().getName()); - } - - /** - * This method is called when a player changes worlds. - * @param event The Event that was fired. - */ - @EventHandler(priority = EventPriority.MONITOR) - public void playerChangedWorld(PlayerChangedWorldEvent event) { - // Permissions now determine whether or not to handle a gamemode. - this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld()); - playerWorld.put(event.getPlayer().getName(), event.getPlayer().getWorld().getName()); - } - - /** - * This method is called when a player quits the game. - * @param event The Event that was fired. - */ - @EventHandler - public void playerQuit(PlayerQuitEvent event) { - this.plugin.removePlayerSession(event.getPlayer()); - } - - /** - * This method is called when a player teleports anywhere. - * @param event The Event that was fired. - */ - @EventHandler(priority = EventPriority.HIGHEST) - public void playerTeleport(PlayerTeleportEvent event) { - Logging.finer("Got teleport event for player '" - + event.getPlayer().getName() + "' with cause '" + event.getCause() + "'"); - if (event.isCancelled()) { - return; - } - if (event.getTo() == null) { - Logging.fine("Player '" + event.getPlayer().getName() + "' is teleporting to a null location!"); - return; - } - Player teleportee = event.getPlayer(); - CommandSender teleporter = teleportee; - String teleporterName = MultiverseCore.getPlayerTeleporter(teleportee.getName()); - if (teleporterName != null) { - if (teleporterName.equals("CONSOLE")) { - Logging.finer("We know the teleporter is the console! Magical!"); - teleporter = this.plugin.getServer().getConsoleSender(); - } else { - teleporter = this.plugin.getServer().getPlayerExact(teleporterName); - } - } - Logging.finer("Inferred sender '" + teleporter + "' from name '" - + teleporterName + "', fetched from name '" + teleportee.getName() + "'"); - MultiverseWorld fromWorld = this.worldManager.getMVWorld(event.getFrom().getWorld().getName()); - MultiverseWorld toWorld = this.worldManager.getMVWorld(event.getTo().getWorld().getName()); - if (toWorld == null) { - Logging.fine("Player '" + teleportee.getName() + "' is teleporting to world '" - + event.getTo().getWorld().getName() + "' which is not managed by Multiverse-Core. No further " - + "actions will be taken by Multiverse-Core."); - return; - } - if (event.getFrom().getWorld().equals(event.getTo().getWorld())) { - // The player is Teleporting to the same world. - Logging.finer("Player '" + teleportee.getName() + "' is teleporting to the same world."); - this.stateSuccess(teleportee.getName(), toWorld.getAlias()); - return; - } - // TODO: Refactor these lines. - // Charge the teleporter - event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, teleporter, teleportee, true)); - if (event.isCancelled() && teleporter != null) { - Logging.fine("Player '" + teleportee.getName() - + "' was DENIED ACCESS to '" + toWorld.getAlias() - + "' because '" + teleporter.getName() - + "' don't have the FUNDS required to enter it."); - return; - } - - // Check if player is allowed to enter the world if we're enforcing permissions - if (plugin.getMVConfig().getEnforceAccess()) { - event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, teleporter, teleportee)); - if (event.isCancelled() && teleporter != null) { - Logging.fine("Player '" + teleportee.getName() - + "' was DENIED ACCESS to '" + toWorld.getAlias() - + "' because '" + teleporter.getName() - + "' don't have: multiverse.access." + event.getTo().getWorld().getName()); - return; - } - } else { - Logging.fine("Player '" + teleportee.getName() - + "' was allowed to go to '" + toWorld.getAlias() + "' because enforceaccess is off."); - } - - // Does a limit actually exist? - if (toWorld.getPlayerLimit() > -1) { - // Are there equal or more people on the world than the limit? - if (toWorld.getCBWorld().getPlayers().size() >= toWorld.getPlayerLimit()) { - // Ouch the world is full, lets see if the player can bypass that limitation - if (!pt.playerCanBypassPlayerLimit(toWorld, teleporter, teleportee)) { - Logging.fine("Player '" + teleportee.getName() - + "' was DENIED ACCESS to '" + toWorld.getAlias() - + "' because the world is full and '" + teleporter.getName() - + "' doesn't have: mv.bypass.playerlimit." + event.getTo().getWorld().getName()); - event.setCancelled(true); - return; - } - } - } - - // By this point anything cancelling the event has returned on the method, meaning the teleport is a success \o/ - this.stateSuccess(teleportee.getName(), toWorld.getAlias()); - } - - private void stateSuccess(String playerName, String worldName) { - Logging.fine("MV-Core is allowing Player '" + playerName - + "' to go to '" + worldName + "'."); - } - - /** - * This method is called to adjust the portal location to the actual portal location (and not - * right outside of it. - * @param event The Event that was fired. - */ - @EventHandler(priority = EventPriority.LOWEST) - public void playerPortalCheck(PlayerPortalEvent event) { - if (event.isCancelled() || event.getFrom() == null) { - return; - } - - // REMEMBER! getTo MAY be NULL HERE!!! - // If the player was actually outside of the portal, adjust the from location - if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() != Material.NETHER_PORTAL) { - Location newloc = this.plugin.getSafeTTeleporter().findPortalBlockNextTo(event.getFrom()); - // TODO: Fix this. Currently, we only check for PORTAL blocks. I'll have to figure out what - // TODO: we want to do here. - if (newloc != null) { - event.setFrom(newloc); - } - } - // Wait for the adjust, then return! - if (event.getTo() == null) { - return; - } - } - /** - * This method is called when a player actually portals via a vanilla style portal. - * @param event The Event that was fired. - */ - @EventHandler(priority = EventPriority.HIGH) - public void playerPortal(PlayerPortalEvent event) { - if (event.isCancelled() || (event.getFrom() == null)) { - return; - } - // The adjust should have happened much earlier. - if (event.getTo() == null) { - Logging.fine("Player '" + event.getPlayer().getName() + "' is portaling to NULL location."); - return; - } - MultiverseWorld fromWorld = this.worldManager.getMVWorld(event.getFrom().getWorld().getName()); - MultiverseWorld toWorld = this.worldManager.getMVWorld(event.getTo().getWorld().getName()); - if (event.getFrom().getWorld().equals(event.getTo().getWorld())) { - // The player is Portaling to the same world. - Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world."); - return; - } - event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, event.getPlayer(), event.getPlayer(), true)); - if (event.isCancelled()) { - Logging.fine("Player '" + event.getPlayer().getName() - + "' was DENIED ACCESS to '" + event.getTo().getWorld().getName() - + "' because they don't have the FUNDS required to enter."); - return; - } - if (plugin.getMVConfig().getEnforceAccess()) { - event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, event.getPlayer(), event.getPlayer())); - if (event.isCancelled()) { - Logging.fine("Player '" + event.getPlayer().getName() - + "' was DENIED ACCESS to '" + event.getTo().getWorld().getName() - + "' because they don't have: multiverse.access." + event.getTo().getWorld().getName()); - } - } else { - Logging.fine("Player '" + event.getPlayer().getName() - + "' was allowed to go to '" + event.getTo().getWorld().getName() - + "' because enforceaccess is off."); - } - if (!this.plugin.getMVConfig().isUsingDefaultPortalSearch()) { - CompatibilityLayer.setPortalSearchRadius(event, this.plugin.getMVConfig().getPortalSearchRadius()); - } - } - - private void sendPlayerToDefaultWorld(final Player player) { - // Remove the player 1 tick after the login. I'm sure there's GOT to be a better way to do this... - this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, - new Runnable() { - @Override - public void run() { - player.teleport(plugin.getMVWorldManager().getFirstSpawnWorld().getSpawnLocation()); - } - }, 1L); - } - - // FOLLOWING 2 Methods and Private class handle Per Player GameModes. - private void handleGameModeAndFlight(Player player, World world) { - - MultiverseWorld mvWorld = this.worldManager.getMVWorld(world.getName()); - if (mvWorld != null) { - this.handleGameModeAndFlight(player, mvWorld); - } else { - Logging.finer("Not handling gamemode and flight for world '" + world.getName() - + "' not managed by Multiverse."); - } - } - - /** - * Handles the gamemode for the specified {@link Player}. - * @param player The {@link Player}. - * @param world The world the player is in. - */ - public void handleGameModeAndFlight(final Player player, final MultiverseWorld world) { - // We perform this task one tick later to MAKE SURE that the player actually reaches the - // destination world, otherwise we'd be changing the player mode if they havent moved anywhere. - this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, - new Runnable() { - @Override - public void run() { - if (!MVPlayerListener.this.pt.playerCanIgnoreGameModeRestriction(world, player)) { - // Check that the player is in the new world and they haven't been teleported elsewhere or the event cancelled. - if (player.getWorld() == world.getCBWorld()) { - Logging.fine("Handling gamemode for player: %s, Changing to %s", player.getName(), world.getGameMode().toString()); - Logging.finest("From World: %s", player.getWorld()); - Logging.finest("To World: %s", world); - player.setGameMode(world.getGameMode()); - // Check if their flight mode should change - // TODO need a override permission for this - if (player.getAllowFlight() && !world.getAllowFlight() && player.getGameMode() != GameMode.CREATIVE) { - player.setAllowFlight(false); - if (player.isFlying()) { - player.setFlying(false); - } - } else if (world.getAllowFlight()) { - if (player.getGameMode() == GameMode.CREATIVE) { - player.setAllowFlight(true); - } - } - } else { - Logging.fine("The gamemode/allowfly was NOT changed for player '%s' because he is now in world '%s' instead of world '%s'", - player.getName(), player.getWorld().getName(), world.getName()); - } - } else { - Logging.fine("Player: " + player.getName() + " is IMMUNE to gamemode changes!"); - } - } - }, 1L); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPortalListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPortalListener.java deleted file mode 100644 index c348ac3da..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVPortalListener.java +++ /dev/null @@ -1,87 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2012. * - * 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.listeners; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.Material; -import org.bukkit.PortalType; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.entity.EntityCreatePortalEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.world.PortalCreateEvent; - -import java.util.logging.Level; - -/** - * A custom listener for portal related events. - */ -public class MVPortalListener implements Listener { - - private MultiverseCore plugin; - - public MVPortalListener(MultiverseCore core) { - this.plugin = core; - } - - /** - * This is called when an entity creates a portal. - * - * @param event The event where an entity created a portal. - */ - @EventHandler - public void entityPortalCreate(EntityCreatePortalEvent event) { - if (event.isCancelled() || event.getBlocks().size() == 0) { - return; - } - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(event.getEntity().getWorld()); - // We have to do it like this due to a bug in 1.1-R3 - if (world != null && !world.getAllowedPortals().isPortalAllowed(event.getPortalType())) { - event.setCancelled(true); - } - } - - /** - * This is called when a portal is created as the result of another world being linked. - * @param event The event where a portal was formed due to a world link - */ - @EventHandler(ignoreCancelled = true) - public void portalForm(PortalCreateEvent event) { - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(event.getWorld()); - if (world != null && !world.getAllowedPortals().isPortalAllowed(PortalType.NETHER)) { - Logging.fine("Cancelling creation of nether portal because portalForm disallows."); - event.setCancelled(true); - } - } - - /** - * This method will prevent ender portals from being created in worlds where they are not allowed due to portalForm. - * - * @param event The player interact event. - */ - @EventHandler(ignoreCancelled = true) - public void portalForm(PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { - return; - } - if (event.getClickedBlock().getType() != Material.END_PORTAL_FRAME) { - return; - } - if (event.getItem() == null || event.getItem().getType() != Material.ENDER_EYE) { - return; - } - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(event.getPlayer().getWorld()); - if (world != null && !world.getAllowedPortals().isPortalAllowed(PortalType.ENDER)) { - Logging.fine("Cancelling creation of ender portal because portalForm disallows."); - event.setCancelled(true); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWeatherListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWeatherListener.java deleted file mode 100644 index 358803e18..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWeatherListener.java +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.weather.ThunderChangeEvent; -import org.bukkit.event.weather.WeatherChangeEvent; - -/** - * Multiverse's Weather {@link Listener}. - */ -public class MVWeatherListener implements Listener { - private MultiverseCore plugin; - - public MVWeatherListener(MultiverseCore plugin) { - this.plugin = plugin; - } - - /** - * This method is called when the weather changes. - * @param event The Event that was fired. - */ - @EventHandler - public void weatherChange(WeatherChangeEvent event) { - if (event.isCancelled()) { - return; - } - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(event.getWorld().getName()); - if (world != null) { - // If it's going to start raining and we have weather disabled - event.setCancelled((event.toWeatherState() && !world.isWeatherEnabled())); - } - } - - /** - * This method is called when a big storm is going to start. - * @param event The Event that was fired. - */ - @EventHandler - public void thunderChange(ThunderChangeEvent event) { - if (event.isCancelled()) { - return; - } - MultiverseWorld world = this.plugin.getMVWorldManager().getMVWorld(event.getWorld().getName()); - if (world != null) { - // If it's going to start raining and we have weather disabled - event.setCancelled((event.toThunderState() && !world.isWeatherEnabled())); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldInitListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldInitListener.java deleted file mode 100644 index cc12a10e8..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldInitListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldInitEvent; - -public class MVWorldInitListener implements Listener { - - MultiverseCore plugin; - - public MVWorldInitListener(MultiverseCore plugin) { - this.plugin = plugin; - } - - @EventHandler - public void initWorld(WorldInitEvent event) { - if (!plugin.getMVWorldManager().isKeepingSpawnInMemory(event.getWorld())) { - event.getWorld().setKeepSpawnInMemory(false); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java deleted file mode 100644 index a63d32bf0..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MVWorldListener.java +++ /dev/null @@ -1,67 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.World; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; - -/** - * Multiverse's World {@link Listener}. - */ -public class MVWorldListener implements Listener { - private MultiverseCore plugin; - private MVWorldManager worldManager; - - public MVWorldListener(MultiverseCore plugin) { - this.plugin = plugin; - this.worldManager = plugin.getMVWorldManager(); - } - - /** - * This method is called when Bukkit fires off a WorldUnloadEvent. - * @param event The Event that was fired. - */ - @EventHandler - public void unloadWorld(WorldUnloadEvent event) { - if (event.isCancelled()) { - return; - } - if (event.getWorld() instanceof World) { - World world = (World) event.getWorld(); - if (world != null) { - this.plugin.getMVWorldManager().unloadWorld(world.getName(), false); - } - } - } - - /** - * This method is called when Bukkit fires off a WorldLoadEvent. - * @param event The Event that was fired. - */ - @EventHandler - public void loadWorld(WorldLoadEvent event) { - World world = event.getWorld(); - if (world != null) { - if (this.plugin.getMVWorldManager().getUnloadedWorlds().contains(world.getName())) { - this.plugin.getMVWorldManager().loadWorld(world.getName()); - } - MultiverseWorld mvWorld = plugin.getMVWorldManager().getMVWorld(world); - if (mvWorld != null) { - // This is where we can temporarily fix those pesky property issues! - world.setPVP(mvWorld.isPVPEnabled()); - world.setDifficulty(mvWorld.getDifficulty()); - } - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/MultiverseCoreListener.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/MultiverseCoreListener.java deleted file mode 100644 index fa1adf676..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/MultiverseCoreListener.java +++ /dev/null @@ -1,79 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.listeners; - -import com.onarandombox.MultiverseCore.event.MVConfigReloadEvent; -import com.onarandombox.MultiverseCore.event.MVPlayerTouchedPortalEvent; -import com.onarandombox.MultiverseCore.event.MVRespawnEvent; -import com.onarandombox.MultiverseCore.event.MVTeleportEvent; -import com.onarandombox.MultiverseCore.event.MVVersionEvent; -import com.onarandombox.MultiverseCore.event.MVWorldDeleteEvent; -import com.onarandombox.MultiverseCore.event.MVWorldPropertyChangeEvent; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; - -/** - * Subclasses of this listener can be used to conveniently listen to MultiverseCore-events. - */ -public abstract class MultiverseCoreListener implements Listener { - /** - * Called when a {@link MVWorldPropertyChangeEvent} is fired. - * @param event The event. - */ - @EventHandler - public void worldPropertyChange(MVWorldPropertyChangeEvent event) { - } - - /** - * Called when a {@link MVWorldDeleteEvent} is fired. - * @param event The event. - */ - @EventHandler - public void worldDelete(MVWorldDeleteEvent event) { - } - - /** - * Called when a {@link MVVersionEvent} is fired. - * @param event The event. - */ - @EventHandler - public void versionRequest(MVVersionEvent event) { - } - - /** - * Called when a {@link MVTeleportEvent} is fired. - * @param event The event. - */ - @EventHandler - public void playerTeleport(MVTeleportEvent event) { - } - - /** - * Called when a {@link MVRespawnEvent} is fired. - * @param event The event. - */ - @EventHandler - public void playerRespawn(MVRespawnEvent event) { - } - - /** - * Called when a {@link MVPlayerTouchedPortalEvent} is fired. - * @param event The event. - */ - @EventHandler - public void playerTouchedPortal(MVPlayerTouchedPortalEvent event) { - } - - /** - * Called when a {@link MVConfigReloadEvent} is fired. - * @param event The event. - */ - @EventHandler - public void configReload(MVConfigReloadEvent event) { - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/NormalChatEvent.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/NormalChatEvent.java deleted file mode 100644 index a712c627e..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/NormalChatEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.onarandombox.MultiverseCore.listeners; - -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerChatEvent; - -/** - * A wrapper for the {@link PlayerChatEvent}. - * @deprecated This is deprecated like the {@link PlayerChatEvent}. - */ -@Deprecated -public class NormalChatEvent implements ChatEvent { - private final PlayerChatEvent event; - - public NormalChatEvent(PlayerChatEvent event) { - this.event = event; - } - - @Override - public boolean isCancelled() { - return event.isCancelled(); - } - - @Override - public String getFormat() { - return event.getFormat(); - } - - @Override - public void setFormat(String s) { - event.setFormat(s); - } - - @Override - public Player getPlayer() { - return event.getPlayer(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/listeners/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/listeners/package-info.java deleted file mode 100644 index c09a53a4d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/listeners/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains all listeners. - */ -package com.onarandombox.MultiverseCore.listeners; diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/AnchorManager.java b/src/main/java/com/onarandombox/MultiverseCore/utils/AnchorManager.java deleted file mode 100644 index 4cac8a1bf..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/AnchorManager.java +++ /dev/null @@ -1,172 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import org.bukkit.Location; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; - -/** - * Manages anchors. - */ -public class AnchorManager { - private MultiverseCore plugin; - private Map anchors; - private FileConfiguration anchorConfig; - - public AnchorManager(MultiverseCore plugin) { - this.plugin = plugin; - this.anchors = new HashMap(); - } - - /** - * Loads all anchors. - */ - public void loadAnchors() { - this.anchors = new HashMap(); - this.anchorConfig = YamlConfiguration.loadConfiguration(new File(this.plugin.getDataFolder(), "anchors.yml")); - this.ensureConfigIsPrepared(); - ConfigurationSection anchorsSection = this.anchorConfig.getConfigurationSection("anchors"); - Set anchorKeys = anchorsSection.getKeys(false); - for (String key : anchorKeys) { - //world:x,y,z:pitch:yaw - Location anchorLocation = plugin.getLocationManipulation().stringToLocation(anchorsSection.getString(key, "")); - if (anchorLocation != null) { - Logging.config("Loading anchor: '%s'...", key); - this.anchors.put(key, anchorLocation); - } else { - Logging.warning("The location for anchor '%s' is INVALID.", key); - } - - } - } - - private void ensureConfigIsPrepared() { - if (this.anchorConfig.getConfigurationSection("anchors") == null) { - this.anchorConfig.createSection("anchors"); - } - } - - /** - * Saves all anchors. - * @return True if all anchors were successfully saved. - */ - public boolean saveAnchors() { - try { - this.anchorConfig.save(new File(this.plugin.getDataFolder(), "anchors.yml")); - return true; - } catch (IOException e) { - Logging.severe("Failed to save anchors.yml. Please check your file permissions."); - return false; - } - } - - /** - * Gets the {@link Location} associated with an anchor. - * @param anchor The name of the anchor. - * @return The {@link Location}. - */ - public Location getAnchorLocation(String anchor) { - if (this.anchors.containsKey(anchor)) { - return this.anchors.get(anchor); - } - return null; - } - - /** - * Saves an anchor. - * @param anchor The name of the anchor. - * @param location The location of the anchor as string. - * @return True if the anchor was successfully saved. - */ - public boolean saveAnchorLocation(String anchor, String location) { - Location parsed = plugin.getLocationManipulation().stringToLocation(location); - return parsed != null && this.saveAnchorLocation(anchor, parsed); - } - - /** - * Saves an anchor. - * @param anchor The name of the anchor. - * @param l The {@link Location} of the anchor. - * @return True if the anchor was successfully saved. - */ - public boolean saveAnchorLocation(String anchor, Location l) { - if (l == null) { - return false; - } - this.anchorConfig.set("anchors." + anchor, plugin.getLocationManipulation().locationToString(l)); - this.anchors.put(anchor, l); - return this.saveAnchors(); - } - - /** - * Gets all anchors. - * @return An unmodifiable {@link Set} containing all anchors. - */ - public Set getAllAnchors() { - return Collections.unmodifiableSet(this.anchors.keySet()); - } - - /** - * Gets all anchors that the specified {@link Player} can access. - * @param p The {@link Player}. - * @return An unmodifiable {@link Set} containing all anchors the specified {@link Player} can access. - */ - public Set getAnchors(Player p) { - if (p == null) { - return this.anchors.keySet(); - } - Set myAnchors = new HashSet(); - for (String anchor : this.anchors.keySet()) { - Location ancLoc = this.anchors.get(anchor); - if (ancLoc == null) { - continue; - } - String worldPerm = "multiverse.access." + ancLoc.getWorld().getName(); - // Add to the list if we're not enforcing access - // OR - // We are enforcing access and the user has the permission. - if (!this.plugin.getMVConfig().getEnforceAccess() || - (this.plugin.getMVConfig().getEnforceAccess() && p.hasPermission(worldPerm))) { - myAnchors.add(anchor); - } else { - Logging.finer(String.format("Not adding anchor %s to the list, user %s doesn't have the %s " + - "permission and 'enforceaccess' is enabled!", - anchor, p.getName(), worldPerm)); - } - } - return Collections.unmodifiableSet(myAnchors); - } - - /** - * Deletes the specified anchor. - * @param s The name of the anchor. - * @return True if the anchor was successfully deleted. - */ - public boolean deleteAnchor(String s) { - if (this.anchors.containsKey(s)) { - this.anchors.remove(s); - this.anchorConfig.set("anchors." + s, null); - return this.saveAnchors(); - } - return false; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/BukkitTravelAgent.java b/src/main/java/com/onarandombox/MultiverseCore/utils/BukkitTravelAgent.java deleted file mode 100644 index e2f658fa5..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/BukkitTravelAgent.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.onarandombox.MultiverseCore.utils; - -import java.util.logging.Level; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; -import com.onarandombox.MultiverseCore.destination.CannonDestination; -import org.bukkit.Location; -import org.bukkit.TravelAgent; -import org.bukkit.event.player.PlayerPortalEvent; - -public class BukkitTravelAgent implements TravelAgent { - private final MVTravelAgent agent; - - public BukkitTravelAgent(MVTravelAgent agent) { - this.agent = agent; - } - - /** - * {@inheritDoc} - */ - @Override - public BukkitTravelAgent setSearchRadius(int radius) { - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public int getSearchRadius() { - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public BukkitTravelAgent setCreationRadius(int radius) { - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public int getCreationRadius() { - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean getCanCreatePortal() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void setCanCreatePortal(boolean create) { - } - - /** - * {@inheritDoc} - */ - @Override - public Location findOrCreate(Location location) { - return this.getSafeLocation(); - } - - /** - * {@inheritDoc} - */ - @Override - public Location findPortal(Location location) { - return this.getSafeLocation(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean createPortal(Location location) { - return false; - } - - private Location getSafeLocation() { - // At this time, these can never use the velocity. - if (agent.destination instanceof CannonDestination) { - Logging.fine("Using Stock TP method. This cannon will have 0 velocity"); - } - SafeTTeleporter teleporter = agent.core.getSafeTTeleporter(); - Location newLoc = agent.destination.getLocation(agent.player); - if (agent.destination.useSafeTeleporter()) { - newLoc = teleporter.getSafeLocation(agent.player, agent.destination); - } - if (newLoc == null) { - return agent.player.getLocation(); - } - return newLoc; - - } - - public void setPortalEventTravelAgent(PlayerPortalEvent event) { - event.setPortalTravelAgent(this); - event.useTravelAgent(true); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/CompatibilityLayer.java b/src/main/java/com/onarandombox/MultiverseCore/utils/CompatibilityLayer.java deleted file mode 100644 index 2a83a7e20..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/CompatibilityLayer.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.onarandombox.MultiverseCore.utils; - -import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.event.entity.EntityPortalEvent; -import org.bukkit.event.player.PlayerPortalEvent; -import org.bukkit.event.player.PlayerRespawnEvent; - -import java.lang.reflect.Method; - -/** - * 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 class, methods and fields. - */ - public static void init() { - 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

- * - * @param event A player respawn event. - * @return If the respawn location is an anchor point. - */ - public static boolean isAnchorSpawn(PlayerRespawnEvent event) { - if (checkAnchorSpawn == null) { - return false; - } - Boolean result = ReflectHelper.invokeMethod(event, checkAnchorSpawn); - if (result == null) { - Logging.warning("Unable to check if spawning at respawn anchor!"); - return false; - } - return result; - } - - /** - *

Gets if Travel Agent is supported on the server's minecraft version.

- *

Removed in minecraft 1.14

- * - * @return True if Travel Agent is supported, else false. - */ - public static boolean isUseTravelAgent() { - return useTravelAgent; - } - - /** - *

Sets search radius for a PlayerPortalEvent.

- * - *

Use travel agent if available, else using new PlayerPortalEvent.setSearchRadius(int) method - * introduced in minecraft 1.15

- * - * @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."); - } - - /** - *

Sets search radius for a EntityPortalEvent.

- * - *

Use travel agent if available, else using new EntityPortalEvent.setSearchRadius(int) method - * introduced in minecraft 1.15

- * - * @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."); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/DebugLog.java b/src/main/java/com/onarandombox/MultiverseCore/utils/DebugLog.java deleted file mode 100644 index c83e7e089..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/DebugLog.java +++ /dev/null @@ -1,129 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.onarandombox.MultiverseCore.MultiverseCoreConfiguration; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.List; -import java.util.logging.FileHandler; -import java.util.logging.Formatter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -/** - * The Multiverse debug-logger. - * @deprecated Deprecated in favor of new Logging lib. See {@link com.dumptruckman.minecraft.util.Logging}. - */ -@Deprecated -public class DebugLog extends Logger { - private FileHandler fh; - private Logger standardLog = null; - private String prefix = "[MVCore-Debug] "; - - /** - * Creates a new debug logger. - * - * @param logger The name of the logger. - * @param file The file to log to. - */ - public DebugLog(String logger, String file) { - super(logger, null); - try { - this.fh = new FileHandler(file, true); - this.setUseParentHandlers(false); - List toRemove = Arrays.asList(this.getHandlers()); - for (Handler handler : toRemove) { - this.removeHandler(handler); - } - this.addHandler(this.fh); - this.setLevel(Level.ALL); - this.fh.setFormatter(new LogFormatter()); - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Sets the log-tag. - * @param tag The new tag. - */ - public void setTag(String tag) { - this.prefix = tag + " "; - } - - /** - * Specifies the logger to use to send debug messages to as the debug logger itself only sends messages to a file. - * - * @param logger Logger to send debug messages to. - */ - public void setStandardLogger(Logger logger) { - this.standardLog = logger; - } - - /** - * Log a message at a certain level. - * - * @param level The log-{@link Level}. - * @param msg the message. - */ - @Override - public void log(final Level level, final String msg) { - if (MultiverseCoreConfiguration.isSet() && MultiverseCoreConfiguration.getInstance().getGlobalDebug() > 0) { - // only redirect to standardLog if it's lower than INFO so we don't log that twice! - if ((level.intValue() < Level.INFO.intValue()) && (standardLog != null)) { - standardLog.log(Level.INFO, prefix + msg); - } - - super.log(level, prefix + msg); - } - } - - /** - * Our log-{@link Formatter}. - */ - private class LogFormatter extends Formatter { - private final SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @Override - public String format(LogRecord record) { - StringBuilder builder = new StringBuilder(); - Throwable ex = record.getThrown(); - - builder.append(this.date.format(record.getMillis())); - builder.append(" ["); - builder.append(record.getLevel().getLocalizedName().toUpperCase()); - builder.append("] "); - builder.append(record.getMessage()); - builder.append('\n'); - - if (ex != null) { - StringWriter writer = new StringWriter(); - ex.printStackTrace(new PrintWriter(writer)); - builder.append(writer); - } - - return builder.toString(); - } - } - - /** - * Closes this {@link DebugLog}. - */ - public void close() { - this.fh.close(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyColorScheme.java b/src/main/java/com/onarandombox/MultiverseCore/utils/FancyColorScheme.java deleted file mode 100644 index d92b17a18..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyColorScheme.java +++ /dev/null @@ -1,68 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import org.bukkit.ChatColor; - -/** - * A color-scheme. - */ -public class FancyColorScheme { - private ChatColor headerColor; - private ChatColor mainColor; - private ChatColor altColor; - private ChatColor defContentColor; - - public FancyColorScheme(ChatColor header, ChatColor main, ChatColor alt, ChatColor defaultColor) { - this.headerColor = header; - this.mainColor = main; - this.altColor = alt; - this.defContentColor = defaultColor; - } - - /** - * Gets the header's {@link ChatColor}. - * @return The header's {@link ChatColor}. - */ - public ChatColor getHeader() { - return this.headerColor; - } - - /** - * Gets the main {@link ChatColor}. - * @return The main {@link ChatColor}. - */ - public ChatColor getMain() { - return this.mainColor; - } - - /** - * Gets the alt {@link ChatColor}. - * @return The alt {@link ChatColor}. - */ - public ChatColor getAlt() { - return this.altColor; - } - - /** - * Gets the default {@link ChatColor}. - * @return The default {@link ChatColor}. - */ - public ChatColor getDefault() { - return this.defContentColor; - } - - /** - * Gets either the main or the alt {@link ChatColor}. - * @param main True if the main-color is desired, false to get the alt color. - * @return The desired {@link ChatColor}. - */ - public ChatColor getMain(boolean main) { - return main ? this.getMain() : this.getAlt(); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyHeader.java b/src/main/java/com/onarandombox/MultiverseCore/utils/FancyHeader.java deleted file mode 100644 index b3c2487ab..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyHeader.java +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.onarandombox.MultiverseCore.api.FancyText; - -/** - * A colored text-header. - */ -public class FancyHeader implements FancyText { - - private FancyColorScheme colors; - private StringBuilder text; - - public FancyHeader(String text, FancyColorScheme scheme) { - this.colors = scheme; - this.text = new StringBuilder(text); - } - - @Override - public String getFancyText() { - return String.format("%s--- %s%s ---", colors.getHeader(), text.toString(), colors.getHeader()); - } - - /** - * Appends text to this {@link FancyHeader}. - * @param string The text to append. - */ - public void appendText(String string) { - this.text.append(string); - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyMessage.java b/src/main/java/com/onarandombox/MultiverseCore/utils/FancyMessage.java deleted file mode 100644 index 7775e9661..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/FancyMessage.java +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.onarandombox.MultiverseCore.api.FancyText; - -/** - * A colored text-message. - */ -public class FancyMessage implements FancyText { - private String title; - private String message; - private boolean main = true; - private FancyColorScheme colors; - - /** - * Allows easy creation of an alternating colored list. - * TODO: Documentation! Why does CheckStyle just ignore this? - * - * @param title The title. - * @param message The body of the message. - * @param scheme The color scheme to use for easy styling. - */ - public FancyMessage(String title, String message, FancyColorScheme scheme) { - this.title = title; - this.message = message; - this.colors = scheme; - } - - /** - * Makes this {@link FancyMessage} use the main-color. - */ - public void setColorMain() { - this.main = true; - } - - /** - * Makes this {@link FancyMessage} use the alt-color. - */ - public void setColorAlt() { - this.main = false; - } - - @Override - public String getFancyText() { - return this.colors.getMain(this.main) + this.title + this.colors.getDefault() + message; - } - - /** - * Specifies whether this {@link FancyMessage} should use the alt-color. - * @param altColor Whether this {@link FancyMessage} should use the alt-color. - */ - public void setAltColor(boolean altColor) { - this.main = !altColor; - } - - /** - * Specifies whether this {@link FancyMessage} should use the main-color. - * @param mainColor Whether this {@link FancyMessage} should use the main-color. - */ - public void setMainColor(boolean mainColor) { - this.main = mainColor; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/FileUtils.java b/src/main/java/com/onarandombox/MultiverseCore/utils/FileUtils.java deleted file mode 100644 index 571143bb8..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/FileUtils.java +++ /dev/null @@ -1,135 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; - -import java.io.File; -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Stream; - -import com.dumptruckman.minecraft.util.Logging; - -/** - * File-utilities. - */ -public class FileUtils { - protected FileUtils() { - throw new UnsupportedOperationException(); - } - - /** - * Used to delete a folder. - * - * @param file The folder to delete. - * @return true if the folder was successfully deleted. - */ - public static boolean deleteFolder(File file) { - try (Stream files = Files.walk(file.toPath())) { - files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); - return true; - } catch (IOException e) { - Logging.warning(e.getMessage()); - return false; - } - } - - /** - * Used to delete the contents of a folder, without deleting the folder itself. - * - * @param file The folder whose contents to delete. - * @return true if the contents were successfully deleted - */ - public static boolean deleteFolderContents(File file) { - try (Stream files = Files.walk(file.toPath())){ - files.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .filter(f -> !f.equals(file)) - .forEach(File::delete); - return true; - } catch (IOException e) { - Logging.warning(e.getMessage()); - return false; - } - } - - /** - * Helper method to copy the world-folder. - * @param source Source-File - * @param target Target-File - * - * @return true if it had success - */ - public static boolean copyFolder(File source, File target) { - return copyFolder(source, target, null); - } - - /** - * Helper method to copy the world-folder. - * @param source Source-File - * @param target Target-File - * @param excludeFiles files to ignore and not copy over to Target-File - * - * @return true if it had success - */ - public static boolean copyFolder(File source, File target, List excludeFiles) { - Path sourceDir = source.toPath(); - Path targetDir = target.toPath(); - - try { - Files.walkFileTree(sourceDir, new CopyDirFileVisitor(sourceDir, targetDir, excludeFiles)); - return true; - } catch (IOException e) { - Logging.warning("Unable to copy directory", e); - return false; - } - } - - private static class CopyDirFileVisitor extends SimpleFileVisitor { - - private final Path sourceDir; - private final Path targetDir; - private final List excludeFiles; - - private CopyDirFileVisitor(Path sourceDir, Path targetDir, List excludeFiles) { - this.sourceDir = sourceDir; - this.targetDir = targetDir; - this.excludeFiles = excludeFiles; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - Path newDir = targetDir.resolve(sourceDir.relativize(dir)); - if (!Files.isDirectory(newDir)) { - Files.createDirectory(newDir); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - // Pass files that are set to ignore - if (excludeFiles != null && excludeFiles.contains(file.getFileName().toString())) - return FileVisitResult.CONTINUE; - // Copy the files - Path targetFile = targetDir.resolve(sourceDir.relativize(file)); - Files.copy(file, targetFile, COPY_ATTRIBUTES); - return FileVisitResult.CONTINUE; - } - } -} \ No newline at end of file diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MVMessaging.java b/src/main/java/com/onarandombox/MultiverseCore/utils/MVMessaging.java deleted file mode 100644 index 8363c12dd..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MVMessaging.java +++ /dev/null @@ -1,93 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import com.onarandombox.MultiverseCore.api.MultiverseMessaging; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * The default-implementation of {@link MultiverseMessaging}. - */ -public class MVMessaging implements MultiverseMessaging { - private Map sentList; - private int cooldown; - - public MVMessaging() { - this.sentList = new HashMap(); - this.cooldown = 5000; // SUPPRESS CHECKSTYLE: MagicNumberCheck - } - - /** - * {@inheritDoc} - */ - @Override - public void setCooldown(int milliseconds) { - this.cooldown = milliseconds; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean sendMessage(CommandSender sender, String message, boolean ignoreCooldown) { - return this.sendMessages(sender, new String[]{ message }, ignoreCooldown); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean sendMessages(CommandSender sender, String[] messages, boolean ignoreCooldown) { - if (!(sender instanceof Player) || ignoreCooldown) { - - sendMessages(sender, messages); - return true; - } - if (!this.sentList.containsKey(sender.getName())) { - sendMessages(sender, messages); - this.sentList.put(sender.getName(), System.currentTimeMillis()); - return true; - } else { - long time = System.currentTimeMillis(); - if (time >= this.sentList.get(sender.getName()) + this.cooldown) { - sendMessages(sender, messages); - this.sentList.put(sender.getName(), System.currentTimeMillis()); - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean sendMessages(CommandSender sender, Collection messages, boolean ignoreCooldown) { - return this.sendMessages(sender, messages.toArray(new String[0]), ignoreCooldown); - } - - private static void sendMessages(CommandSender sender, String[] messages) { - for (String s : messages) { - sender.sendMessage(s); - } - } - - /** - * {@inheritDoc} - */ - @Override - public int getCooldown() { - return cooldown; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MVPermissions.java b/src/main/java/com/onarandombox/MultiverseCore/utils/MVPermissions.java deleted file mode 100644 index 24ca86924..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MVPermissions.java +++ /dev/null @@ -1,418 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.pneumaticraft.commandhandler.PermissionsInterface; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; - -import java.util.List; -import java.util.logging.Level; - -/** - * Multiverse's {@link PermissionsInterface}. - */ -public class MVPermissions implements PermissionsInterface { - - private MultiverseCore plugin; - private MVWorldManager worldMgr; - - public MVPermissions(MultiverseCore plugin) { - this.plugin = plugin; - this.worldMgr = plugin.getMVWorldManager(); - - } - - /** - * Check if a Player can ignore GameMode restrictions for world they travel to. - * - * @param p The {@link Player} to check. - * @param w The {@link MultiverseWorld} the player wants to teleport to. - * @return True if they should bypass restrictions. - */ - public boolean canIgnoreGameModeRestriction(Player p, MultiverseWorld w) { - return p.hasPermission("mv.bypass.gamemode." + w.getName()); - } - - /** - * Check if a Player can teleport to the Destination world from there current world. - * - * @param p The {@link Player} to check. - * @param w The {@link MultiverseWorld} the player wants to teleport to. - * @return Whether the player can teleport to the given {@link MultiverseWorld}. - */ - public boolean canTravelFromWorld(Player p, MultiverseWorld w) { - List blackList = w.getWorldBlacklist(); - - boolean returnValue = true; - - for (String s : blackList) { - if (s.equalsIgnoreCase(p.getWorld().getName())) { - returnValue = false; - break; - } - } - - return returnValue; - } - - /** - * Checks if the specified {@link CommandSender} can travel to the specified {@link Location}. - * @param sender The {@link CommandSender}. - * @param location The {@link Location}. - * @return Whether the {@link CommandSender} can travel to the specified {@link Location}. - */ - public boolean canTravelFromLocation(CommandSender sender, Location location) { - // Now The Bed destination can return null now. - if (location == null) { - return false; - } - if (!(sender instanceof Player)) { - return true; - } - Player teleporter = (Player) sender; - if (!this.worldMgr.isMVWorld(location.getWorld().getName())) { - return false; - } - return canTravelFromWorld(teleporter, this.worldMgr.getMVWorld(location.getWorld().getName())); - } - - /** - * Check if the Player has the permissions to enter this world. - * - * @param p The {@link Player} player that wants to enter - * @param w The {@link MultiverseWorld} he wants to enter - * @return Whether he has the permission to enter the world - */ - public boolean canEnterWorld(Player p, MultiverseWorld w) { - // If we're not enforcing access, anyone can enter. - if (!plugin.getMVConfig().getEnforceAccess()) { - Logging.finest("EnforceAccess is OFF. Player was allowed in " + w.getAlias()); - return true; - } - return this.hasPermission(p, "multiverse.access." + w.getName(), false); - } - - private boolean canEnterLocation(Player p, Location l) { - if (l == null) { - return false; - } - String worldName = l.getWorld().getName(); - if (!this.plugin.getMVWorldManager().isMVWorld(worldName)) { - return false; - } - return this.hasPermission(p, "multiverse.access." + worldName, false); - } - - /** - * Check to see if a sender can enter a destination. - * The reason this is not a player, is it can be used to simply check permissions - * The console should, for exmaple, always see all worlds - * - * @param sender The CommandSender to check. - * @param d The destination they are requesting. - * @return True if that sender can go to that destination - */ - public boolean canEnterDestination(CommandSender sender, MVDestination d) { - if (!(sender instanceof Player)) { - return true; - } - Player p = (Player) sender; - if (d == null || d.getLocation(p) == null) { - return false; - } - String worldName = d.getLocation(p).getWorld().getName(); - if (!this.worldMgr.isMVWorld(worldName)) { - return false; - } - if (!canEnterLocation(p, d.getLocation(p))) { - return false; - } - return this.hasPermission(p, d.getRequiredPermission(), false); - } - - /** - * Tells a {@link CommandSender} why another {@link CommandSender} can or can not access a certain {@link MVDestination}. - * @param asker The {@link CommandSender} that's asking. - * @param playerInQuestion The {@link CommandSender} whose permissions we want to know. - * @param d The {@link MVDestination}. - */ - public void tellMeWhyICantDoThis(CommandSender asker, CommandSender playerInQuestion, MVDestination d) { - boolean cango = true; - if (!(playerInQuestion instanceof Player)) { - asker.sendMessage(String.format("The console can do %severything%s.", ChatColor.RED, ChatColor.WHITE)); - return; - } - Player p = (Player) playerInQuestion; - if (d == null) { - asker.sendMessage(String.format("The provided Destination is %sNULL%s, and therefore %sINVALID%s.", - ChatColor.RED, ChatColor.WHITE, ChatColor.RED, ChatColor.WHITE)); - cango = false; - } - // We know it'll be a player here due to the first line of this method. - if (d.getLocation(p) == null) { - asker.sendMessage(String.format( - "The player will spawn at an %sindeterminate location%s. Talk to the MV Devs if you see this", - ChatColor.RED, ChatColor.WHITE)); - cango = false; - } - String worldName = d.getLocation(p).getWorld().getName(); - if (!this.worldMgr.isMVWorld(worldName)) { - asker.sendMessage(String.format("The destination resides in a world(%s%s%s) that is not managed by Multiverse.", - ChatColor.AQUA, worldName, ChatColor.WHITE)); - asker.sendMessage(String.format("Type %s/mv import ?%s to see the import command's help page.", - ChatColor.DARK_AQUA, ChatColor.WHITE)); - cango = false; - } - if (!this.hasPermission(p, "multiverse.access." + worldName, false)) { - asker.sendMessage(String.format("The player (%s%s%s) does not have the required world entry permission (%s%s%s) to go to the destination (%s%s%s).", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, "multiverse.access." + worldName, ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - cango = false; - } - if (!this.hasPermission(p, d.getRequiredPermission(), false)) { - asker.sendMessage(String.format("The player (%s%s%s) does not have the required entry permission (%s%s%s) to go to the destination (%s%s%s).", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, d.getRequiredPermission(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - cango = false; - } - if (cango) { - asker.sendMessage(String.format("The player (%s%s%s) CAN go to the destination (%s%s%s).", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - } else { - asker.sendMessage(String.format("The player (%s%s%s) cannot access the destination %s%s%s. Therefore they can't use mvtp at all for this.", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - return; - } - if (!this.hasPermission(p, "multiverse.teleport.self." + d.getIdentifier(), false)) { - asker.sendMessage(String.format("The player (%s%s%s) does not have the required teleport permission (%s%s%s) to use %s/mvtp %s%s.", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, "multiverse.teleport.self." + d.getIdentifier(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - } else { - asker.sendMessage(String.format("The player (%s%s%s) has the required teleport permission (%s%s%s) to use %s/mvtp %s%s.", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, "multiverse.teleport.self." + d.getIdentifier(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - } - if (!this.hasPermission(p, "multiverse.teleport.other." + d.getIdentifier(), false)) { - asker.sendMessage(String.format("The player (%s%s%s) does not have the required teleport permission (%s%s%s) to send others to %s%s%s via mvtp.", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, "multiverse.teleport.other." + d.getIdentifier(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - } else { - asker.sendMessage(String.format("The player (%s%s%s) has required teleport permission (%s%s%s) to send others to %s%s%s via mvtp.", - ChatColor.AQUA, p.getDisplayName(), ChatColor.WHITE, - ChatColor.GREEN, "multiverse.teleport.other." + d.getIdentifier(), ChatColor.WHITE, - ChatColor.DARK_AQUA, d.getName(), ChatColor.WHITE)); - } - } - - /** - * Check to see if a player has a permission. - * - * @param sender Who is requesting the permission. - * @param node The permission node in string format; multiverse.core.list.worlds for example. - * @param isOpRequired deprecated This is not used for anything anymore. - * @return True if they have that permission or any parent. - */ - @Override - public boolean hasPermission(CommandSender sender, String node, boolean isOpRequired) { - if (!(sender instanceof Player)) { - return true; - } - // NO one can access a null permission (mainly used for destinations):w - if (node == null) { - return false; - } - // Everyone can access an empty permission - // Currently used for the PlayerDestination - if (node.equals("")) { - return true; - } - - return checkActualPermission(sender, node); - } - - // TODO: Better player checks, most likely not needed, but safer. - private boolean checkActualPermission(CommandSender sender, String node) { - Player player = (Player) sender; - - boolean hasPermission = sender.hasPermission(node); - if (!sender.isPermissionSet(node)) { - Logging.finer(String.format("The node [%s%s%s] was %sNOT%s set for [%s%s%s].", - ChatColor.RED, node, ChatColor.WHITE, ChatColor.RED, ChatColor.WHITE, ChatColor.AQUA, - player.getDisplayName(), ChatColor.WHITE)); - } - if (hasPermission) { - Logging.finer("Checking to see if player [" + player.getName() + "] has permission [" + node + "]... YES"); - } else { - Logging.finer("Checking to see if player [" + player.getName() + "] has permission [" + node + "]... NO"); - } - return hasPermission; - } - - /** - * Checks to see if the sender has any parent perms. - * Stops when it finds one or when there are no more parents. - * This method is recursive. - * - * @param sender Who is asking for the permission. - * @param node The permission node to check (possibly already a parent). - * @return True if they have any parent perm, false if none. - */ - // TODO remove this...? - private boolean hasAnyParentPermission(CommandSender sender, String node) { - String parentPerm = this.pullOneLevelOff(node); - // Base case - if (parentPerm == null) { - return false; - } - // If they have a parent, they're good - if (this.checkActualPermission(sender, parentPerm + ".*")) { - return true; - } - return hasAnyParentPermission(sender, parentPerm); - } - - /** - * Pulls one level off of a yaml style node. - * Given multiverse.core.list.worlds will return multiverse.core.list - * - * @param node The root node to check. - * @return The parent of the node - */ - private static String pullOneLevelOff(String node) { - if (node == null) { - return null; - } - int index = node.lastIndexOf("."); - if (index > 0) { - return node.substring(0, index); - } - return null; - } - - /** - * Gets the type of this {@link PermissionsInterface}. - * @return The type of this {@link PermissionsInterface}. - */ - public String getType() { - return "Bukkit Permissions (SuperPerms)"; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasAnyPermission(CommandSender sender, List nodes, boolean isOpRequired) { - for (String node : nodes) { - if (this.hasPermission(sender, node, isOpRequired)) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasAllPermission(CommandSender sender, List nodes, boolean isOpRequired) { - for (String node : nodes) { - if (!this.hasPermission(sender, node, isOpRequired)) { - return false; - } - } - return true; - } - - /** - * Adds a permission. - * @param string The permission as {@link String}. - * @param defaultValue The default-value. - * @return The permission as {@link Permission}. - */ - public Permission addPermission(String string, PermissionDefault defaultValue) { - if (this.plugin.getServer().getPluginManager().getPermission(string) == null) { - Permission permission = new Permission(string, defaultValue); - this.plugin.getServer().getPluginManager().addPermission(permission); - this.addToParentPerms(string); - } - return this.plugin.getServer().getPluginManager().getPermission(string); - } - - private void addToParentPerms(String permString) { - String permStringChopped = permString.replace(".*", ""); - - String[] seperated = permStringChopped.split("\\."); - String parentPermString = getParentPerm(seperated); - if (parentPermString == null) { - addToRootPermission("*", permStringChopped); - addToRootPermission("*.*", permStringChopped); - return; - } - Permission parentPermission = this.plugin.getServer().getPluginManager().getPermission(parentPermString); - // Creat parent and grandparents - if (parentPermission == null) { - parentPermission = new Permission(parentPermString); - this.plugin.getServer().getPluginManager().addPermission(parentPermission); - - this.addToParentPerms(parentPermString); - } - // Create actual perm. - Permission actualPermission = this.plugin.getServer().getPluginManager().getPermission(permString); - // Extra check just to make sure the actual one is added - if (actualPermission == null) { - - actualPermission = new Permission(permString); - this.plugin.getServer().getPluginManager().addPermission(actualPermission); - } - if (!parentPermission.getChildren().containsKey(permString)) { - parentPermission.getChildren().put(actualPermission.getName(), true); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(parentPermission); - } - } - - private void addToRootPermission(String rootPerm, String permStringChopped) { - Permission rootPermission = this.plugin.getServer().getPluginManager().getPermission(rootPerm); - if (rootPermission == null) { - rootPermission = new Permission(rootPerm); - this.plugin.getServer().getPluginManager().addPermission(rootPermission); - } - rootPermission.getChildren().put(permStringChopped + ".*", true); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(rootPermission); - } - - /** - * If the given permission was 'multiverse.core.tp.self', this would return 'multiverse.core.tp.*'. - */ - private static String getParentPerm(String[] seperated) { - if (seperated.length == 1) { - return null; - } - String returnString = ""; - for (int i = 0; i < seperated.length - 1; i++) { - returnString += seperated[i] + "."; - } - return returnString + "*"; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MVPlayerSession.java b/src/main/java/com/onarandombox/MultiverseCore/utils/MVPlayerSession.java deleted file mode 100644 index c1c10cbbf..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MVPlayerSession.java +++ /dev/null @@ -1,46 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.onarandombox.MultiverseCore.api.MultiverseCoreConfig; -import org.bukkit.entity.Player; - -import java.util.Date; - -/** - * A player-session. - */ -public class MVPlayerSession { - - private Player player; // Player holder, may be unnecessary. - - private long teleportLast = 0L; // Timestamp for the Players last Portal Teleportation. - private long messageLast = 0L; // Timestamp for the Players last Alert Message. - - private MultiverseCoreConfig config; // Configuration file to find out Cooldown Timers. - - public MVPlayerSession(Player player, MultiverseCoreConfig config) { - this.player = player; - this.config = config; - // this.bedSpawn = null; - } - - /** Update the Teleport time. */ - public void teleport() { - this.teleportLast = (new Date()).getTime(); - } - - /** - * Grab whether the cooldown on Portal use has expired or not. - * @return True if the {@link Player} associated with this player-session is teleportable. - */ - public boolean getTeleportable() { - long time = (new Date()).getTime(); - return ((time - this.teleportLast) > this.config.getTeleportCooldown()); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MVTravelAgent.java b/src/main/java/com/onarandombox/MultiverseCore/utils/MVTravelAgent.java deleted file mode 100644 index a86def8a5..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MVTravelAgent.java +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import org.bukkit.entity.Player; - -/** - * The Multiverse TravelAgent. - */ -public class MVTravelAgent { - protected MVDestination destination; - protected MultiverseCore core; - protected Player player; - - public MVTravelAgent(MultiverseCore multiverseCore, MVDestination d, Player p) { - this.destination = d; - this.core = multiverseCore; - this.player = p; - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MaterialConverter.java b/src/main/java/com/onarandombox/MultiverseCore/utils/MaterialConverter.java deleted file mode 100644 index 0fd19457e..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MaterialConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.onarandombox.MultiverseCore.utils; - -import de.themoep.idconverter.IdMappings; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * A tool for converting values which may be an old type ID to a Material. - */ -public class MaterialConverter { - - /** - * Converts the value in the given config at the given path from a numeric id or flattened material name to a - * Material. - * - * @param config The config with the value to convert. - * @param path The path of the value in the config. - * @return The converted Material type or null if no matching type. - */ - @Nullable - public static Material convertConfigType(@NotNull ConfigurationSection config, @NotNull String path) { - return convertTypeString(config.getString(path)); - } - - /** - * Converts a string representing a numeric id or flattened material name to a Material. - * - * @param value The value to convert. - * @return The converted Material type or null if no matching type. - */ - @Nullable - public static Material convertTypeString(@Nullable String value) { - IdMappings.Mapping mapping = IdMappings.getById(value != null ? value : ""); - if (mapping != null) { - return Material.matchMaterial(mapping.getFlatteningType()); - } else { - return Material.matchMaterial(value != null ? value : ""); - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/PermissionTools.java b/src/main/java/com/onarandombox/MultiverseCore/utils/PermissionTools.java deleted file mode 100644 index ef5e08c4b..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/PermissionTools.java +++ /dev/null @@ -1,303 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; - -import java.util.logging.Level; - -/** - * Utility-class for permissions. - */ -public class PermissionTools { - private MultiverseCore plugin; - - public PermissionTools(MultiverseCore plugin) { - this.plugin = plugin; - } - - /** - * Adds a permission to the parent-permissions. - * @param permString The new permission as {@link String}. - */ - public void addToParentPerms(String permString) { - String permStringChopped = permString.replace(".*", ""); - - String[] seperated = permStringChopped.split("\\."); - String parentPermString = getParentPerm(seperated); - if (parentPermString == null) { - addToRootPermission("*", permStringChopped); - addToRootPermission("*.*", permStringChopped); - return; - } - Permission parentPermission = this.plugin.getServer().getPluginManager().getPermission(parentPermString); - // Creat parent and grandparents - if (parentPermission == null) { - parentPermission = new Permission(parentPermString); - this.plugin.getServer().getPluginManager().addPermission(parentPermission); - - this.addToParentPerms(parentPermString); - } - // Create actual perm. - Permission actualPermission = this.plugin.getServer().getPluginManager().getPermission(permString); - // Extra check just to make sure the actual one is added - if (actualPermission == null) { - - actualPermission = new Permission(permString); - this.plugin.getServer().getPluginManager().addPermission(actualPermission); - } - if (!parentPermission.getChildren().containsKey(permString)) { - parentPermission.getChildren().put(actualPermission.getName(), true); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(parentPermission); - } - } - - private void addToRootPermission(String rootPerm, String permStringChopped) { - Permission rootPermission = this.plugin.getServer().getPluginManager().getPermission(rootPerm); - if (rootPermission == null) { - rootPermission = new Permission(rootPerm); - this.plugin.getServer().getPluginManager().addPermission(rootPermission); - } - rootPermission.getChildren().put(permStringChopped + ".*", true); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(rootPermission); - } - - /** - * If the given permission was 'multiverse.core.tp.self', this would return 'multiverse.core.tp.*'. - * - * @param separatedPermissionString The array of a dot separated perm string. - * @return The dot separated parent permission string. - */ - private static String getParentPerm(String[] separatedPermissionString) { - if (separatedPermissionString.length == 1) { - return null; - } - String returnString = ""; - for (int i = 0; i < separatedPermissionString.length - 1; i++) { - returnString += separatedPermissionString[i] + "."; - } - return returnString + "*"; - } - - /** - * Checks if the given {@link Player} has enough money to enter the specified {@link MultiverseWorld}. - * @param fromWorld The {@link MultiverseWorld} the player is coming from. - * @param toWorld The {@link MultiverseWorld} the player is going to. - * @param teleporter The teleporter. - * @param teleportee The teleportee. - * @param pay If the player has to pay the money. - * @return True if the player can enter the world. - */ - public boolean playerHasMoneyToEnter(MultiverseWorld fromWorld, MultiverseWorld toWorld, CommandSender teleporter, Player teleportee, boolean pay) { - Player teleporterPlayer; - if (plugin.getMVConfig().getTeleportIntercept()) { - if (teleporter instanceof ConsoleCommandSender) { - return true; - } - - if (teleporter == null) { - teleporter = teleportee; - } - - if (!(teleporter instanceof Player)) { - return false; - } - teleporterPlayer = (Player) teleporter; - } else { - if (teleporter instanceof Player) { - teleporterPlayer = (Player) teleporter; - } else { - teleporterPlayer = null; - } - - // Old-style! - if (teleporterPlayer == null) { - return true; - } - } - - // If the toWorld isn't controlled by MV, - // We don't care. - if (toWorld == null) { - return true; - } - - // Only check payments if it's a different world: - if (!toWorld.equals(fromWorld)) { - final double price = toWorld.getPrice(); - // Don't bother checking economy stuff if it doesn't even cost to enter. - if (price == 0D) { - return true; - } - // If the player does not have to pay, return now. - if (this.plugin.getMVPerms().hasPermission(teleporter, toWorld.getExemptPermission().getName(), true)) { - return true; - } - - final MVEconomist economist = plugin.getEconomist(); - final Material currency = toWorld.getCurrency(); - final String formattedAmount = economist.formatPrice(price, currency); - - if (economist.isPlayerWealthyEnough(teleporterPlayer, price, currency)) { - if (pay) { - if (price < 0D) { - economist.deposit(teleporterPlayer, -price, currency); - } else { - economist.withdraw(teleporterPlayer, price, currency); - } - sendTeleportPaymentMessage(economist, teleporterPlayer, teleportee, toWorld.getColoredWorldString(), price, currency); - } - } else { - if (teleportee.equals(teleporter)) { - teleporterPlayer.sendMessage(economist.getNSFMessage(currency, - "You need " + formattedAmount + " to enter " + toWorld.getColoredWorldString())); - } else { - teleporterPlayer.sendMessage(economist.getNSFMessage(currency, - "You need " + formattedAmount + " to send " + teleportee.getName() + " to " + toWorld.getColoredWorldString())); - } - return false; - } - } - return true; - } - - private void sendTeleportPaymentMessage (MVEconomist economist, Player teleporterPlayer, Player teleportee, String toWorld, double price, Material currency) { - price = Math.abs(price); - if (teleporterPlayer.equals(teleportee)) { - teleporterPlayer.sendMessage("You were " + (price > 0D ? "charged " : "given ") + economist.formatPrice(price, currency) + " for teleporting to " + toWorld); - } else { - teleporterPlayer.sendMessage("You were " + (price > 0D ? "charged " : "given ") + economist.formatPrice(price, currency) + " for teleporting " + teleportee.getName() + " to " + toWorld); - } - } - - - /** - * Checks to see if player can go to a world given their current status. - *

- * The return is a little backwards, and will return a value safe for event.setCancelled. - * - * @param fromWorld The MultiverseWorld they are in. - * @param toWorld The MultiverseWorld they want to go to. - * @param teleporter The CommandSender that wants to send someone somewhere. If null, - * will be given the same value as teleportee. - * @param teleportee The player going somewhere. - * @return True if they can't go to the world, False if they can. - */ - public boolean playerCanGoFromTo(MultiverseWorld fromWorld, MultiverseWorld toWorld, CommandSender teleporter, Player teleportee) { - Logging.finest("Checking '" + teleporter + "' can send '" + teleportee + "' somewhere"); - - Player teleporterPlayer; - if (plugin.getMVConfig().getTeleportIntercept()) { - // The console can send anyone anywhere - if (teleporter instanceof ConsoleCommandSender) { - return true; - } - - // Make sure we have a teleporter of some kind, even if it's inferred to be the teleportee - if (teleporter == null) { - teleporter = teleportee; - } - - // Now make sure we can cast the teleporter to a player, 'cause I'm tired of console things now - if (!(teleporter instanceof Player)) { - return false; - } - teleporterPlayer = (Player) teleporter; - } else { - if (teleporter instanceof Player) { - teleporterPlayer = (Player) teleporter; - } else { - teleporterPlayer = null; - } - - // Old-style! - if (teleporterPlayer == null) { - return true; - } - } - - // Actual checks - if (toWorld != null) { - if (!this.plugin.getMVPerms().canEnterWorld(teleporterPlayer, toWorld)) { - if (teleportee.equals(teleporter)) { - teleporter.sendMessage("You don't have access to go here..."); - } else { - teleporter.sendMessage("You can't send " + teleportee.getName() + " here..."); - } - - return false; - } - } else { - // TODO: Determine if this value is false because a world didn't exist - // or if it was because a world wasn't imported. - return true; - } - if (fromWorld != null) { - if (fromWorld.getWorldBlacklist().contains(toWorld.getName())) { - if (teleportee.equals(teleporter)) { - teleporter.sendMessage("You don't have access to go to " + toWorld.getColoredWorldString() + " from " + fromWorld.getColoredWorldString()); - } else { - teleporter.sendMessage("You don't have access to send " + teleportee.getName() + " from " - + fromWorld.getColoredWorldString() + " to " + toWorld.getColoredWorldString()); - } - return false; - } - } - return true; - } - - /** - * Checks to see if a player can bypass the player limit. - * - * @param toWorld The world travelling to. - * @param teleporter The player that initiated the teleport. - * @param teleportee The player travelling. - * @return True if they can bypass the player limit. - */ - public boolean playerCanBypassPlayerLimit(MultiverseWorld toWorld, CommandSender teleporter, Player teleportee) { - if (teleporter == null) { - teleporter = teleportee; - } - - if (!(teleporter instanceof Player)) { - return true; - } - - MVPermissions perms = plugin.getMVPerms(); - if (perms.hasPermission(teleportee, "mv.bypass.playerlimit." + toWorld.getName(), false)) { - return true; - } else { - teleporter.sendMessage("The world " + toWorld.getColoredWorldString() + " is full"); - return false; - } - } - - /** - * Checks to see if a player should bypass game mode restrictions. - * - * @param toWorld world travelling to. - * @param teleportee player travelling. - * @return True if they should bypass restrictions - */ - public boolean playerCanIgnoreGameModeRestriction(MultiverseWorld toWorld, Player teleportee) { - if (toWorld != null) { - return this.plugin.getMVPerms().canIgnoreGameModeRestriction(teleportee, toWorld); - } else { - // TODO: Determine if this value is false because a world didn't exist - // or if it was because a world wasn't imported. - return true; - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/PurgeWorlds.java b/src/main/java/com/onarandombox/MultiverseCore/utils/PurgeWorlds.java deleted file mode 100644 index dbd3bf877..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/PurgeWorlds.java +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; - -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Animals; -import org.bukkit.entity.EnderDragon; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Squid; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; - -/** - * Utility class that removes animals from worlds that don't belong there. - * - * @deprecated Use instead: {@link com.onarandombox.MultiverseCore.api.WorldPurger} and {@link SimpleWorldPurger}. - */ -@Deprecated -public class PurgeWorlds { - - private MultiverseCore plugin; - - public PurgeWorlds(MultiverseCore plugin) { - this.plugin = plugin; - } - - /** - * Synchronizes the given world with it's settings. - * - * @param sender The {@link CommandSender} who is requesting the world be purged. - * @param worlds A list of {@link MultiverseWorld} - */ - public void purgeWorlds(CommandSender sender, List worlds) { - if (worlds == null || worlds.isEmpty()) { - return; - } - for (MultiverseWorld world : worlds) { - this.purgeWorld(sender, world); - } - } - - /** - * Convenience method for {@link #purgeWorld(CommandSender, MultiverseWorld, List, boolean, boolean)} that takes the settings from the world-config. - * - * @param sender The {@link CommandSender} that initiated the action - * @param world The {@link MultiverseWorld}. - */ - public void purgeWorld(CommandSender sender, MultiverseWorld world) { - if (world == null) { - return; - } - ArrayList allMobs = new ArrayList(world.getAnimalList()); - allMobs.addAll(world.getMonsterList()); - purgeWorld(sender, world, allMobs, !world.canAnimalsSpawn(), !world.canMonstersSpawn()); - } - - /** - * Clear all animals/monsters that do not belong to a world according to the config. - * @param sender The {@link CommandSender} that initiated the action. - * @param mvworld The {@link MultiverseWorld}. - * @param thingsToKill A {@link List} of animals/monsters to be killed. - * @param negateAnimals Whether the monsters in the list should be negated. - * @param negateMonsters Whether the animals in the list should be negated. - */ - public void purgeWorld(CommandSender sender, MultiverseWorld mvworld, List thingsToKill, boolean negateAnimals, boolean negateMonsters) { - if (mvworld == null) { - return; - } - World world = this.plugin.getServer().getWorld(mvworld.getName()); - if (world == null) { - return; - } - int entitiesKilled = 0; - for (Entity e : world.getEntities()) { - Logging.finest("Entity list (aval for purge) from WORLD < " + mvworld.getName() + " >: " + e.toString()); - - // Check against Monsters - if (killMonster(mvworld, e, thingsToKill, negateMonsters)) { - entitiesKilled++; - continue; - } - // Check against Animals - if (this.killCreature(mvworld, e, thingsToKill, negateAnimals)) { - entitiesKilled++; - } - - } - if (sender != null) { - sender.sendMessage(entitiesKilled + " entities purged from the world '" + world.getName() + "'"); - } - } - - private boolean killCreature(MultiverseWorld mvworld, Entity e, List creaturesToKill, boolean negate) { - String entityName = e.toString().replaceAll("Craft", "").toUpperCase(); - if (e instanceof Squid || e instanceof Animals) { - if (creaturesToKill.contains(entityName) || creaturesToKill.contains("ALL") || creaturesToKill.contains("ANIMALS")) { - if (!negate) { - e.remove(); - return true; - } - } else { - if (negate) { - e.remove(); - return true; - } - } - } - return false; - } - - /** - * Will kill the monster if it's in the list UNLESS the NEGATE boolean is set, then it will kill it if it's NOT. - */ - private boolean killMonster(MultiverseWorld mvworld, Entity e, List creaturesToKill, boolean negate) { - String entityName = ""; - if (e instanceof EnderDragon) { - entityName = "ENDERDRAGON"; - } else { - entityName = e.toString().replaceAll("Craft", "").toUpperCase(); - } - if (e instanceof Slime || e instanceof Monster || e instanceof Ghast || e instanceof EnderDragon) { - Logging.finest("Looking at a monster: " + e); - if (creaturesToKill.contains(entityName) || creaturesToKill.contains("ALL") || creaturesToKill.contains("MONSTERS")) { - if (!negate) { - Logging.finest("Removing a monster: " + e); - e.remove(); - return true; - } - } else { - if (negate) { - Logging.finest("Removing a monster: " + e); - e.remove(); - return true; - } - } - } - return false; - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleBlockSafety.java b/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleBlockSafety.java deleted file mode 100644 index 3072d8a8d..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleBlockSafety.java +++ /dev/null @@ -1,268 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.api.BlockSafety; -import com.onarandombox.MultiverseCore.api.Core; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Bed; -import org.bukkit.entity.Minecart; -import org.bukkit.entity.Vehicle; - -import java.util.EnumSet; -import java.util.Iterator; -import java.util.Set; - -/** - * The default-implementation of {@link BlockSafety}. - */ -public class SimpleBlockSafety implements BlockSafety { - private final Core plugin; - private static final Set AROUND_BLOCK = EnumSet.noneOf(BlockFace.class); - - static { - AROUND_BLOCK.add(BlockFace.NORTH); - AROUND_BLOCK.add(BlockFace.NORTH_EAST); - AROUND_BLOCK.add(BlockFace.EAST); - AROUND_BLOCK.add(BlockFace.SOUTH_EAST); - AROUND_BLOCK.add(BlockFace.SOUTH); - AROUND_BLOCK.add(BlockFace.SOUTH_WEST); - AROUND_BLOCK.add(BlockFace.WEST); - AROUND_BLOCK.add(BlockFace.NORTH_WEST); - } - - public SimpleBlockSafety(Core plugin) { - this.plugin = plugin; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isBlockAboveAir(Location l) { - Location downOne = l.clone(); - downOne.setY(downOne.getY() - 1); - return (downOne.getBlock().getType() == Material.AIR); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean playerCanSpawnHereSafely(World world, double x, double y, double z) { - Location l = new Location(world, x, y, z); - return playerCanSpawnHereSafely(l); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean playerCanSpawnHereSafely(Location l) { - if (l == null) { - // Can't safely spawn at a null location! - return false; - } - - World world = l.getWorld(); - Location actual = l.clone(); - Location upOne = l.clone(); - Location downOne = l.clone(); - upOne.setY(upOne.getY() + 1); - downOne.setY(downOne.getY() - 1); - - if (isSolidBlock(world.getBlockAt(actual).getType()) - || isSolidBlock(upOne.getBlock().getType())) { - Logging.finer("Error Here (Actual)? (%s)[%s]", actual.getBlock().getType(), - isSolidBlock(actual.getBlock().getType())); - Logging.finer("Error Here (upOne)? (%s)[%s]", upOne.getBlock().getType(), - isSolidBlock(upOne.getBlock().getType())); - return false; - } - - if (downOne.getBlock().getType() == Material.LAVA) { - Logging.finer("Error Here (downOne)? (%s)[%s]", downOne.getBlock().getType(), isSolidBlock(downOne.getBlock().getType())); - return false; - } - - if (downOne.getBlock().getType() == Material.FIRE) { - Logging.finer("There's fire below! (%s)[%s]", actual.getBlock().getType(), isSolidBlock(actual.getBlock().getType())); - return false; - } - - if (isBlockAboveAir(actual)) { - Logging.finer("Is block above air [%s]", isBlockAboveAir(actual)); - Logging.finer("Has 2 blocks of water below [%s]", this.hasTwoBlocksofWaterBelow(actual)); - return this.hasTwoBlocksofWaterBelow(actual); - } - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getSafeBedSpawn(Location l) { - // The passed location, may be null (if the bed is invalid) - if (l == null) { - return null; - } - final Location trySpawn = this.getSafeSpawnAroundABlock(l); - if (trySpawn != null) { - return trySpawn; - } - Location otherBlock = this.findOtherBedPiece(l); - if (otherBlock == null) { - return null; - } - // Now we have 2 locations, check around each, if the type is bed, skip it. - return this.getSafeSpawnAroundABlock(otherBlock); - } - - /** - * Find a safe spawn around a location. (N,S,E,W,NE,NW,SE,SW) - * @param l Location to check around - * @return A safe location, or none if it wasn't found. - */ - private Location getSafeSpawnAroundABlock(Location l) { - Iterator checkblock = AROUND_BLOCK.iterator(); - while (checkblock.hasNext()) { - final BlockFace face = checkblock.next(); - if (this.playerCanSpawnHereSafely(l.getBlock().getRelative(face).getLocation())) { - // Don't forget to center the player. - return l.getBlock().getRelative(face).getLocation().add(.5, 0, .5); - } - } - return null; - } - - /** - * Find the other bed block. - * @param checkLoc The location to check for the other piece at - * @return The location of the other bed piece, or null if it was a jacked up bed. - */ - private Location findOtherBedPiece(Location checkLoc) { - BlockData data = checkLoc.getBlock().getBlockData(); - if (!(data instanceof Bed)) { - return null; - } - Bed b = (Bed) data; - - if (b.getPart() == Bed.Part.HEAD) { - return checkLoc.getBlock().getRelative(b.getFacing().getOppositeFace()).getLocation(); - } - // We shouldn't ever be looking at the foot, but here's the code for it. - return checkLoc.getBlock().getRelative(b.getFacing()).getLocation(); - } - - - /** - * {@inheritDoc} - */ - @Override - public Location getTopBlock(Location l) { - Location check = l.clone(); - check.setY(127); // SUPPRESS CHECKSTYLE: MagicNumberCheck - while (check.getY() > 0) { - if (this.playerCanSpawnHereSafely(check)) { - return check; - } - check.setY(check.getY() - 1); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getBottomBlock(Location l) { - Location check = l.clone(); - check.setY(0); - while (check.getY() < 127) { // SUPPRESS CHECKSTYLE: MagicNumberCheck - if (this.playerCanSpawnHereSafely(check)) { - return check; - } - check.setY(check.getY() + 1); - } - return null; - } - - /* - * If someone has a better way of this... Please either tell us, or submit a pull request! - */ - public static boolean isSolidBlock(Material type) { - return type.isSolid(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEntitiyOnTrack(Location l) { - Material currentBlock = l.getBlock().getType(); - return (currentBlock == Material.POWERED_RAIL - || currentBlock == Material.DETECTOR_RAIL - || currentBlock == Material.RAIL - || currentBlock == Material.ACTIVATOR_RAIL); - } - - /** - * Checks recursively below a {@link Location} for 2 blocks of water. - * - * @param l The {@link Location} - * @return Whether there are 2 blocks of water - */ - private boolean hasTwoBlocksofWaterBelow(Location l) { - if (l.getBlockY() < 0) { - return false; - } - Location oneBelow = l.clone(); - oneBelow.subtract(0, 1, 0); - if (oneBelow.getBlock().getType() == Material.WATER) { - Location twoBelow = oneBelow.clone(); - twoBelow.subtract(0, 1, 0); - return (oneBelow.getBlock().getType() == Material.WATER); - } - if (oneBelow.getBlock().getType() != Material.AIR) { - return false; - } - return hasTwoBlocksofWaterBelow(oneBelow); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canSpawnCartSafely(Minecart cart) { - if (this.isBlockAboveAir(cart.getLocation())) { - return true; - } - if (this.isEntitiyOnTrack(plugin.getLocationManipulation().getNextBlock(cart))) { - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean canSpawnVehicleSafely(Vehicle vehicle) { - if (this.isBlockAboveAir(vehicle.getLocation())) { - return true; - } - return false; - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleLocationManipulation.java b/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleLocationManipulation.java deleted file mode 100644 index 92767d632..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleLocationManipulation.java +++ /dev/null @@ -1,261 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Vehicle; -import org.bukkit.util.Vector; - -import com.onarandombox.MultiverseCore.api.LocationManipulation; - -import java.text.DecimalFormat; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * The default-implementation of {@link LocationManipulation}. - */ -public class SimpleLocationManipulation implements LocationManipulation { - private static final Map ORIENTATION_INTS; - - static { - Map orientationInts = new HashMap(); - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - orientationInts.put("n", 180); - orientationInts.put("ne", 225); - orientationInts.put("e", 270); - orientationInts.put("se", 315); - orientationInts.put("s", 0); - orientationInts.put("sw", 45); - orientationInts.put("w", 90); - orientationInts.put("nw", 135); - - orientationInts.put("north", 180); - orientationInts.put("northeast", 225); - orientationInts.put("east", 270); - orientationInts.put("southeast", 315); - orientationInts.put("south", 0); - orientationInts.put("southwest", 45); - orientationInts.put("west", 90); - orientationInts.put("northwest", 135); - - // "freeze" the map: - ORIENTATION_INTS = Collections.unmodifiableMap(orientationInts); - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - } - - /** - * {@inheritDoc} - */ - @Override - public String locationToString(Location location) { - if (location == null) { - return ""; - } - return String.format(Locale.ENGLISH, "%s:%.2f,%.2f,%.2f:%.2f:%.2f", location.getWorld().getName(), - location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); - } - - /** - * {@inheritDoc} - */ - @Override - public Location getBlockLocation(Location l) { - l.setX(l.getBlockX()); - l.setY(l.getBlockY()); - l.setZ(l.getBlockZ()); - return l; - } - - /** - * {@inheritDoc} - */ - @Override - public Location stringToLocation(String locationString) { - //format: - //world:x,y,z:pitch:yaw - if (locationString == null) { - return null; - } - - // Split the whole string, format is: - // {'world', 'x,y,z'[, 'pitch', 'yaw']} - String[] split = locationString.split(":"); - if (split.length < 2 || split.length > 4) { // SUPPRESS CHECKSTYLE: MagicNumberCheck - return null; - } - // Split the xyz string, format is: - // {'x', 'y', 'z'} - String[] xyzsplit = split[1].split(","); - if (xyzsplit.length != 3) { - return null; - } - - // Verify the world is valid - World w = Bukkit.getWorld(split[0]); - if (w == null) { - return null; - } - - try { - float pitch = 0; - float yaw = 0; - if (split.length >= 3) { - yaw = (float) Double.parseDouble(split[2]); - } - if (split.length == 4) { // SUPPRESS CHECKSTYLE: MagicNumberCheck - pitch = (float) Double.parseDouble(split[3]); - } - return new Location(w, Double.parseDouble(xyzsplit[0]), Double.parseDouble(xyzsplit[1]), Double.parseDouble(xyzsplit[2]), yaw, pitch); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * {@inheritDoc} - */ - @Override - public String strCoords(Location l) { - String result = ""; - DecimalFormat df = new DecimalFormat(); - df.setMinimumFractionDigits(0); - df.setMaximumFractionDigits(2); - result += ChatColor.WHITE + "X: " + ChatColor.AQUA + df.format(l.getX()) + " "; - result += ChatColor.WHITE + "Y: " + ChatColor.AQUA + df.format(l.getY()) + " "; - result += ChatColor.WHITE + "Z: " + ChatColor.AQUA + df.format(l.getZ()) + " "; - result += ChatColor.WHITE + "P: " + ChatColor.GOLD + df.format(l.getPitch()) + " "; - result += ChatColor.WHITE + "Y: " + ChatColor.GOLD + df.format(l.getYaw()) + " "; - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public String strCoordsRaw(Location l) { - if (l == null) { - return "null"; - } - String result = ""; - DecimalFormat df = new DecimalFormat(); - df.setMinimumFractionDigits(0); - df.setMaximumFractionDigits(2); - result += "X: " + df.format(l.getX()) + " "; - result += "Y: " + df.format(l.getY()) + " "; - result += "Z: " + df.format(l.getZ()) + " "; - result += "P: " + df.format(l.getPitch()) + " "; - result += "Y: " + df.format(l.getYaw()) + " "; - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public String getDirection(Location location) { - // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck - double r = (location.getYaw() % 360) + 180; - // Remember, these numbers are every 45 degrees with a 22.5 offset, to detect boundaries. - String dir; - if (r < 22.5) - dir = "n"; - else if (r < 67.5) - dir = "ne"; - else if (r < 112.5) - dir = "e"; - else if (r < 157.5) - dir = "se"; - else if (r < 202.5) - dir = "s"; - else if (r < 247.5) - dir = "sw"; - else if (r < 292.5) - dir = "w"; - else if (r < 337.5) - dir = "nw"; - else - dir = "n"; - // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck - - return dir; - } - - /** - * {@inheritDoc} - */ - @Override - public float getYaw(String orientation) { - if (orientation == null) { - return 0; - } - if (ORIENTATION_INTS.containsKey(orientation.toLowerCase())) { - return ORIENTATION_INTS.get(orientation.toLowerCase()); - } - return 0; - } - - /** - * {@inheritDoc} - */ - @Override - public float getSpeed(Vector v) { - return (float) Math.sqrt(v.getX() * v.getX() + v.getZ() * v.getZ()); - } - - // X, Y, Z - // -W/+E,0, -N/+S - - /** - * {@inheritDoc} - */ - @Override - public Vector getTranslatedVector(Vector v, String direction) { - if (direction == null) { - return v; - } - float speed = getSpeed(v); - float halfSpeed = (float) (speed / 2.0); - // TODO: Mathmatacize this: - if (direction.equalsIgnoreCase("n")) { - return new Vector(0, 0, -1 * speed); - } else if (direction.equalsIgnoreCase("ne")) { - return new Vector(halfSpeed, 0, -1 * halfSpeed); - } else if (direction.equalsIgnoreCase("e")) { - return new Vector(speed, 0, 0); - } else if (direction.equalsIgnoreCase("se")) { - return new Vector(halfSpeed, 0, halfSpeed); - } else if (direction.equalsIgnoreCase("s")) { - return new Vector(0, 0, speed); - } else if (direction.equalsIgnoreCase("sw")) { - return new Vector(-1 * halfSpeed, 0, halfSpeed); - } else if (direction.equalsIgnoreCase("w")) { - return new Vector(-1 * speed, 0, 0); - } else if (direction.equalsIgnoreCase("nw")) { - return new Vector(-1 * halfSpeed, 0, -1 * halfSpeed); - } - return v; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getNextBlock(Vehicle v) { - Vector vector = v.getVelocity(); - Location location = v.getLocation(); - int x = vector.getX() < 0 ? vector.getX() == 0 ? 0 : -1 : 1; - int z = vector.getZ() < 0 ? vector.getZ() == 0 ? 0 : -1 : 1; - return location.add(x, 0, z); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleSafeTTeleporter.java b/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleSafeTTeleporter.java deleted file mode 100644 index 93367ae51..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleSafeTTeleporter.java +++ /dev/null @@ -1,340 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; -import com.onarandombox.MultiverseCore.destination.InvalidDestination; -import com.onarandombox.MultiverseCore.enums.TeleportResult; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Minecart; -import org.bukkit.entity.Player; -import org.bukkit.entity.Vehicle; -import org.bukkit.util.Vector; - -/** - * The default-implementation of {@link SafeTTeleporter}. - */ -public class SimpleSafeTTeleporter implements SafeTTeleporter { - private MultiverseCore plugin; - - public SimpleSafeTTeleporter(MultiverseCore plugin) { - this.plugin = plugin; - } - - private static final Vector DEFAULT_VECTOR = new Vector(); - private static final int DEFAULT_TOLERANCE = 6; - private static final int DEFAULT_RADIUS = 9; - - /** - * {@inheritDoc} - */ - @Override - public Location getSafeLocation(Location l) { - return this.getSafeLocation(l, DEFAULT_TOLERANCE, DEFAULT_RADIUS); - } - - /** - * {@inheritDoc} - */ - @Override - public Location getSafeLocation(Location l, int tolerance, int radius) { - // Check around the player first in a configurable radius: - // TODO: Make this configurable - Location safe = checkAboveAndBelowLocation(l, tolerance, radius); - if (safe != null) { - safe.setX(safe.getBlockX() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck - safe.setZ(safe.getBlockZ() + .5); // SUPPRESS CHECKSTYLE: MagicNumberCheck - Logging.fine("Hey! I found one: " + plugin.getLocationManipulation().strCoordsRaw(safe)); - } else { - Logging.fine("Uh oh! No safe place found!"); - } - return safe; - } - - private Location checkAboveAndBelowLocation(Location l, int tolerance, int radius) { - // Tolerance must be an even number: - if (tolerance % 2 != 0) { - tolerance += 1; - } - // We want half of it, so we can go up and down - tolerance /= 2; - Logging.finer("Given Location of: " + plugin.getLocationManipulation().strCoordsRaw(l)); - Logging.finer("Checking +-" + tolerance + " with a radius of " + radius); - - // For now this will just do a straight up block. - Location locToCheck = l.clone(); - // Check the main level - Location safe = this.checkAroundLocation(locToCheck, radius); - if (safe != null) { - return safe; - } - // We've already checked zero right above this. - int currentLevel = 1; - while (currentLevel <= tolerance) { - // Check above - locToCheck = l.clone(); - locToCheck.add(0, currentLevel, 0); - safe = this.checkAroundLocation(locToCheck, radius); - if (safe != null) { - return safe; - } - - // Check below - locToCheck = l.clone(); - locToCheck.subtract(0, currentLevel, 0); - safe = this.checkAroundLocation(locToCheck, radius); - if (safe != null) { - return safe; - } - currentLevel++; - } - - return null; - } - - /* - * For my crappy algorithm, radius MUST be odd. - */ - private Location checkAroundLocation(Location l, int diameter) { - if (diameter % 2 == 0) { - diameter += 1; - } - Location checkLoc = l.clone(); - - // Start at 3, the min diameter around a block - int loopcounter = 3; - while (loopcounter <= diameter) { - boolean foundSafeArea = checkAroundSpecificDiameter(checkLoc, loopcounter); - // If a safe area was found: - if (foundSafeArea) { - // Return the checkLoc, it is the safe location. - return checkLoc; - } - // Otherwise, let's reset our location - checkLoc = l.clone(); - // And increment the radius - loopcounter += 2; - } - return null; - } - - private boolean checkAroundSpecificDiameter(Location checkLoc, int circle) { - // Adjust the circle to get how many blocks to step out. - // A radius of 3 makes the block step 1 - // A radius of 5 makes the block step 2 - // A radius of 7 makes the block step 3 - // ... - int adjustedCircle = ((circle - 1) / 2); - checkLoc.add(adjustedCircle, 0, 0); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - // Now we go to the right that adjustedCircle many - for (int i = 0; i < adjustedCircle; i++) { - checkLoc.add(0, 0, 1); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - } - - // Then down adjustedCircle *2 - for (int i = 0; i < adjustedCircle * 2; i++) { - checkLoc.add(-1, 0, 0); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - } - - // Then left adjustedCircle *2 - for (int i = 0; i < adjustedCircle * 2; i++) { - checkLoc.add(0, 0, -1); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - } - - // Then up Then left adjustedCircle *2 - for (int i = 0; i < adjustedCircle * 2; i++) { - checkLoc.add(1, 0, 0); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - } - - // Then finish up by doing adjustedCircle - 1 - for (int i = 0; i < adjustedCircle - 1; i++) { - checkLoc.add(0, 0, 1); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(checkLoc)) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, MVDestination d) { - if (d instanceof InvalidDestination) { - Logging.finer("Entity tried to teleport to an invalid destination"); - return TeleportResult.FAIL_INVALID; - } - Player teleporteePlayer = null; - if (teleportee instanceof Player) { - teleporteePlayer = ((Player) teleportee); - } else if (teleportee.getPassenger() instanceof Player) { - teleporteePlayer = ((Player) teleportee.getPassenger()); - } - - if (teleporteePlayer == null) { - return TeleportResult.FAIL_INVALID; - } - MultiverseCore.addPlayerToTeleportQueue(teleporter.getName(), teleporteePlayer.getName()); - - Location safeLoc = d.getLocation(teleportee); - if (d.useSafeTeleporter()) { - safeLoc = this.getSafeLocation(teleportee, d); - } - - if (safeLoc != null) { - if (teleportee.teleport(safeLoc)) { - Vector v = d.getVelocity(); - if (v != null && !DEFAULT_VECTOR.equals(v)) { - Bukkit.getScheduler().runTaskLater(this.plugin, () -> { - teleportee.setVelocity(d.getVelocity()); - }, 1); - } - return TeleportResult.SUCCESS; - } - return TeleportResult.FAIL_OTHER; - } - return TeleportResult.FAIL_UNSAFE; - } - - /** - * {@inheritDoc} - */ - @Override - public TeleportResult safelyTeleport(CommandSender teleporter, Entity teleportee, Location location, boolean safely) { - if (safely) { - location = this.getSafeLocation(location); - } - - if (location != null) { - if (teleportee.teleport(location)) { - return TeleportResult.SUCCESS; - } - return TeleportResult.FAIL_OTHER; - } - return TeleportResult.FAIL_UNSAFE; - } - - /** - * {@inheritDoc} - */ - @Override - public Location getSafeLocation(Entity e, MVDestination d) { - Location l = d.getLocation(e); - if (plugin.getBlockSafety().playerCanSpawnHereSafely(l)) { - Logging.fine("The first location you gave me was safe."); - return l; - } - if (e instanceof Minecart) { - Minecart m = (Minecart) e; - if (!plugin.getBlockSafety().canSpawnCartSafely(m)) { - return null; - } - } else if (e instanceof Vehicle) { - Vehicle v = (Vehicle) e; - if (!plugin.getBlockSafety().canSpawnVehicleSafely(v)) { - return null; - } - } - Location safeLocation = this.getSafeLocation(l); - if (safeLocation != null) { - // Add offset to account for a vehicle on dry land! - if (e instanceof Minecart && !plugin.getBlockSafety().isEntitiyOnTrack(safeLocation)) { - safeLocation.setY(safeLocation.getBlockY() + .5); - Logging.finer("Player was inside a minecart. Offsetting Y location."); - } - Logging.finer("Had to look for a bit, but I found a safe place for ya!"); - return safeLocation; - } - if (e instanceof Player) { - Player p = (Player) e; - this.plugin.getMessaging().sendMessage(p, "No safe locations found!", false); - Logging.finer("No safe location found for " + p.getName()); - } else if (e.getPassenger() instanceof Player) { - Player p = (Player) e.getPassenger(); - this.plugin.getMessaging().sendMessage(p, "No safe locations found!", false); - Logging.finer("No safe location found for " + p.getName()); - } - Logging.fine("Sorry champ, you're basically trying to teleport into a minefield. I should just kill you now."); - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Location findPortalBlockNextTo(Location l) { - Block b = l.getWorld().getBlockAt(l); - Location foundLocation = null; - if (b.getType() == Material.NETHER_PORTAL) { - return l; - } - if (b.getRelative(BlockFace.NORTH).getType() == Material.NETHER_PORTAL) { - foundLocation = getCloserBlock(l, b.getRelative(BlockFace.NORTH).getLocation(), foundLocation); - } - if (b.getRelative(BlockFace.SOUTH).getType() == Material.NETHER_PORTAL) { - foundLocation = getCloserBlock(l, b.getRelative(BlockFace.SOUTH).getLocation(), foundLocation); - } - if (b.getRelative(BlockFace.EAST).getType() == Material.NETHER_PORTAL) { - foundLocation = getCloserBlock(l, b.getRelative(BlockFace.EAST).getLocation(), foundLocation); - } - if (b.getRelative(BlockFace.WEST).getType() == Material.NETHER_PORTAL) { - foundLocation = getCloserBlock(l, b.getRelative(BlockFace.WEST).getLocation(), foundLocation); - } - return foundLocation; - } - - private static Location getCloserBlock(Location source, Location blockA, Location blockB) { - // If B wasn't given, return a. - if (blockB == null) { - return blockA; - } - // Center our calculations - blockA.add(.5, 0, .5); - blockB.add(.5, 0, .5); - - // Retrieve the distance to the normalized blocks - double testA = source.distance(blockA); - double testB = source.distance(blockB); - - // Compare and return - if (testA <= testB) { - return blockA; - } - return blockB; - } - - @Override - public TeleportResult teleport(final CommandSender teleporter, final Player teleportee, final MVDestination destination) { - return this.safelyTeleport(teleporter, teleportee, destination); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleWorldPurger.java b/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleWorldPurger.java deleted file mode 100644 index bfaa4df99..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/SimpleWorldPurger.java +++ /dev/null @@ -1,199 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import com.onarandombox.MultiverseCore.api.WorldPurger; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Animals; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Ghast; -import org.bukkit.entity.Golem; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Monster; -import org.bukkit.entity.Phantom; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.Slime; -import org.bukkit.entity.Squid; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Utility class that removes animals from worlds that don't belong there. - */ -public class SimpleWorldPurger implements WorldPurger { - - private MultiverseCore plugin; - - private Class ambientClass = null; - - public SimpleWorldPurger(MultiverseCore plugin) { - this.plugin = plugin; - try { - Class entityClass = Class.forName("org.bukkit.entity.Ambient"); - if (Entity.class.isAssignableFrom(entityClass)) { - ambientClass = entityClass; - } - } catch (ClassNotFoundException ignore) { } - } - - /** - * {@inheritDoc} - */ - @Override - public void purgeWorlds(List worlds) { - if (worlds == null || worlds.isEmpty()) { - return; - } - for (MultiverseWorld world : worlds) { - this.purgeWorld(world); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void purgeWorld(MultiverseWorld world) { - if (world == null) { - return; - } - ArrayList allMobs = new ArrayList(world.getAnimalList()); - allMobs.addAll(world.getMonsterList()); - purgeWorld(world, allMobs, !world.canAnimalsSpawn(), !world.canMonstersSpawn()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean shouldWeKillThisCreature(MultiverseWorld world, Entity e) { - ArrayList allMobs = new ArrayList(world.getAnimalList()); - allMobs.addAll(world.getMonsterList()); - return this.shouldWeKillThisCreature(e, allMobs, !world.canAnimalsSpawn(), !world.canMonstersSpawn()); - } - - /** - * {@inheritDoc} - */ - @Override - public void purgeWorld(MultiverseWorld mvworld, List thingsToKill, - boolean negateAnimals, boolean negateMonsters, CommandSender sender) { - if (mvworld == null) { - return; - } - World world = mvworld.getCBWorld(); - if (world == null) { - return; - } - int projectilesKilled = 0; - int entitiesKilled = 0; - boolean specifiedAll = thingsToKill.contains("ALL"); - boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll; - boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll; - List worldEntities = world.getEntities(); - List livingEntities = new ArrayList(worldEntities.size()); - List projectiles = new ArrayList(worldEntities.size()); - for (final Entity e : worldEntities) { - if (e instanceof Projectile) { - final Projectile p = (Projectile) e; - if (p.getShooter() != null) { - projectiles.add((Projectile) e); - } - } else if (e instanceof LivingEntity) { - livingEntities.add((LivingEntity) e); - } - } - for (final LivingEntity e : livingEntities) { - if (killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters)) { - final Iterator it = projectiles.iterator(); - while (it.hasNext()) { - final Projectile p = it.next(); - if (p.getShooter().equals(e)) { - p.remove(); - it.remove(); - projectilesKilled++; - } - } - e.remove(); - entitiesKilled++; - } - } - if (sender != null) { - sender.sendMessage(entitiesKilled + " entities purged from the world '" + world.getName() + "' along with " + projectilesKilled + " projectiles that belonged to them."); - } - } - - private boolean killDecision(Entity e, List thingsToKill, boolean negateAnimals, - boolean negateMonsters, boolean specifiedAnimals, boolean specifiedMonsters) { - boolean negate = false; - boolean specified = false; - if (e instanceof Golem || e instanceof Squid || e instanceof Animals - || (ambientClass != null && ambientClass.isInstance(e))) { - // it's an animal - if (specifiedAnimals && !negateAnimals) { - Logging.finest("Removing an entity because I was told to remove all animals in world %s: %s", e.getWorld().getName(), e); - return true; - } - if (specifiedAnimals) - specified = true; - negate = negateAnimals; - } else if (e instanceof Monster || e instanceof Ghast || e instanceof Slime || e instanceof Phantom) { - // it's a monster - if (specifiedMonsters && !negateMonsters) { - Logging.finest("Removing an entity because I was told to remove all monsters in world %s: %s", e.getWorld().getName(), e); - return true; - } - if (specifiedMonsters) - specified = true; - negate = negateMonsters; - } - for (String s : thingsToKill) { - EntityType type = EntityType.fromName(s); - if (type != null && type.equals(e.getType())) { - specified = true; - if (!negate) { - Logging.finest("Removing an entity because it WAS specified and we are NOT negating in world %s: %s", e.getWorld().getName(), e); - return true; - } - break; - } - } - if (!specified && negate) { - Logging.finest("Removing an entity because it was NOT specified and we ARE negating in world %s: %s", e.getWorld().getName(), e); - return true; - } - - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean shouldWeKillThisCreature(Entity e, List thingsToKill, boolean negateAnimals, boolean negateMonsters) { - boolean specifiedAll = thingsToKill.contains("ALL"); - boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll; - boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll; - return this.killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters); - } - - /** - * {@inheritDoc} - */ - @Override - public void purgeWorld(MultiverseWorld mvworld, List thingsToKill, boolean negateAnimals, boolean negateMonsters) { - purgeWorld(mvworld, thingsToKill, negateAnimals, negateMonsters, null); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/UnsafeCallWrapper.java b/src/main/java/com/onarandombox/MultiverseCore/utils/UnsafeCallWrapper.java deleted file mode 100644 index 2dfdcaad3..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/UnsafeCallWrapper.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.onarandombox.MultiverseCore.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.api.Core; - -import java.util.concurrent.Callable; - -/** - * Wraps calls that could result in exceptions that are not Multiverse's fault. - */ -public class UnsafeCallWrapper { - private final Core core; - - public UnsafeCallWrapper(Core core) { - this.core = core; - } - - /** - * Wraps calls that could result in exceptions that are not Multiverse's fault. - * - * @param callable The potentially unsafe call. - * @param plugin The plugin that's probably the culprit. - * @param action What MV was attempting to do (error message, format string). - * @param formatArgs The formatting arguments for the error message. - * The exception that was thrown will be appended to these objects. - * @param The type of the return value. - * @return The return value or null if the call failed. - */ - public T wrap(Callable callable, String plugin, String action, Object... formatArgs) { - try { - // We're ready to catch you! JUMP! - return callable.call(); - } catch (Throwable t) { - Object[] actualFormatArgs = new Object[formatArgs.length + 1]; - System.arraycopy(formatArgs, 0, actualFormatArgs, 0, formatArgs.length); - actualFormatArgs[formatArgs.length] = t; - Logging.warning(action, actualFormatArgs); - Logging.warning("This is a bug in %s, NOT a bug in Multiverse!", plugin); - if (core.getMVConfig().getGlobalDebug() >= 1) - t.printStackTrace(); - return null; - } - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java deleted file mode 100644 index 0b2ab0701..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldManager.java +++ /dev/null @@ -1,1032 +0,0 @@ -/****************************************************************************** - * Multiverse 2 Copyright (c) the Multiverse Team 2011. * - * 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.utils; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MVWorld; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.MultiverseCoreConfiguration; -import com.onarandombox.MultiverseCore.WorldProperties; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -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; -import org.bukkit.WorldCreator; -import org.bukkit.WorldType; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.plugin.Plugin; - -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.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -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 WorldPurger worldPurger; - private final Map worlds; - private Map worldsFromTheConfig; - private FileConfiguration configWorlds = null; - private Map defaultGens; - private String firstSpawn; - - public WorldManager(MultiverseCore core) { - this.plugin = core; - this.worldsFromTheConfig = new HashMap(); - this.worlds = new ConcurrentHashMap(); - this.worldPurger = new SimpleWorldPurger(plugin); - } - - /** - * {@inheritDoc} - */ - @Override - public void getDefaultWorldGenerators() { - this.defaultGens = new HashMap(); - File[] files = this.plugin.getServerFolder().listFiles(new FilenameFilter() { - @Override - public boolean accept(File file, String s) { - return s.equalsIgnoreCase("bukkit.yml"); - } - }); - if (files != null && files.length == 1) { - FileConfiguration bukkitConfig = YamlConfiguration.loadConfiguration(files[0]); - if (bukkitConfig.isConfigurationSection("worlds")) { - Set keys = bukkitConfig.getConfigurationSection("worlds").getKeys(false); - for (String key : keys) { - defaultGens.put(key, bukkitConfig.getString("worlds." + key + ".generator", "")); - } - } - } else { - Logging.warning("Could not read 'bukkit.yml'. Any Default worldgenerators will not be loaded!"); - } - } - - /** - * {@inheritDoc} - * @deprecated Use {@link #cloneWorld(String, String)} instead. - */ - @Override - @Deprecated - public boolean cloneWorld(String oldName, String newName, String generator) { - return this.cloneWorld(oldName, newName); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean cloneWorld(String oldName, String newName) { - // Make sure we already know about the old world and that we don't - // already know about the new world. - if (!this.worldsFromTheConfig.containsKey(oldName)) { - for (Map.Entry entry : this.worldsFromTheConfig.entrySet()) { - if (oldName.equals(entry.getValue().getAlias())) { - oldName = entry.getKey(); - break; - } - } - if (!this.worldsFromTheConfig.containsKey(oldName)) { - Logging.warning("Old world '%s' does not exist", oldName); - return false; - } - } - if (this.isMVWorld(newName)) { - Logging.warning("New world '%s' already exists", newName); - return false; - } - - // Check for valid world name - if (!(WorldNameChecker.isValidWorldName(oldName) && WorldNameChecker.isValidWorldName(newName))) { - return false; - } - - final File oldWorldFile = new File(this.plugin.getServer().getWorldContainer(), oldName); - final File newWorldFile = new File(this.plugin.getServer().getWorldContainer(), newName); - final List ignoreFiles = new ArrayList<>(Arrays.asList("session.lock", "uid.dat")); - - // Make sure the new world doesn't exist outside of multiverse. - if (newWorldFile.exists()) { - Logging.warning("Folder for new world '%s' already exists", newName); - return false; - } - - // Load the old world... but just the metadata. - boolean wasJustLoaded = false; - boolean wasLoadSpawn = false; - if (this.plugin.getServer().getWorld(oldName) == null) { - wasJustLoaded = true; - WorldProperties props = this.worldsFromTheConfig.get(oldName); - wasLoadSpawn = props.isKeepingSpawnInMemory(); - if (wasLoadSpawn) { - // No chunks please. - props.setKeepSpawnInMemory(false); - } - if (!this.loadWorld(oldName)) { - return false; - } - this.plugin.getServer().getWorld(oldName).setAutoSave(false); - } - - // Grab a bit of metadata from the old world. - MultiverseWorld oldWorld = getMVWorld(oldName); - - // Don't need the loaded world anymore. - if (wasJustLoaded) { - this.unloadWorld(oldName, true); - oldWorld = null; - if (wasLoadSpawn) { - this.worldsFromTheConfig.get(oldName).setKeepSpawnInMemory(true); - } - } - - boolean wasAutoSave = false; - if (oldWorld != null && oldWorld.getCBWorld().isAutoSave()) { - wasAutoSave = true; - Logging.config("Saving world '%s'", oldName); - oldWorld.getCBWorld().setAutoSave(false); - oldWorld.getCBWorld().save(); - } - Logging.config("Copying files for world '%s'", oldName); - if (!FileUtils.copyFolder(oldWorldFile, newWorldFile, ignoreFiles)) { - Logging.warning("Failed to copy files for world '%s', see the log info", newName); - return false; - } - if (oldWorld != null && wasAutoSave) { - oldWorld.getCBWorld().setAutoSave(true); - } - - if (newWorldFile.exists()) { - Logging.fine("Succeeded at copying files"); - - // initialize new properties with old ones - WorldProperties newProps = new WorldProperties(); - newProps.copyValues(this.worldsFromTheConfig.get(oldName)); - // don't keep the alias the same -- that would be useless - newProps.setAlias(""); - // store the new properties in worlds config map - this.worldsFromTheConfig.put(newName, newProps); - - // save the worlds config to disk (worlds.yml) - if (!saveWorldsConfig()) { - Logging.severe("Failed to save worlds.yml"); - return false; - } - - // actually load the world - if (doLoad(newName)) { - Logging.fine("Succeeded at loading cloned world '" + newName + "'"); - return true; - } - Logging.severe("Failed to load the cloned world '" + newName + "'"); - return false; - } - - Logging.warning("Failed to copy files for world '%s', see the log info", newName); - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean addWorld(String name, Environment env, String seedString, WorldType type, Boolean generateStructures, - String generator) { - return this.addWorld(name, env, seedString, type, generateStructures, generator, true); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean addWorld(String name, Environment env, String seedString, WorldType type, Boolean generateStructures, - String generator, boolean useSpawnAdjust) { - if (name.equalsIgnoreCase("plugins") || name.equalsIgnoreCase("logs")) { - return false; - } - - if (!WorldNameChecker.isValidWorldName(name)) { - Logging.warning("Invalid world name '" + name + "'"); - Logging.warning("World name should not contain spaces or special characters!"); - return false; - } - - Long seed = null; - WorldCreator c = new WorldCreator(name); - if (seedString != null && seedString.length() > 0) { - try { - seed = Long.parseLong(seedString); - } catch (NumberFormatException numberformatexception) { - seed = (long) seedString.hashCode(); - } - c.seed(seed); - } - - // TODO: Use the fancy kind with the commandSender - if (generator != null && generator.length() != 0) { - c.generator(generator); - } - c.environment(env); - if (type != null) { - c.type(type); - } - if (generateStructures != null) { - c.generateStructures(generateStructures); - } - - // Important: doLoad() needs the MVWorld-object in worldsFromTheConfig - if (!worldsFromTheConfig.containsKey(name)) { - WorldProperties props = new WorldProperties(useSpawnAdjust, env); - worldsFromTheConfig.put(name, props); - } - - StringBuilder builder = new StringBuilder(); - builder.append("Loading World & Settings - '").append(name).append("'"); - builder.append(" - Env: ").append(env); - builder.append(" - Type: ").append(type); - if (seed != null) { - builder.append(" & seed: ").append(seed); - } - if (generator != null) { - builder.append(" & generator: ").append(generator); - } - Logging.info(builder.toString()); - - if (!doLoad(c, true)) { - Logging.severe("Failed to Create/Load the world '" + name + "'"); - return false; - } - - // set generator (special case because we can't read it from org.bukkit.World) - this.worlds.get(name).setGenerator(generator); - - this.saveWorldsConfig(); - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public ChunkGenerator getChunkGenerator(String generator, final String generatorID, final String worldName) { - if (generator == null) { - return null; - } - - final Plugin myPlugin = this.plugin.getServer().getPluginManager().getPlugin(generator); - if (myPlugin == null) { - return null; - } else { - return plugin.getUnsafeCallWrapper().wrap(new Callable() { - @Override - public ChunkGenerator call() throws Exception { - return myPlugin.getDefaultWorldGenerator(worldName, generatorID); - } - }, myPlugin.getName(), "Failed to get the default chunk generator: %s"); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean removeWorldFromConfig(String name) { - if (!unloadWorld(name)) { - return false; - } - if (this.worldsFromTheConfig.containsKey(name)) { - this.worldsFromTheConfig.remove(name); - Logging.info("World '%s' was removed from config.yml", name); - - this.saveWorldsConfig(); - return true; - } else { - Logging.info("World '%s' was already removed from config.yml", name); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void setFirstSpawnWorld(String world) { - if ((world == null) && (this.plugin.getServer().getWorlds().size() > 0)) { - this.firstSpawn = this.plugin.getServer().getWorlds().get(0).getName(); - } else { - this.firstSpawn = world; - } - } - - /** - * {@inheritDoc} - */ - @Override - public MultiverseWorld getFirstSpawnWorld() { - MultiverseWorld world = this.getMVWorld(this.firstSpawn); - if (world == null) { - // If the spawn world was unloaded, get the default world - Logging.warning("The world specified as the spawn world (" + this.firstSpawn + ") did not exist!!"); - try { - return this.getMVWorld(this.plugin.getServer().getWorlds().get(0)); - } catch (IndexOutOfBoundsException e) { - // This should only happen in tests. - return null; - } - } - return world; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean unloadWorld(String name) { - return this.unloadWorld(name, true); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean unloadWorld(String name, boolean unloadBukkit) { - if (this.worlds.containsKey(name)) { - this.worldsFromTheConfig.get(name).cacheVirtualProperties(); - if (unloadBukkit && this.unloadWorldFromBukkit(name, true)) { - this.worlds.remove(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 Multiverse.", name); - return true; - } else { - 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."); - Logging.warning("To let Multiverse know about it, use:"); - Logging.warning("/mv import %s %s", name, this.plugin.getServer().getWorld(name).getEnvironment().toString()); - } else if (this.worldsFromTheConfig.containsKey(name)) { - return true; // it's already unloaded - } else { - Logging.info("Multiverse does not know about '%s' and it's not loaded by Bukkit.", name); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean loadWorld(String name) { - // Check if the World is already loaded - if (this.worlds.containsKey(name)) { - return true; - } - - // Check that the world is in the config - if (worldsFromTheConfig.containsKey(name)) { - return doLoad(name); - } else { - return false; - } - } - - private void brokenWorld(String name) { - Logging.severe("The world '" + name + "' could NOT be loaded because it contains errors and is probably corrupt!"); - Logging.severe("Try using Minecraft Region Fixer to repair your world! '" + name + "'"); - Logging.severe("https://github.com/Fenixin/Minecraft-Region-Fixer"); - } - - private void nullWorld(String name) { - Logging.severe("The world '" + name + "' could NOT be loaded because the server didn't like it!"); - Logging.severe("We don't really know why this is. Contact the developer of your server software!"); - Logging.severe("Server version info: " + Bukkit.getServer().getVersion()); - } - - private boolean doLoad(String name) { - return doLoad(name, false, null); - } - - private boolean doLoad(String name, boolean ignoreExists, WorldType type) { - if (!worldsFromTheConfig.containsKey(name)) - throw new IllegalArgumentException("That world doesn't exist!"); - - final WorldProperties world = worldsFromTheConfig.get(name); - final WorldCreator creator = WorldCreator.name(name); - - creator.environment(world.getEnvironment()).seed(world.getSeed()); - if (type != null) { - creator.type(type); - } - - boolean generatorSuccess = true; - if ((world.getGenerator() != null) && (!world.getGenerator().equals("null"))) - generatorSuccess = null != plugin.getUnsafeCallWrapper().wrap(new Callable() { - @Override - public Object call() throws Exception { - creator.generator(world.getGenerator()); - return new Object(); - } - }, "the generator plugin", "Failed to set the generator for world '%s' to '%s': %s", name, world.getGenerator()); - - return generatorSuccess && doLoad(creator, ignoreExists); - } - - private boolean doLoad(WorldCreator creator, boolean ignoreExists) { - String worldName = creator.name(); - if (!worldsFromTheConfig.containsKey(worldName)) - throw new IllegalArgumentException("That world doesn't exist!"); - if (worlds.containsKey(worldName)) - throw new IllegalArgumentException("That world is already loaded!"); - - if (!ignoreExists && !new File(this.plugin.getServer().getWorldContainer(), worldName).exists() && !new File(this.plugin.getServer().getWorldContainer().getParent(), worldName).exists()) { - Logging.warning("WorldManager: Can't load this world because the folder was deleted/moved: " + worldName); - Logging.warning("Use '/mv remove' to remove it from the config!"); - return false; - } - - WorldProperties mvworld = worldsFromTheConfig.get(worldName); - World cbworld; - try { - cbworld = creator.createWorld(); - } catch (Exception e) { - e.printStackTrace(); - brokenWorld(worldName); - return false; - } - if (cbworld == null) { - nullWorld(worldName); - return false; - } - MVWorld world = new MVWorld(plugin, cbworld, mvworld); - if (MultiverseCoreConfiguration.getInstance().isAutoPurgeEnabled()) { - this.worldPurger.purgeWorld(world); - } - this.worlds.put(worldName, world); - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean deleteWorld(String name, boolean removeFromConfig, boolean deleteWorldFolder) { - if (this.hasUnloadedWorld(name, false)) { - // Attempt to load if unloaded so we can actually delete the world - if (!this.doLoad(name)) { - return false; - } - } - - World world = this.plugin.getServer().getWorld(name); - if (world == null) { - // We can only delete loaded worlds - return false; - } - - // call the event! - MVWorldDeleteEvent mvwde = new MVWorldDeleteEvent(getMVWorld(name), removeFromConfig); - this.plugin.getServer().getPluginManager().callEvent(mvwde); - if (mvwde.isCancelled()) { - Logging.fine("Tried to delete a world, but the event was cancelled!"); - return false; - } - - if (removeFromConfig) { - if (!removeWorldFromConfig(name)) { - return false; - } - } else { - if (!this.unloadWorld(name)) { - return false; - } - } - - try { - File worldFile = world.getWorldFolder(); - Logging.finer("deleteWorld(): worldFile: " + worldFile.getAbsolutePath()); - if (deleteWorldFolder ? FileUtils.deleteFolder(worldFile) : FileUtils.deleteFolderContents(worldFile)) { - Logging.info("World '%s' was DELETED.", name); - return true; - } else { - Logging.severe("World '%s' was NOT deleted.", name); - Logging.severe("Are you sure the folder %s exists?", name); - Logging.severe("Please check your file permissions on '%s'", name); - return false; - } - } catch (Throwable e) { - Logging.severe("Hrm, something didn't go as planned. Here's an exception for ya."); - Logging.severe("You can go politely explain your situation in #multiverse on esper.net"); - Logging.severe("But from here, it looks like your folder is oddly named."); - Logging.severe("This world has been removed from Multiverse-Core so your best bet is to go delete the folder by hand. Sorry."); - Logging.severe(e.getMessage()); - return false; - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean deleteWorld(String name, boolean removeFromConfig) { - return this.deleteWorld(name, removeFromConfig, true); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean deleteWorld(String name) { - return this.deleteWorld(name, true); - } - - /** - * Unload a world from Bukkit. - * - * @param name Name of the world to unload - * @param safely Perform this safely. Set to True to save world files before unloading. - * @return True if the world was unloaded, false if not. - */ - private boolean unloadWorldFromBukkit(String name, boolean safely) { - this.removePlayersFromWorld(name); - return this.plugin.getServer().unloadWorld(name, safely); - } - - /** - * {@inheritDoc} - */ - @Override - public void removePlayersFromWorld(String name) { - World w = this.plugin.getServer().getWorld(name); - if (w != null) { - World safeWorld = this.plugin.getServer().getWorlds().get(0); - List ps = w.getPlayers(); - SafeTTeleporter teleporter = this.plugin.getSafeTTeleporter(); - for (Player p : ps) { - // We're removing players forcefully from a world, they'd BETTER spawn safely. - teleporter.safelyTeleport(null, p, safeWorld.getSpawnLocation(), true); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public Collection getMVWorlds() { - return this.worlds.values(); - } - - /** - * {@inheritDoc} - */ - @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; - } - MultiverseWorld world = this.worlds.get(name); - if (world != null) { - return world; - } - return (checkAliases) ? this.getMVWorldByAlias(name) : null; - } - - /** - * {@inheritDoc} - */ - @Override - public MultiverseWorld getMVWorld(World world) { - if (world != null) { - return this.getMVWorld(world.getName(), false); - } - return null; - } - - /** - * Returns a {@link MVWorld} if it exists, and null if it does not. This will search ONLY alias. - * - * @param alias The alias of the world to get. - * @return A {@link MVWorld} or null. - */ - private MultiverseWorld getMVWorldByAlias(String alias) { - for (MultiverseWorld w : this.worlds.values()) { - if (w.getAlias().equalsIgnoreCase(alias)) { - return w; - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isMVWorld(final String 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)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isMVWorld(World world) { - return world != null && this.isMVWorld(world.getName()); - } - - /** - * This method ONLY checks the alias of each world. - * - * @param alias The alias of the world to check. - * @return True if the world exists, false if not. - */ - private boolean isMVWorldAlias(final String alias) { - for (MultiverseWorld w : this.worlds.values()) { - if (w.getAlias().equalsIgnoreCase(alias)) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void loadDefaultWorlds() { - this.ensureConfigIsPrepared(); - List myWorlds = this.plugin.getServer().getWorlds(); - for (World w : myWorlds) { - String name = w.getName(); - if (!worldsFromTheConfig.containsKey(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); - } - } - } - - private void ensureConfigIsPrepared() { - this.configWorlds.options().pathSeparator(SEPARATOR); - if (this.configWorlds.getConfigurationSection("worlds") == null) { - this.configWorlds.createSection("worlds"); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void loadWorlds(boolean forceLoad) { - // Basic Counter to count how many Worlds we are loading. - int count = 0; - this.ensureConfigIsPrepared(); - this.ensureSecondNamespaceIsPrepared(); - - // Force the worlds to be loaded, ie don't just load new worlds. - if (forceLoad) { - // Remove all world permissions. - Permission allAccess = this.plugin.getServer().getPluginManager().getPermission("multiverse.access.*"); - Permission allExempt = this.plugin.getServer().getPluginManager().getPermission("multiverse.exempt.*"); - for (MultiverseWorld w : this.worlds.values()) { - // Remove this world from the master list - if (allAccess != null) { - allAccess.getChildren().remove(w.getAccessPermission().getName()); - } - if (allExempt != null) { - allExempt.getChildren().remove(w.getAccessPermission().getName()); - } - this.plugin.getServer().getPluginManager().removePermission(w.getAccessPermission().getName()); - this.plugin.getServer().getPluginManager().removePermission(w.getExemptPermission().getName()); - // Special namespace for gamemodes - this.plugin.getServer().getPluginManager().removePermission("mv.bypass.gamemode." + w.getName()); - } - // Recalc the all permission - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allAccess); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allExempt); - this.worlds.clear(); - } - - for (Map.Entry entry : worldsFromTheConfig.entrySet()) { - if (worlds.containsKey(entry.getKey())) { - continue; - } - if (!entry.getValue().getAutoLoad()) - continue; - - if (doLoad(entry.getKey())) - count++; - } - - // Simple Output to the Console to show how many Worlds were loaded. - Logging.config("%s - World(s) loaded.", count); - this.saveWorldsConfig(); - } - - private void ensureSecondNamespaceIsPrepared() { - Permission special = this.plugin.getServer().getPluginManager().getPermission("mv.bypass.gamemode.*"); - if (special == null) { - special = new Permission("mv.bypass.gamemode.*", PermissionDefault.FALSE); - this.plugin.getServer().getPluginManager().addPermission(special); - } - } - - /** - * {@inheritDoc} - * @deprecated This is deprecated! - */ - @Override - @Deprecated - public PurgeWorlds getWorldPurger() { - return new PurgeWorlds(plugin); - } - - /** - * {@inheritDoc} - */ - @Override - public WorldPurger getTheWorldPurger() { - return worldPurger; - } - - private static final char SEPARATOR = '\uF8FF'; - - public boolean isKeepingSpawnInMemory(World world) { - WorldProperties properties = worldsFromTheConfig.get(world.getName()); - return properties == null || properties.isKeepingSpawnInMemory(); - } - - /** - * {@inheritDoc} - */ - @Override - public FileConfiguration loadWorldConfig(File file) { - this.configWorlds = YamlConfiguration.loadConfiguration(file); - this.ensureConfigIsPrepared(); - try { - this.configWorlds.save(new File(this.plugin.getDataFolder(), "worlds.yml")); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - // load world-objects - Stack worldKeys = new Stack(); - worldKeys.addAll(this.configWorlds.getConfigurationSection("worlds").getKeys(false)); - Map newWorldsFromTheConfig = new HashMap(); - while (!worldKeys.isEmpty()) { - String key = worldKeys.pop(); - String path = "worlds" + SEPARATOR + key; - Object obj = this.configWorlds.get(path); - if ((obj != null) && (obj instanceof WorldProperties)) { - String worldName = key.replaceAll(String.valueOf(SEPARATOR), "."); - WorldProperties props = (WorldProperties) obj; - if (this.worldsFromTheConfig.containsKey(worldName)) { - // Object-Recycling :D - // TODO Why is is checking worldsFromTheConfig and then getting from worlds? So confused... (DTM) - MVWorld mvWorld = (MVWorld) this.worlds.get(worldName); - if (mvWorld != null) { - mvWorld.copyValues((WorldProperties) obj); - } - } - newWorldsFromTheConfig.put(worldName, props); - } else if (this.configWorlds.isConfigurationSection(path)) { - ConfigurationSection section = this.configWorlds.getConfigurationSection(path); - Set subkeys = section.getKeys(false); - for (String subkey : subkeys) { - worldKeys.push(key + SEPARATOR + subkey); - } - } - } - this.worldsFromTheConfig = newWorldsFromTheConfig; - this.worlds.keySet().retainAll(this.worldsFromTheConfig.keySet()); - return this.configWorlds; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean saveWorldsConfig() { - try { - this.configWorlds.options().pathSeparator(SEPARATOR); - this.configWorlds.set("worlds", null); - for (Map.Entry entry : worldsFromTheConfig.entrySet()) { - this.configWorlds.set("worlds" + SEPARATOR + entry.getKey(), entry.getValue()); - } - this.configWorlds.save(new File(this.plugin.getDataFolder(), "worlds.yml")); - return true; - } catch (IOException e) { - Logging.severe("Could not save worlds.yml. Please check your settings."); - return false; - } - } - - /** - * {@inheritDoc} - */ - @Override - public MultiverseWorld getSpawnWorld() { - return this.getMVWorld(this.plugin.getServer().getWorlds().get(0)); - } - - /** - * {@inheritDoc} - */ - @Override - public List getUnloadedWorlds() { - List allNames = new ArrayList(this.worldsFromTheConfig.keySet()); - allNames.removeAll(worlds.keySet()); - return allNames; - } - - /** - * {@inheritDoc} - */ - @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) { - Logging.warning("Unable to regen a world that does not exist!"); - return false; - } - - List ps = world.getCBWorld().getPlayers(); - - // Apply new seed if needed. - if (useNewSeed) { - long theSeed; - - if (randomSeed) { - theSeed = new Random().nextLong(); - } else { - try { - theSeed = Long.parseLong(seed); - } catch (NumberFormatException e) { - theSeed = seed.hashCode(); - } - } - - world.setSeed(theSeed); - } - - WorldType type = world.getWorldType(); - - // Save current GameRules if needed. - Map, 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); - } - } - } - - // 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, 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 boolean setGameRuleValue(World world, GameRule gameRule, Object value) { - try { - return world.setGameRule(gameRule, (T) value); - } catch (Exception e) { - return false; - } - } - - /** - * Gets the {@link FileConfiguration} that this {@link WorldManager} is using. - * @return The {@link FileConfiguration} that this {@link WorldManager} is using. - */ - public FileConfiguration getConfigWorlds() { - return this.configWorlds; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasUnloadedWorld(String name, boolean includeLoaded) { - if (getMVWorld(name) != null) { - return includeLoaded; - } - for (Map.Entry entry : this.worldsFromTheConfig.entrySet()) { - if (name.equals(entry.getKey()) || name.equals(entry.getValue().getAlias())) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public Collection 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()); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsConfigurator.java b/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsConfigurator.java deleted file mode 100644 index 5b34acad2..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsConfigurator.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.onarandombox.MultiverseCore.utils.metrics; - -import java.util.Collection; -import java.util.Map; -import java.util.function.Consumer; - -import com.dumptruckman.minecraft.util.Logging; -import com.onarandombox.MultiverseCore.MultiverseCore; -import com.onarandombox.MultiverseCore.api.MVWorldManager; -import com.onarandombox.MultiverseCore.api.MultiverseWorld; -import org.apache.commons.lang.WordUtils; -import org.bstats.bukkit.Metrics; -import org.bukkit.World; - -public class MetricsConfigurator { - - private static final int PLUGIN_ID = 7765; - private static final String NO_GENERATOR_NAME = "N/A"; - - public static void configureMetrics(MultiverseCore plugin) { - MetricsConfigurator configurator = new MetricsConfigurator(plugin); - configurator.initMetrics(); - } - - private final MultiverseCore plugin; - private final Metrics metrics; - - private MetricsConfigurator(MultiverseCore plugin) { - this.plugin = plugin; - this.metrics = new Metrics(plugin, PLUGIN_ID); - } - - private MVWorldManager getWorldManager() { - return plugin.getMVWorldManager(); - } - - private Collection getMVWorlds() { - return getWorldManager().getMVWorlds(); - } - - private void initMetrics() { - try { - addCustomGeneratorsMetric(); - addEnvironmentsMetric(); - addWorldCountMetric(); - - Logging.fine("Metrics enabled."); - } catch (Exception e) { - Logging.warning("There was an issue while enabling metrics:"); - e.printStackTrace(); - } - } - - private void addCustomGeneratorsMetric() { - addAdvancedPieMetric("custom_generators", map -> { - for (MultiverseWorld w : getMVWorlds()) { - MetricsHelper.incrementCount(map, getGeneratorName(w)); - } - }); - } - - private String getGeneratorName(MultiverseWorld world) { - String gen = world.getGenerator(); - return (gen != null && !gen.equalsIgnoreCase("null")) ? gen.split(":")[0] : NO_GENERATOR_NAME; - } - - private void addEnvironmentsMetric() { - addAdvancedPieMetric("environments", map -> { - for (MultiverseWorld w : getMVWorlds()) { - MetricsHelper.incrementCount(map, titleCaseEnv(w.getEnvironment())); - } - }); - } - - private String titleCaseEnv(World.Environment env) { - String envName = env.name().replaceAll("_+", " "); - return WordUtils.capitalizeFully(envName); - } - - private void addWorldCountMetric() { - addMultiLineMetric("world_count", map -> { - int loadedWorldsCount = getMVWorlds().size(); - map.put("Loaded worlds", loadedWorldsCount); - map.put("Total number of worlds", loadedWorldsCount + getWorldManager().getUnloadedWorlds().size()); - }); - } - - private void addAdvancedPieMetric(String chartId, Consumer> metricsFunc) { - metrics.addCustomChart(MetricsHelper.createAdvancedPieChart(chartId, metricsFunc)); - } - - private void addMultiLineMetric(String chartId, Consumer> metricsFunc) { - metrics.addCustomChart(MetricsHelper.createMultiLineChart(chartId, metricsFunc)); - } -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsHelper.java b/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsHelper.java deleted file mode 100644 index 8fa8da456..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/metrics/MetricsHelper.java +++ /dev/null @@ -1,33 +0,0 @@ -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; - -enum MetricsHelper { - ; - - /** - * Adds one to the value in the given map with the given key. If the key does not exist in the map, it will be added with a value of 1. - */ - static void incrementCount(Map map, String key) { - Integer count = map.getOrDefault(key, 0); - map.put(key, count + 1); - } - - static AdvancedPie createAdvancedPieChart(String chartId, Consumer> metricsFunc) { - Map map = new HashMap<>(); - metricsFunc.accept(map); - return new AdvancedPie(chartId, () -> map); - } - - static MultiLineChart createMultiLineChart(String chartId, Consumer> metricsFunc) { - Map map = new HashMap<>(); - metricsFunc.accept(map); - return new MultiLineChart(chartId, () -> map); - } - -} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/package-info.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/package-info.java deleted file mode 100644 index a0d0b87b7..000000000 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This package contains webpaste-utilities. - */ -package com.onarandombox.MultiverseCore.utils.webpaste; diff --git a/src/main/java/org/mvplugins/multiverse/core/BstatsMetricsConfigurator.java b/src/main/java/org/mvplugins/multiverse/core/BstatsMetricsConfigurator.java new file mode 100644 index 000000000..b75d20769 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/BstatsMetricsConfigurator.java @@ -0,0 +1,106 @@ +package org.mvplugins.multiverse.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import org.apache.commons.lang.WordUtils; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.AdvancedPie; +import org.bstats.charts.MultiLineChart; +import org.bukkit.World; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +final class BstatsMetricsConfigurator { + + private static final int PLUGIN_ID = 7765; + private static final String NO_GENERATOR_NAME = "N/A"; + + private final WorldManager worldManager; + private final Metrics metrics; + + @Inject + private BstatsMetricsConfigurator(MultiverseCore plugin, WorldManager worldManager) { + this.worldManager = worldManager; + this.metrics = new Metrics(plugin, PLUGIN_ID); + } + + @PostConstruct + private void initMetrics() { + try { + addCustomGeneratorsMetric(); + addEnvironmentsMetric(); + addWorldCountMetric(); + + Logging.fine("Metrics enabled."); + } catch (Exception e) { + Logging.warning("There was an issue while enabling metrics:"); + e.printStackTrace(); + } + } + + private void addCustomGeneratorsMetric() { + addAdvancedPieMetric("custom_generators", map -> { + for (MultiverseWorld w : worldManager.getLoadedWorlds()) { + incrementCount(map, getGeneratorName(w)); + } + }); + } + + private String getGeneratorName(MultiverseWorld world) { + String gen = world.getGenerator(); + return (gen != null && !gen.equalsIgnoreCase("null")) ? gen.split(":")[0] : NO_GENERATOR_NAME; + } + + private void addEnvironmentsMetric() { + addAdvancedPieMetric("environments", map -> { + for (MultiverseWorld w : worldManager.getLoadedWorlds()) { + incrementCount(map, titleCaseEnv(w.getEnvironment())); + } + }); + } + + private String titleCaseEnv(World.Environment env) { + String envName = env.name().replaceAll("_+", " "); + return WordUtils.capitalizeFully(envName); + } + + private void addWorldCountMetric() { + addMultiLineMetric("world_count", map -> { + map.put("Loaded worlds", worldManager.getLoadedWorlds().size()); + map.put("Total number of worlds", worldManager.getWorlds().size()); + }); + } + + private void addAdvancedPieMetric(String chartId, Consumer> metricsFunc) { + metrics.addCustomChart(createAdvancedPieChart(chartId, metricsFunc)); + } + + private void addMultiLineMetric(String chartId, Consumer> metricsFunc) { + metrics.addCustomChart(createMultiLineChart(chartId, metricsFunc)); + } + + private void incrementCount(Map map, String key) { + Integer count = map.getOrDefault(key, 0); + map.put(key, count + 1); + } + + private AdvancedPie createAdvancedPieChart(String chartId, Consumer> metricsFunc) { + Map map = new HashMap<>(); + metricsFunc.accept(map); + return new AdvancedPie(chartId, () -> map); + } + + private MultiLineChart createMultiLineChart(String chartId, Consumer> metricsFunc) { + Map map = new HashMap<>(); + metricsFunc.accept(map); + return new MultiLineChart(chartId, () -> map); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/MultiverseCore.java b/src/main/java/org/mvplugins/multiverse/core/MultiverseCore.java new file mode 100644 index 000000000..38e7a44fa --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/MultiverseCore.java @@ -0,0 +1,382 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core; + +import java.util.logging.Logger; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.plugin.ServicePriority; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.commands.CoreCommand; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.listeners.CoreListener; +import org.mvplugins.multiverse.core.inject.PluginServiceLocator; +import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory; +import org.mvplugins.multiverse.core.utils.StringFormatter; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.location.NullLocation; +import org.mvplugins.multiverse.core.world.location.SpawnLocation; + +/** + * The start of the Multiverse-Core plugin + */ +@Service +public class MultiverseCore extends MultiversePlugin { + + private static final int MIN_TARGET_CORE_PROTOCOL_VERSION = 50; + private static final int CORE_PROTOCOL_VERSION = 50; + + private PluginServiceLocator serviceLocator; + + @Inject + private Provider configProvider; + @Inject + private Provider worldManagerProvider; + @Inject + private Provider anchorManagerProvider; + @Inject + private Provider commandManagerProvider; + @Inject + private Provider destinationsProviderProvider; + @Inject + private Provider metricsConfiguratorProvider; + @Inject + private Provider economistProvider; + + /** + * This is the constructor for the MultiverseCore. + */ + public MultiverseCore() { + super(); + } + + @Override + public void onLoad() { + // Setup our Logging + Logging.init(this); + + // Create our DataFolder + if (!getDataFolder().exists() && !getDataFolder().mkdirs()) { + Logging.severe("Failed to create data folder!"); + getServer().getPluginManager().disablePlugin(this); + return; + } + + // Register our config classes + ConfigurationSerialization.registerClass(NullLocation.class); + ConfigurationSerialization.registerClass(SpawnLocation.class); + + // Setup our MultiversePluginsRegistration + MultiversePluginsRegistration.get().setCore(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onEnable() { + super.onEnable(); + initializeDependencyInjection(); + + // Load our configs first as we need them for everything else. + var config = configProvider.get(); + var loadSuccess = config.load().andThenTry(config::save).isSuccess(); + if (!loadSuccess || !config.isLoaded()) { + Logging.severe("Your configs were not loaded."); + Logging.severe("Please check your configs and restart the server."); + this.getServer().getPluginManager().disablePlugin(this); + return; + } + Logging.setShowingConfig(shouldShowConfig()); + + // Initialize the worlds + worldManagerProvider.get().initAllWorlds().andThenTry(() -> { + loadEconomist(); // Setup economy here so vault is loaded + loadAnchors(); + registerEvents(); + setUpLocales(); + registerCommands(); + registerDestinations(); + setupMetrics(); + loadPlaceholderApiIntegration(); + loadApiService(); + if (!saveAllConfigs()) { + Logging.severe("Failed to save configs, things may not work as expected."); + } + logEnableMessage(); + }).onFailure(e -> { + Logging.severe("Failed to multiverse core! Disabling..."); + e.printStackTrace(); + getServer().getPluginManager().disablePlugin(this); + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDisable() { + super.onDisable(); + MultiverseCoreApi.shutdown(); + saveAllConfigs(); + shutdownDependencyInjection(); + Logging.shutdown(); + } + + private void initializeDependencyInjection() { + var serviceLocatorFactory = PluginServiceLocatorFactory.get(); + serviceLocator = serviceLocatorFactory.registerPlugin(new MultiverseCorePluginBinder(this)) + .flatMap(PluginServiceLocator::enable) + .getOrElseThrow(exception -> { + Logging.severe("Failed to initialize dependency injection!"); + getServer().getPluginManager().disablePlugin(this); + return new RuntimeException(exception); + }); + } + + private void shutdownDependencyInjection() { + if (serviceLocator != null) { + serviceLocator.disable(); + serviceLocator = null; + } + PluginServiceLocatorFactory.get().shutdown(); + } + + private boolean shouldShowConfig() { + return !configProvider.get().getSilentStart(); + } + + private void loadEconomist() { + Try.run(() -> economistProvider.get()) + .onFailure(e -> { + Logging.severe("Failed to load economy integration"); + e.printStackTrace(); + }); + } + + private void loadAnchors() { + Try.of(() -> anchorManagerProvider.get()) + .onSuccess(AnchorManager::loadAnchors) + .onFailure(e -> { + Logging.severe("Failed to load anchors"); + e.printStackTrace(); + }); + } + + /** + * Function to Register all the Events needed. + */ + private void registerEvents() { + var pluginManager = getServer().getPluginManager(); + + Try.run(() -> serviceLocator.getAllServices(CoreListener.class).forEach( + listener -> pluginManager.registerEvents(listener, this))) + .onFailure(e -> { + throw new RuntimeException("Failed to register listeners. Terminating...", e); + }); + } + + /** + * Register Multiverse-Core commands to Command Manager. + */ + private void registerCommands() { + Try.of(() -> commandManagerProvider.get()) + .andThenTry(commandManager -> serviceLocator.getAllServices(CoreCommand.class) + .forEach(commandManager::registerCommand)) + .onFailure(e -> { + Logging.severe("Failed to register commands"); + e.printStackTrace(); + }); + } + + /** + * Register locales. + */ + private void setUpLocales() { + Try.of(() -> commandManagerProvider.get()) + .mapTry(MVCommandManager::getLocales) + .andThen(pluginLocales -> { + pluginLocales.addFileResClassLoader(this); + pluginLocales.addMessageBundles("multiverse-core"); + }) + .onFailure(e -> { + Logging.severe("Failed to register locales"); + e.printStackTrace(); + }); + } + + /** + * Register all the destinations. + */ + private void registerDestinations() { + Try.of(() -> destinationsProviderProvider.get()) + .andThenTry(destinationsProvider -> { + serviceLocator.getAllServices(Destination.class) + .forEach(destinationsProvider::registerDestination); + }) + .onFailure(e -> { + Logging.severe("Failed to register destinations"); + e.printStackTrace(); + }); + } + + /** + * Setup bstats Metrics. + */ + private void setupMetrics() { + if (TestingMode.isDisabled()) { + // Load metrics + Try.of(() -> metricsConfiguratorProvider.get()) + .onFailure(e -> { + Logging.severe("Failed to setup metrics"); + e.printStackTrace(); + }); + } else { + Logging.info("Metrics are disabled in testing mode."); + } + } + + /** + * Setup placeholder api hook + */ + private void loadPlaceholderApiIntegration() { + if (configProvider.get().isRegisterPapiHook() + && getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) { + Try.run(() -> serviceLocator.getService(PlaceholderExpansionHook.class)) + .onFailure(e -> { + Logging.severe("Failed to load PlaceholderAPI integration."); + e.printStackTrace(); + }); + } + } + + /** + * Setup the api service for {@link MultiverseCoreApi} + */ + private void loadApiService() { + MultiverseCoreApi.init(serviceLocator); + Bukkit.getServicesManager().register( + MultiverseCoreApi.class, MultiverseCoreApi.get(), this, ServicePriority.Normal); + Logging.fine("api service loaded"); + } + + /** + * Save config.yml, worlds.yml, and anchors.yml. + * + * @return true if all configs were successfully saved + */ + private boolean saveAllConfigs() { + // TODO: Make this all Try + return configProvider.get().save().isSuccess() + && worldManagerProvider.get().saveWorldsConfig() + && anchorManagerProvider.get().saveAnchors(); + } + + /** + * Logs the enable message. + */ + private void logEnableMessage() { + Logging.config("Version %s (API v%s) Enabled - By %s", + this.getDescription().getVersion(), getCoreProtocolVersion(), StringFormatter.joinAnd(getDescription().getAuthors())); + + if (configProvider.get().isShowingDonateMessage()) { + Logging.config("Help dumptruckman keep this project alive. Become a patron! https://www.patreon.com/dumptruckman"); + Logging.config("One time donations are also appreciated: https://www.paypal.me/dumptruckman"); + } + } + + /** + * The current core's protocol version. + * + * @return The current core's protocol version + */ + int getCoreProtocolVersion() { + return CORE_PROTOCOL_VERSION; + } + + /** + * The minimum protocol version that submodules must target. + * + * @return The minimum protocol version + */ + int getMinTargetCoreProtocolVersion() { + return MIN_TARGET_CORE_PROTOCOL_VERSION; + } + + /** + * {@inheritDoc} + */ + @Override + public int getTargetCoreProtocolVersion() { + return CORE_PROTOCOL_VERSION; + } + + /** + * {@inheritDoc} + */ + @Override + public PluginServiceLocator getServiceLocator() { + return serviceLocator; + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public Logger getLogger() { + return Logging.getLogger(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull FileConfiguration getConfig() { + MVCoreConfig mvCoreConfig = this.configProvider.get(); + var config = mvCoreConfig.getConfig(); + if (config != null && mvCoreConfig.isLoaded()) { + return config; + } + + var loadSuccess = mvCoreConfig.load().isSuccess(); + if (!loadSuccess || !mvCoreConfig.isLoaded()) { + throw new RuntimeException("Failed to load configs"); + } + return mvCoreConfig.getConfig(); + } + + /** + * {@inheritDoc} + */ + @Override + public void reloadConfig() { + this.configProvider.get().load(); + } + + /** + * {@inheritDoc} + */ + @Override + public void saveConfig() { + this.configProvider.get().save(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/MultiverseCoreApi.java b/src/main/java/org/mvplugins/multiverse/core/MultiverseCoreApi.java new file mode 100644 index 000000000..fa7940836 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/MultiverseCoreApi.java @@ -0,0 +1,144 @@ +package org.mvplugins.multiverse.core; + +import org.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.inject.PluginServiceLocator; +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.BlockSafety; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; + +import java.util.Objects; + +/** + * Provides access to the MultiverseCore API. + */ +public class MultiverseCoreApi { + + private static MultiverseCoreApi instance; + + static void init(PluginServiceLocator serviceLocator) { + if (instance != null) { + throw new IllegalStateException("MultiverseCoreApi has already been initialized!"); + } + instance = new MultiverseCoreApi(serviceLocator); + } + + static void shutdown() { + instance = null; + } + + /** + * Gets the MultiverseCoreApi. This will throw an exception if the Multiverse-Core has not been initialized. + * + * @return The MultiverseCoreApi + */ + public static MultiverseCoreApi get() { + if (instance == null) { + throw new IllegalStateException("MultiverseCoreApi has not been initialized!"); + } + return instance; + } + + private final PluginServiceLocator serviceLocator; + + private MultiverseCoreApi(PluginServiceLocator serviceProvider) { + this.serviceLocator = serviceProvider; + } + + /** + * Gets the instance of AnchorManager. + * + * @return The AnchorManager instance + */ + public @NotNull AnchorManager getAnchorManager() { + return Objects.requireNonNull(serviceLocator.getActiveService(AnchorManager.class)); + } + + /** + * Gets the instance of BlockSafety. + * + * @return The BlockSafety instance + */ + public @NotNull BlockSafety getBlockSafety() { + return Objects.requireNonNull(serviceLocator.getActiveService(BlockSafety.class)); + } + + /** + * Gets the instance of DestinationsProvider. + * + * @return The DestinationsProvider instance + */ + public @NotNull DestinationsProvider getDestinationsProvider() { + return Objects.requireNonNull(serviceLocator.getActiveService(DestinationsProvider.class)); + } + + /** + * Gets the instance of GeneratorProvider. + * + * @return The GeneratorProvider instance + */ + public @NotNull GeneratorProvider getGeneratorProvider() { + return Objects.requireNonNull(serviceLocator.getActiveService(GeneratorProvider.class)); + } + + /** + * Gets the instance of LocationManipulation. + * + * @return The LocationManipulation instance + */ + public @NotNull LocationManipulation getLocationManipulation() { + return Objects.requireNonNull(serviceLocator.getActiveService(LocationManipulation.class)); + } + + /** + * Gets the instance of MVCoreConfig. + * + * @return The MVCoreConfig instance + */ + public @NotNull MVCoreConfig getMVCoreConfig() { + return Objects.requireNonNull(serviceLocator.getActiveService(MVCoreConfig.class)); + } + + /** + * Gets the instance of MVEconomist. + * + * @return The MVEconomist instance + */ + public @NotNull MVEconomist getMVEconomist() { + return Objects.requireNonNull(serviceLocator.getActiveService(MVEconomist.class)); + } + + /** + * Gets the instance of SafetyTeleporter. + * + * @return The SafetyTeleporter instance + */ + public @NotNull AsyncSafetyTeleporter getSafetyTeleporter() { + return Objects.requireNonNull(serviceLocator.getActiveService(AsyncSafetyTeleporter.class)); + } + + /** + * Gets the instance of WorldManager. + * + * @return The WorldManager instance + */ + public @NotNull WorldManager getWorldManager() { + return Objects.requireNonNull(serviceLocator.getActiveService(WorldManager.class)); + } + + /** + * Gets the instance of Multiverse-Core's PluginServiceLocator. + *
+ * You can use this to hook into the hk2 dependency injection system used by Multiverse-Core. + * + * @return The Multiverse-Core's PluginServiceLocator + */ + public PluginServiceLocator getServiceLocator() { + return serviceLocator; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/MultiverseCorePluginBinder.java b/src/main/java/org/mvplugins/multiverse/core/MultiverseCorePluginBinder.java new file mode 100644 index 000000000..bf242d5f9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/MultiverseCorePluginBinder.java @@ -0,0 +1,19 @@ +package org.mvplugins.multiverse.core; + +import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.inject.binder.JavaPluginBinder; + +final class MultiverseCorePluginBinder extends JavaPluginBinder { + + MultiverseCorePluginBinder(@NotNull MultiverseCore plugin) { + super(plugin); + } + + @Override + protected ScopedBindingBuilder bindPluginClass( + ScopedBindingBuilder bindingBuilder) { + return super.bindPluginClass(bindingBuilder).to(MultiverseCore.class); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/MultiversePlugin.java b/src/main/java/org/mvplugins/multiverse/core/MultiversePlugin.java new file mode 100644 index 000000000..7071e6a58 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/MultiversePlugin.java @@ -0,0 +1,41 @@ +package org.mvplugins.multiverse.core; + +import org.bukkit.plugin.java.JavaPlugin; +import org.mvplugins.multiverse.core.inject.PluginServiceLocator; + +/** + * Common plugin class for all Multiverse plugins. + */ +public abstract class MultiversePlugin extends JavaPlugin { + /** + * {@inheritDoc} + */ + @Override + public void onEnable() { + super.onEnable(); + MultiversePluginsRegistration.get().registerMultiversePlugin(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDisable() { + super.onDisable(); + MultiversePluginsRegistration.get().unregisterMultiversePlugin(this); + } + + /** + * The minimum protocol version that this plugin is compatible with Multiverse-Core. + * + * @return The Integer protocol version. + */ + public abstract int getTargetCoreProtocolVersion(); + + /** + * Gets the {@link PluginServiceLocator} for this plugin. + * + * @return The {@link PluginServiceLocator} + */ + public abstract PluginServiceLocator getServiceLocator(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/MultiversePluginsRegistration.java b/src/main/java/org/mvplugins/multiverse/core/MultiversePluginsRegistration.java new file mode 100644 index 000000000..227afbfbf --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/MultiversePluginsRegistration.java @@ -0,0 +1,89 @@ +package org.mvplugins.multiverse.core; + +import com.dumptruckman.minecraft.util.Logging; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Handle loading sub-modules of the Multiverse-Core and checking for compatibility with protocol versions. + */ +public class MultiversePluginsRegistration { + + private static MultiversePluginsRegistration instance; + + public static MultiversePluginsRegistration get() { + if (instance == null) { + instance = new MultiversePluginsRegistration(); + } + return instance; + } + + private MultiverseCore core; + private final List registeredPlugins; + private int pluginCount = 0; + + public MultiversePluginsRegistration() { + registeredPlugins = new ArrayList<>(); + } + + void setCore(MultiverseCore core) { + this.core = core; + } + + void deferenceCore() { + core = null; + } + + void registerMultiversePlugin(MultiversePlugin plugin) { + if (core == null) { + throw new IllegalStateException("MultiverseCore has not been initialized!"); + } + if (core.getCoreProtocolVersion() < plugin.getTargetCoreProtocolVersion()) { + Logging.severe("Your Multiverse-Core is OUT OF DATE!"); + Logging.severe("This version of %s requires Protocol Level: %d", plugin.getDescription().getName(), plugin.getTargetCoreProtocolVersion()); + Logging.severe("Your of Core Protocol Level is: %s", core.getTargetCoreProtocolVersion()); + Logging.severe("Grab an updated copy at: "); + Logging.severe(core.getDescription().getWebsite()); + Logging.severe("Disabling!"); + core.getServer().getPluginManager().disablePlugin(plugin); + return; + } + if (core.getMinTargetCoreProtocolVersion() > plugin.getTargetCoreProtocolVersion()) { + Logging.severe("Your %s is OUT OF DATE!", plugin.getDescription().getName()); + Logging.severe("This version of Multiverse-Core requires AT LEAST Protocol Level: " + core.getCoreProtocolVersion()); + Logging.severe("Your of %s Protocol Level is: %s", plugin.getDescription().getName(), plugin.getTargetCoreProtocolVersion()); + Logging.severe("Grab an updated copy at: "); + Logging.severe(plugin.getDescription().getWebsite()); + Logging.severe("Disabling!"); + core.getServer().getPluginManager().disablePlugin(plugin); + return; + } + registeredPlugins.add(plugin.getDescription().getName()); + pluginCount++; + } + + void unregisterMultiversePlugin(MultiversePlugin plugin) { + registeredPlugins.remove(plugin.getDescription().getName()); + pluginCount--; + } + + /** + * Gets the list of multiverse plugins modules running on this server. + * + * @return The list of multiverse plugins. + */ + public List getRegisteredPlugins() { + return Collections.unmodifiableList(registeredPlugins); + } + + /** + * Get the number of multiverse plugins running on this server. + * + * @return The number of multiverse plugins. + */ + public int getPluginCount() { + return pluginCount; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/PlaceholderExpansionHook.java b/src/main/java/org/mvplugins/multiverse/core/PlaceholderExpansionHook.java new file mode 100644 index 000000000..c435b1003 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/PlaceholderExpansionHook.java @@ -0,0 +1,163 @@ +package org.mvplugins.multiverse.core; + +import io.vavr.control.Option; +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.utils.StringFormatter; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +final class PlaceholderExpansionHook extends PlaceholderExpansion { + + private final MultiverseCore plugin; + private final WorldManager worldManager; + private final MVEconomist economist; + + @Inject + public PlaceholderExpansionHook(MultiverseCore plugin, WorldManager worldManager, MVEconomist economist) { + this.plugin = plugin; + this.worldManager = worldManager; + this.economist = economist; + } + + @PostConstruct + @Override + public boolean register() { + return super.register(); + } + + @Override + public @NotNull String getIdentifier() { + return "multiverse-core"; + } + + @Override + public @NotNull String getAuthor() { + return StringFormatter.joinAnd(plugin.getDescription().getAuthors()); + } + + @Override + public @NotNull String getVersion() { + return plugin.getDescription().getVersion(); + } + + @Override + public boolean persist() { + return true; + } + + /** + * Placeholder implementation, format: %multiverse-core_<placeholder>_[world]% world is optional. + * + * @param offlinePlayer Player to get the placeholder for + * @param params Placeholder to get + * @return Placeholder value + */ + @Override + public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { + // Split string in to an Array with underscores + String[] paramsArray = params.split("_", 2); + + // No placeholder defined + if (paramsArray.length < 1) { + warning("No placeholder defined"); + return null; + } + + final var placeholder = paramsArray[0]; + Option targetWorld; + + // If no world is defined, use the player's world + if (paramsArray.length == 1) { + if (!offlinePlayer.isOnline()) { + return null; + } + targetWorld = worldManager.getLoadedWorld(((Player)offlinePlayer).getWorld()); + } else { + targetWorld = worldManager.getLoadedWorld(paramsArray[1]); + } + + // Fail if world is null + return targetWorld.map(world -> getWorldPlaceHolderValue(placeholder, world)).getOrNull(); + } + + private @Nullable String getWorldPlaceHolderValue(@NotNull String placeholder, @NotNull LoadedMultiverseWorld world) { + // Switch to find what specific placeholder we want + switch (placeholder.toLowerCase()) { + case "alias" -> { + return world.getAlias(); + } + case "animalspawn" -> { + return String.valueOf(world.getSpawningAnimals()); + } + case "autoheal" -> { + return String.valueOf(world.getAutoHeal()); + } + case "blacklist" -> { + return String.join(", ", world.getWorldBlacklist()); + } + case "currency" -> { + return String.valueOf(world.getCurrency()); + } + case "difficulty" -> { + return world.getDifficulty().toString(); + } + case "entryfee" -> { + return economist.formatPrice(world.getPrice(), world.getCurrency()); + } + case "environment" -> { + return world.getEnvironment().toString().toLowerCase(); + } + case "flight" -> { + return String.valueOf(world.getAllowFlight()); + } + case "gamemode" -> { + return world.getGameMode().toString().toLowerCase(); + } + case "generator" -> { + return world.getGenerator(); + } + case "hunger" -> { + return String.valueOf(world.getHunger()); + } + case "monstersspawn" -> { + return String.valueOf(world.getSpawningMonsters()); + } + case "name" -> { + return world.getName(); + } + case "playerlimit" -> { + return String.valueOf(world.getPlayerLimit()); + } + case "price" -> { + return String.valueOf(world.getPrice()); + } + case "pvp" -> { + return String.valueOf(world.getPvp()); + } + case "seed" -> { + return String.valueOf(world.getSeed()); + } + case "type" -> { + return world.getBukkitWorld().map(World::getWorldType).map(Enum::name).getOrElse("null"); + } + case "weather" -> { + return String.valueOf(world.getAllowWeather()); + } + default -> { + warning("Unknown placeholder: " + placeholder); + return null; + } + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/TestingMode.java b/src/main/java/org/mvplugins/multiverse/core/TestingMode.java similarity index 60% rename from src/main/java/com/onarandombox/MultiverseCore/utils/TestingMode.java rename to src/main/java/org/mvplugins/multiverse/core/TestingMode.java index 81bb0f6e3..8f2eb3a67 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/TestingMode.java +++ b/src/main/java/org/mvplugins/multiverse/core/TestingMode.java @@ -1,18 +1,22 @@ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core; /** * A utility class that enables automated tests to flag Multiverse for testing. This allows Multiverse to not perform * certain behaviors such as enabled stats uploads. */ -public class TestingMode { +final class TestingMode { private static boolean enabled = false; - public static void enable() { + private TestingMode() { + // No instantiation + } + + static void enable() { enabled = true; } - public static boolean isDisabled() { + static boolean isDisabled() { return !enabled; } } diff --git a/src/main/java/org/mvplugins/multiverse/core/anchor/AnchorManager.java b/src/main/java/org/mvplugins/multiverse/core/anchor/AnchorManager.java new file mode 100644 index 000000000..d0ad17bbb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/anchor/AnchorManager.java @@ -0,0 +1,218 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.anchor; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; + +/** + * Manages anchors. + */ +@Service +public final class AnchorManager { + + private static final String ANCHORS_FILE = "anchors.yml"; + private static final String ANCHORS_CONFIG_SECTION = "anchors"; + + private Map anchors; + private FileConfiguration anchorConfig; + + private final Plugin plugin; + private final LocationManipulation locationManipulation; + private final MVCoreConfig config; + + @Inject + AnchorManager( + MultiverseCore plugin, + LocationManipulation locationManipulation, + MVCoreConfig config) { + this.plugin = plugin; + this.locationManipulation = locationManipulation; + this.config = config; + + anchors = new HashMap<>(); + } + + /** + * Loads all anchors. + */ + public void loadAnchors() { + anchors = new HashMap<>(); + anchorConfig = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), ANCHORS_FILE)); + var anchorsSection = getAnchorsConfigSection(); + Set anchorKeys = anchorsSection.getKeys(false); + for (String key : anchorKeys) { + //world:x,y,z:pitch:yaw + Location anchorLocation = locationManipulation.stringToLocation(anchorsSection.getString(key, "")); + if (anchorLocation != null) { + Logging.config("Loading anchor: '%s'...", key); + anchors.put(key, anchorLocation); + } else { + Logging.warning("The location for anchor '%s' is INVALID.", key); + } + + } + } + + private ConfigurationSection getAnchorsConfigSection() { + var anchorsConfigSection = anchorConfig.getConfigurationSection(ANCHORS_CONFIG_SECTION); + if (anchorsConfigSection == null) { + anchorsConfigSection = anchorConfig.createSection(ANCHORS_CONFIG_SECTION); + } + return anchorsConfigSection; + } + + /** + * Saves all anchors. + * + * @return True if all anchors were successfully saved. + */ + public boolean saveAnchors() { + try { + anchorConfig.save(new File(plugin.getDataFolder(), ANCHORS_FILE)); + return true; + } catch (IOException e) { + Logging.severe("Failed to save anchors.yml. Please check your file permissions."); + return false; + } + } + + /** + * Gets the {@link Location} associated with an anchor. + * + * @param anchor The name of the anchor. + * @return The {@link Location}. + */ + public Location getAnchorLocation(String anchor) { + if (anchors.containsKey(anchor)) { + return anchors.get(anchor); + } + return null; + } + + /** + * Saves an anchor. + * + * @param anchor The name of the anchor. + * @param location The location of the anchor as string. + * @return True if the anchor was successfully saved. + */ + public boolean saveAnchorLocation(String anchor, String location) { + Location parsed = locationManipulation.stringToLocation(location); + return saveAnchorLocation(anchor, parsed); + } + + /** + * Saves an anchor. + * + * @param anchor The name of the anchor. + * @param l The {@link Location} of the anchor. + * @return True if the anchor was successfully saved. + */ + public boolean saveAnchorLocation(String anchor, Location l) { + if (l == null) { + return false; + } + getAnchorsConfigSection().set(anchor, locationManipulation.locationToString(l)); + anchors.put(anchor, l); + return saveAnchors(); + } + + /** + * Gets all anchors. + * + * @return An unmodifiable {@link Set} containing all anchors. + */ + public Set getAllAnchors() { + return Collections.unmodifiableSet(anchors.keySet()); + } + + /** + * Gets all anchors that the specified {@link Player} can access. + * + * @param player The {@link Player}. + * @return An unmodifiable {@link Set} containing all anchors the specified {@link Player} can access. + */ + public Set getAnchors(@Nullable Player player) { + if (player == null) { + return anchors.keySet(); + } else { + return getAnchorsForPlayer(player); + } + } + + private Set getAnchorsForPlayer(@NotNull Player player) { + return anchors.entrySet().stream() + .filter(entry -> shouldIncludeAnchorForPlayer(entry.getKey(), entry.getValue(), player)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + + private boolean shouldIncludeAnchorForPlayer(String anchor, Location location, Player player) { + var world = getLocationWorld(location); + return world != null && playerCanAccess(player, world, anchor); + } + + private @Nullable World getLocationWorld(@Nullable Location location) { + if (location == null) { + return null; + } + return location.getWorld(); + } + + private boolean playerCanAccess(Player player, World world, String anchor) { + String worldPerm = "multiverse.access." + world.getName(); + if (playerCanAccess(player, worldPerm)) { + return true; + } + Logging.finer(String.format("Not adding anchor %s to the list, user %s doesn't have the %s permission " + + "and 'enforceaccess' is enabled!", anchor, player.getName(), worldPerm)); + return false; + } + + private boolean playerCanAccess(Player player, String worldPerm) { + return !config.getEnforceAccess() || player.hasPermission(worldPerm); + } + + /** + * Deletes the specified anchor. + * + * @param s The name of the anchor. + * @return True if the anchor was successfully deleted. + */ + public boolean deleteAnchor(String s) { + if (anchors.containsKey(s)) { + anchors.remove(s); + getAnchorsConfigSection().set(s, null); + return saveAnchors(); + } + return false; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/anchor/package-info.java b/src/main/java/org/mvplugins/multiverse/core/anchor/package-info.java new file mode 100644 index 000000000..178ece384 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/anchor/package-info.java @@ -0,0 +1,4 @@ +/** + * this package contains the classes that are used for the anchor system. + */ +package org.mvplugins.multiverse.core.anchor; diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/AnchorDeleteCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorDeleteCommand.java new file mode 100644 index 000000000..cc81b982c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorDeleteCommand.java @@ -0,0 +1,46 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; + +@Service +@CommandAlias("mv") +final class AnchorDeleteCommand extends CoreCommand { + + private final AnchorManager anchorManager; + + @Inject + AnchorDeleteCommand(@NotNull MVCommandManager commandManager, @NotNull AnchorManager anchorManager) { + super(commandManager); + this.anchorManager = anchorManager; + } + + @Subcommand("anchor delete") + @CommandPermission("multiverse.core.anchor.delete") + @CommandCompletion("") + @Syntax("") + @Description("") + void onAnchorDeleteCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("") + String anchorName) { + if (anchorManager.deleteAnchor(anchorName)) { + issuer.sendMessage("&aAnchor &f" + anchorName + "&a deleted."); + } else { + issuer.sendMessage("&cFailed to delete anchor."); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/AnchorListCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorListCommand.java new file mode 100644 index 000000000..c1b997a0c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorListCommand.java @@ -0,0 +1,82 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.List; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.ListContentProvider; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; + +@Service +@CommandAlias("mv") +final class AnchorListCommand extends CoreCommand { + + private final AnchorManager anchorManager; + private final LocationManipulation locationManipulation; + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + @Inject + AnchorListCommand( + @NotNull MVCommandManager commandManager, + @NotNull AnchorManager anchorManager, + @NotNull LocationManipulation locationManipulation) { + super(commandManager); + this.anchorManager = anchorManager; + this.locationManipulation = locationManipulation; + } + + @Subcommand("anchor list") + @CommandPermission("multiverse.core.anchor.list") + @CommandCompletion("@flags:groupName=mvanchorlistcommand") + @Syntax("[--page ] [--filter ]") + @Description("") + void onAnchorListCommand( + MVCommandIssuer issuer, + + @Optional + @Syntax("[--page ] [--filter ]") + @Description("") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + ContentDisplay.create() + .addContent(ListContentProvider.forContent(getAnchors(issuer.getPlayer()))) + .withSendHandler(PagedSendHandler.create() + .withHeader("&3==== [ Multiverse Anchors ] ====") + .doPagination(true) + .withTargetPage(parsedFlags.flagValue(pageFlag, 1)) + .withFilter(parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()))) + .send(issuer); + } + + private List getAnchors(Player player) { + return anchorManager.getAnchors(player).stream().map(anchorName -> { + Location anchorLocation = anchorManager.getAnchorLocation(anchorName); + return "&a" + anchorName + "&7 - &f" + locationManipulation.locationToString(anchorLocation); + }).toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/AnchorSetCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorSetCommand.java new file mode 100644 index 000000000..8cbe11f77 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/AnchorSetCommand.java @@ -0,0 +1,93 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; + +@Service +@CommandAlias("mv") +final class AnchorSetCommand extends CoreCommand { + + private final AnchorManager anchorManager; + private final LocationManipulation locationManipulation; + + @Inject + AnchorSetCommand( + @NotNull MVCommandManager commandManager, + @NotNull AnchorManager anchorManager, + @NotNull LocationManipulation locationManipulation) { + super(commandManager); + this.anchorManager = anchorManager; + this.locationManipulation = locationManipulation; + } + + @Subcommand("anchor set") + @CommandPermission("multiverse.core.anchor.create") + @CommandCompletion("@anchornames") + @Syntax(" [location]") + @Description("") + void onAnchorSetCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerOnly") + Player player, + + @Single + @Syntax("") + @Description("") + //TODO: Check if anchor name is valid or exists already. + String anchorName) { + Location anchorLocation = player.getLocation(); + if (anchorManager.saveAnchorLocation(anchorName, anchorLocation)) { + sendAnchorSetSuccessMessage(issuer, anchorName, locationManipulation.locationToString(anchorLocation)); + } else { + sendAnchorSetFailedMessage(issuer, anchorName); + } + } + + @Subcommand("anchor set") + @CommandPermission("multiverse.core.anchor.create") + @CommandCompletion("@anchornames @locations") + @Syntax(" [location]") + @Description("") + void onAnchorSetCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("") + String anchorName, + + @Single + @Syntax("[location]") + @Description("") + String locationString) { + if (anchorManager.saveAnchorLocation(anchorName, locationString)) { + sendAnchorSetSuccessMessage(issuer, anchorName, locationString); + } else { + sendAnchorSetFailedMessage(issuer, anchorName); + } + } + + private void sendAnchorSetSuccessMessage(MVCommandIssuer issuer, String anchorName, String locationString) { + issuer.sendMessage("&aAnchor &f" + anchorName + "&a set to &f" + locationString); + } + + private void sendAnchorSetFailedMessage(MVCommandIssuer issuer, String anchorName) { + issuer.sendMessage("&cFailed to set anchor &f" + anchorName + "."); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CheckCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CheckCommand.java new file mode 100644 index 000000000..619c129c5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CheckCommand.java @@ -0,0 +1,70 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +@Service +@CommandAlias("mv") +final class CheckCommand extends CoreCommand { + + private final CorePermissionsChecker corePermissionsChecker; + private final LocationManipulation locationManipulation; + + @Inject + CheckCommand(@NotNull MVCommandManager commandManager, + @NotNull CorePermissionsChecker corePermissionsChecker, + @NotNull LocationManipulation locationManipulation) { + super(commandManager); + this.corePermissionsChecker = corePermissionsChecker; + this.locationManipulation = locationManipulation; + } + + @CommandAlias("mvcheck") + @Subcommand("check") + @CommandPermission("multiverse.core.check") + @CommandCompletion("@players @destinations") + @Syntax(" ") + @Description("{@@mv-core.check.description}") + void onCheckCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.check.player.description}") + Player player, + + @Syntax("") + @Description("{@@mv-core.check.destination.description}") + DestinationInstance destination) { + issuer.sendInfo(this.corePermissionsChecker.checkTeleportPermissions(player, player, destination) + ? MVCorei18n.CHECK_HASPERMISSION + : MVCorei18n.CHECK_NOPERMISSION, + Replace.PLAYER.with(player.getName()), + Replace.DESTINATION.with(destination)); + issuer.sendInfo(MVCorei18n.CHECK_LOCATION, + replace("{location}").with(destination.getLocation(player) + .map(locationManipulation::locationToString) + .map(Message::of) + .getOrElse(() -> Message.of(MVCorei18n.GENERIC_NULL, "Null!")))); + + // TODO: Show permission required for this particular destination + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CloneCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CloneCommand.java new file mode 100644 index 000000000..cd898b8c4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CloneCommand.java @@ -0,0 +1,90 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.options.CloneWorldOptions; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +@Service +@CommandAlias("mv") +final class CloneCommand extends CoreCommand { + + private final WorldManager worldManager; + + private final CommandFlag resetWorldConfigFlag = flag(CommandFlag.builder("--reset-world-config") + .addAlias("-wc") + .build()); + + private final CommandFlag resetGamerulesFlag = flag(CommandFlag.builder("--reset-gamerules") + .addAlias("-gm") + .build()); + + private final CommandFlag resetWorldBorderFlag = flag(CommandFlag.builder("--reset-world-border") + .addAlias("-wb") + .build()); + + @Inject + CloneCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) { + super(commandManager); + this.worldManager = worldManager; + } + + @CommandAlias("mvcl|mvclone") + @Subcommand("clone") + @CommandPermission("multiverse.core.clone") + @CommandCompletion("@mvworlds:scope=loaded @empty @flags:groupName=mvclonecommand") + @Syntax(" ") + @Description("{@@mv-core.clone.description}") + void onCloneCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.clone.world.description}") + LoadedMultiverseWorld world, + + @Syntax("") + @Description("{@@mv-core.clone.newWorld.description}") + String newWorldName, + + @Optional + @Syntax(/* TODO */ "") + @Description("{@@mv-core.regen.other.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + issuer.sendInfo(MVCorei18n.CLONE_CLONING, + Replace.WORLD.with(world.getName()), + replace("{newworld}").with(newWorldName)); + CloneWorldOptions cloneWorldOptions = CloneWorldOptions.fromTo(world, newWorldName) + .keepWorldConfig(!parsedFlags.hasFlag(resetWorldConfigFlag)) + .keepGameRule(!parsedFlags.hasFlag(resetGamerulesFlag)) + .keepWorldBorder(!parsedFlags.hasFlag(resetWorldBorderFlag)); + worldManager.cloneWorld(cloneWorldOptions) + .onSuccess(newWorld -> { + Logging.fine("World clone success: " + newWorld); + issuer.sendInfo(MVCorei18n.CLONE_SUCCESS, Replace.WORLD.with(newWorld.getName())); + }).onFailure(failure -> { + Logging.fine("World clone failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ConfigCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ConfigCommand.java new file mode 100644 index 000000000..8f91b47fa --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ConfigCommand.java @@ -0,0 +1,71 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.exceptions.MultiverseException; + +@Service +@CommandAlias("mv") +final class ConfigCommand extends CoreCommand { + + private final MVCoreConfig config; + + @Inject + ConfigCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) { + super(commandManager); + this.config = config; + } + + @CommandAlias("mvconfig|mvconf") + @Subcommand("config") + @CommandPermission("multiverse.core.config") + @CommandCompletion("@mvconfigs @mvconfigvalues") + @Syntax(" [value]") + @Description("Show or set a config value.") + void onConfigCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("The name of the config to set or show.") + String name, + + @Optional + @Syntax("[value]") + @Description("The value to set the config to. If not specified, the current value will be shown.") + String value) { + if (value == null) { + showConfigValue(issuer, name); + return; + } + updateConfigValue(issuer, name, value); + } + + private void showConfigValue(MVCommandIssuer issuer, String name) { + config.getStringPropertyHandle().getProperty(name) + .onSuccess(value -> issuer.sendMessage(name + "is currently set to " + value)) + .onFailure(e -> issuer.sendMessage(e.getMessage())); + } + + private void updateConfigValue(MVCommandIssuer issuer, String name, String value) { + config.getStringPropertyHandle().setPropertyString(name, value) + .onSuccess(ignore -> { + config.save(); + issuer.sendMessage("Successfully set " + name + " to " + value); + }) + .onFailure(ignore -> issuer.sendMessage("Unable to set " + name + " to " + value + ".")) + .onFailure(MultiverseException.class, e -> Option.of(e.getMVMessage()).peek(issuer::sendError)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ConfirmCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ConfirmCommand.java new file mode 100644 index 000000000..31ce3f769 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ConfirmCommand.java @@ -0,0 +1,37 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Default; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; + +@Service +@CommandAlias("mv") +final class ConfirmCommand extends CoreCommand { + + @Inject + ConfirmCommand(@NotNull MVCommandManager commandManager) { + super(commandManager); + } + + @CommandAlias("mvconfirm") + @Subcommand("confirm") + @CommandPermission("multiverse.core.confirm") + @Syntax("[otp]") + @Description("{@@mv-core.confirm.description}") + void onConfirmCommand( + @NotNull MVCommandIssuer issuer, + + @Default("0") + int otp) { + this.commandManager.getCommandQueueManager().runQueuedCommand(issuer, otp); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CoordinatesCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CoordinatesCommand.java new file mode 100644 index 000000000..a7c442f10 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CoordinatesCommand.java @@ -0,0 +1,58 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Subcommand; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +@Service +@CommandAlias("mv") +final class CoordinatesCommand extends CoreCommand { + + private final LocationManipulation locationManipulation; + + @Inject + CoordinatesCommand( + @NotNull MVCommandManager commandManager, + @NotNull LocationManipulation locationManipulation) { + super(commandManager); + this.locationManipulation = locationManipulation; + } + + @CommandAlias("mvcoord|mvco") + @Subcommand("coordinates|coords|coord|co") + @CommandPermission("multiverse.core.coord") + @Description("{@@mv-core.coordinates.description}") + void onCoordinatesCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerOnly") + Player player, + + @Flags("resolve=issuerOnly") + MultiverseWorld world) { + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_TITLE); + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLD, Replace.WORLD.with(world.getName())); + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_ALIAS, replace("{alias}").with(world.getAlias())); + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLDSCALE, + replace("{scale}").with(String.valueOf(world.getScale()))); + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_COORDINATES, + replace("{coordinates}").with(locationManipulation.strCoords(player.getLocation()))); + issuer.sendInfo(MVCorei18n.COORDINATES_INFO_DIRECTION, + replace("{direction}").with(locationManipulation.getDirection(player.getLocation()))); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CoreCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CoreCommand.java new file mode 100644 index 000000000..91300e975 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CoreCommand.java @@ -0,0 +1,17 @@ +package org.mvplugins.multiverse.core.commands; + +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Contract; + +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.MultiverseCommand; + +/** + * Represents a command that is part of the Multiverse-Core plugin. + */ +@Contract +public abstract class CoreCommand extends MultiverseCommand { + protected CoreCommand(@NotNull MVCommandManager commandManager) { + super(commandManager, "mv"); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java new file mode 100644 index 000000000..4e08152e7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/CreateCommand.java @@ -0,0 +1,164 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Collections; + +import co.aikar.commands.ACFUtil; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import com.google.common.collect.Lists; +import jakarta.inject.Inject; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.result.Attempt.Failure; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; +import org.mvplugins.multiverse.core.world.options.CreateWorldOptions; +import org.mvplugins.multiverse.core.world.reasons.CreateFailureReason; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +@Service +@CommandAlias("mv") +final class CreateCommand extends CoreCommand { + + private final WorldManager worldManager; + private GeneratorProvider generatorProvider; + + private final CommandValueFlag seedFlag = flag(CommandValueFlag.builder("--seed", String.class) + .addAlias("-s") + .completion(input -> Collections.singleton(String.valueOf(ACFUtil.RANDOM.nextLong()))) + .build()); + + private final CommandValueFlag generatorFlag = flag(CommandValueFlag + .builder("--generator", String.class) + .addAlias("-g") + .completion(input -> generatorProvider.suggestGeneratorString(input)) + .build()); + + private final CommandValueFlag worldTypeFlag = flag(CommandValueFlag + .enumBuilder("--world-type", WorldType.class) + .addAlias("-t") + .build()); + + private final CommandFlag noAdjustSpawnFlag = flag(CommandFlag.builder("--no-adjust-spawn") + .addAlias("-n") + .build()); + + private final CommandFlag noStructuresFlag = flag(CommandFlag.builder("--no-structures") + .addAlias("-a") + .build()); + + private final CommandValueFlag biomeFlag = flag(CommandValueFlag.builder("--biome", Biome.class) + .addAlias("-b") + .completion(input -> Lists.newArrayList(Registry.BIOME).stream() + .filter(biome -> biome != Biome.CUSTOM) + .map(biome -> biome.getKey().getKey()) + .toList()) + .context(biomeStr -> Registry.BIOME.get(NamespacedKey.minecraft(biomeStr))) + .build()); + + @Inject + CreateCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull GeneratorProvider generatorProvider) { + super(commandManager); + this.worldManager = worldManager; + this.generatorProvider = generatorProvider; + } + + @CommandAlias("mvcreate|mvc") + @Subcommand("create") + @CommandPermission("multiverse.core.create") + @CommandCompletion("@empty @environments @flags:groupName=mvcreatecommand") + @Syntax(" [--seed --generator --world-type --adjust-spawn " + + "--no-structures --biome ]") + @Description("{@@mv-core.create.description}") + void onCreateCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.create.name.description}") + String worldName, + + @Syntax("") + @Description("{@@mv-core.create.environment.description}") + World.Environment environment, + + @Optional + @Syntax("[--seed --generator --world-type --adjust-spawn " + + "--no-structures --biome ]") + @Description("{@@mv-core.create.flags.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + messageWorldDetails(issuer, worldName, environment, parsedFlags); + + issuer.sendInfo(MVCorei18n.CREATE_LOADING); + + worldManager.createWorld(CreateWorldOptions.worldName(worldName) + .biome(parsedFlags.flagValue(biomeFlag, Biome.CUSTOM)) + .environment(environment) + .seed(parsedFlags.flagValue(seedFlag)) + .worldType(parsedFlags.flagValue(worldTypeFlag, WorldType.NORMAL)) + .useSpawnAdjust(!parsedFlags.hasFlag(noAdjustSpawnFlag)) + .generator(parsedFlags.flagValue(generatorFlag, "")) + .generateStructures(!parsedFlags.hasFlag(noStructuresFlag))) + .onSuccess(newWorld -> messageSuccess(issuer, newWorld)) + .onFailure(failure -> messageFailure(issuer, failure)); + } + + private void messageWorldDetails(MVCommandIssuer issuer, String worldName, + World.Environment environment, ParsedCommandFlags parsedFlags) { + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES, + replace("{worldName}").with(worldName)); + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ENVIRONMENT, + replace("{environment}").with(environment.name())); + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_SEED, + replace("{seed}").with(parsedFlags.flagValue(seedFlag, "RANDOM"))); + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_WORLDTYPE, + replace("{worldType}").with(parsedFlags.flagValue(worldTypeFlag, WorldType.NORMAL).name())); + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN, + replace("{adjustSpawn}").with(String.valueOf(!parsedFlags.hasFlag(noAdjustSpawnFlag)))); + if (parsedFlags.hasFlag(biomeFlag)) { + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_BIOME, + replace("{biome}").with(parsedFlags.flagValue(biomeFlag, Biome.CUSTOM).name())); + } + if (parsedFlags.hasFlag(generatorFlag)) { + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR, + replace("{generator}").with(parsedFlags.flagValue(generatorFlag))); + } + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES, + replace("{structures}").with(String.valueOf(!parsedFlags.hasFlag(noStructuresFlag)))); + } + + private void messageSuccess(MVCommandIssuer issuer, LoadedMultiverseWorld newWorld) { + Logging.fine("World create success: " + newWorld); + issuer.sendInfo(MVCorei18n.CREATE_SUCCESS, Replace.WORLD.with(newWorld.getName())); + } + + private void messageFailure(MVCommandIssuer issuer, Failure failure) { + Logging.fine("World create failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/DebugCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/DebugCommand.java new file mode 100644 index 000000000..b4a8847ac --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/DebugCommand.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.locale.MVCorei18n; + +@Service +@CommandAlias("mv") +final class DebugCommand extends CoreCommand { + + private final MVCoreConfig config; + + @Inject + DebugCommand(@NotNull MVCommandManager commandManager, @NotNull MVCoreConfig config) { + super(commandManager); + this.config = config; + } + + @Subcommand("debug") + @CommandPermission("multiverse.core.debug") + @Description("{@@mv-core.debug.info.description}") + void onShowDebugCommand(BukkitCommandIssuer issuer) { + this.displayDebugMode(issuer); + } + + @CommandAlias("mvdebug") + @Subcommand("debug") + @CommandPermission("multiverse.core.debug") + @CommandCompletion("@range:3") + @Syntax("<{@@mv-core.debug.change.syntax}>") + @Description("{@@mv-core.debug.change.description}") + void onChangeDebugCommand( + BukkitCommandIssuer issuer, + + @Syntax("<{@@mv-core.debug.change.syntax}>") + @Description("{@@mv-core.debug.change.level.description}") + int level) { + + config.setGlobalDebug(level); + config.save(); + this.displayDebugMode(issuer); + } + + private void displayDebugMode(BukkitCommandIssuer issuer) { + final int debugLevel = config.getGlobalDebug(); + if (debugLevel == 0) { + issuer.sendInfo(MVCorei18n.DEBUG_INFO_OFF); + return; + } + issuer.sendInfo(MVCorei18n.DEBUG_INFO_ON, "{level}", String.valueOf(debugLevel)); + Logging.fine("Multiverse Debug ENABLED."); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/DeleteCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/DeleteCommand.java new file mode 100644 index 000000000..edf3e190e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/DeleteCommand.java @@ -0,0 +1,106 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Collections; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.queue.CommandQueuePayload; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.WorldTickDeferrer; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter; + +@Service +@CommandAlias("mv") +final class DeleteCommand extends CoreCommand { + + private final WorldManager worldManager; + private final PlayerWorldTeleporter playerWorldTeleporter; + private final WorldTickDeferrer worldTickDeferrer; + + private final CommandFlag removePlayersFlag = flag(CommandFlag.builder("--remove-players") + .addAlias("-r") + .build()); + + @Inject + DeleteCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull PlayerWorldTeleporter playerWorldTeleporter, + @NotNull WorldTickDeferrer worldTickDeferrer) { + super(commandManager); + this.worldManager = worldManager; + this.playerWorldTeleporter = playerWorldTeleporter; + this.worldTickDeferrer = worldTickDeferrer; + } + + @CommandAlias("mvdelete") + @Subcommand("delete") + @CommandPermission("multiverse.core.delete") + @CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvdeletecommand") + @Syntax("") + @Description("{@@mv-core.delete.description}") + void onDeleteCommand( + MVCommandIssuer issuer, + + @Single + @Syntax("") + @Description("The world you want to delete.") + MultiverseWorld world, + + @Optional + @Syntax("[--remove-players]") + @Description("") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + this.commandManager.getCommandQueueManager().addToQueue(CommandQueuePayload + .issuer(issuer) + .action(() -> runDeleteCommand(issuer, world, parsedFlags)) + .prompt(Message.of(MVCorei18n.DELETE_PROMPT, "", + Replace.WORLD.with(world.getName())))); + } + + private void runDeleteCommand(MVCommandIssuer issuer, MultiverseWorld world, ParsedCommandFlags parsedFlags) { + issuer.sendInfo(MVCorei18n.DELETE_DELETING, Replace.WORLD.with(world.getName())); + + var future = parsedFlags.hasFlag(removePlayersFlag) + && world.isLoaded() + && world instanceof LoadedMultiverseWorld loadedWorld + ? playerWorldTeleporter.removeFromWorld(loadedWorld) + : Async.completedFuture(Collections.emptyList()); + + future.thenRun(() -> worldTickDeferrer.deferWorldTick(() -> doWorldDeleting(issuer, world))); + } + + private void doWorldDeleting(MVCommandIssuer issuer, MultiverseWorld world) { + worldManager.deleteWorld(world) + .onSuccess(deletedWorldName -> { + Logging.fine("World delete success: " + deletedWorldName); + issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, Replace.WORLD.with(deletedWorldName)); + }).onFailure(failure -> { + Logging.fine("World delete failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/DumpsCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsCommand.java new file mode 100644 index 000000000..6200895bd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsCommand.java @@ -0,0 +1,69 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commands.DumpsLogPoster.LogsType; +import org.mvplugins.multiverse.core.commands.DumpsLogPoster.UploadType; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; + +@Service +@CommandAlias("mv") +final class DumpsCommand extends CoreCommand { + + private final DumpsService dumpsService; + + private final CommandValueFlag logsFlag = flag(CommandValueFlag + .enumBuilder("--logs", LogsType.class) + .addAlias("-l") + .build()); + + private final CommandValueFlag uploadFlag = flag(CommandValueFlag + .enumBuilder("--upload", UploadType.class) + .addAlias("-u") + .build()); + + // Does not upload logs or plugin list (except if --logs mclogs is there) + private final CommandFlag paranoidFlag = flag(CommandFlag.builder("--paranoid") + .addAlias("-p") + .build()); + + @Inject + DumpsCommand(@NotNull MVCommandManager commandManager, @NotNull DumpsService dumpsService) { + super(commandManager); + this.dumpsService = dumpsService; + } + + @Subcommand("dumps") + @CommandPermission("multiverse.core.dumps") + @CommandCompletion("@flags:groupName=mvdumpscommand") + @Syntax("[--logs ] [--upload ] [--paranoid]") + @Description("{@@mv-core.dumps.description}") + void onDumpsCommand( + MVCommandIssuer issuer, + + @Optional + @Syntax("[--logs ] [--upload ] [--paranoid]") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + // Grab all our flags + boolean paranoid = parsedFlags.hasFlag(paranoidFlag); + LogsType logsType = parsedFlags.flagValue(logsFlag, LogsType.MCLOGS); + UploadType servicesType = parsedFlags.flagValue(uploadFlag, UploadType.PASTESDEV); + + dumpsService.postLogs(issuer, logsType, servicesType, paranoid); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/DumpsLogPoster.java b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsLogPoster.java new file mode 100644 index 000000000..9e5ab21a5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsLogPoster.java @@ -0,0 +1,126 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Map; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.event.MVDumpsDebugInfoEvent; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.webpaste.PasteFailedException; +import org.mvplugins.multiverse.core.utils.webpaste.PasteService; +import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceFactory; +import org.mvplugins.multiverse.core.utils.webpaste.PasteServiceType; + +final class DumpsLogPoster extends BukkitRunnable { + + enum UploadType { + PASTEGG, + PASTESDEV + } + + enum LogsType { + APPEND, + MCLOGS + } + + private final MVCommandIssuer issuer; + private final LogsType logsType; + private final UploadType servicesType; + private final boolean paranoid; + private final String logs; + private final MVDumpsDebugInfoEvent versionEvent; + + DumpsLogPoster(@NotNull MVCommandIssuer issuer, + @NotNull DumpsLogPoster.LogsType logsType, + @NotNull DumpsLogPoster.UploadType servicesType, + boolean paranoid, + @Nullable String logs, + @NotNull MVDumpsDebugInfoEvent versionEvent) { + this.issuer = issuer; + this.logsType = logsType; + this.servicesType = servicesType; + this.paranoid = paranoid; + this.logs = logs; + this.versionEvent = versionEvent; + } + + @Override + public void run() { + // TODO: Refactor into smaller methods + Logging.finer("Logs type is: " + logsType); + Logging.finer("Services is: " + servicesType); + + // Deal with logs flag + if (!paranoid) { + switch (logsType) { + case MCLOGS -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST, + "{service}", "Logs", + "{link}", postToService(PasteServiceType.MCLOGS, logs, null)); + case APPEND -> versionEvent.putDetailedDebugInfo("latest.log", logs); + } + } + + // Get the files from the event + final Map files = versionEvent.getDetailedDebugInfo(); + + // Deal with uploading debug info + switch (servicesType) { + case PASTEGG -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST, + "{service}", "paste.gg", + "{link}", postToService(PasteServiceType.PASTEGG, null, files)); + case PASTESDEV -> issuer.sendInfo(MVCorei18n.DUMPS_URL_LIST, + "{service}", "pastes.dev", + "{link}", postToService(PasteServiceType.PASTESDEV, null, files)); + } + } + + private String postToService(@NotNull PasteServiceType type, @Nullable String rawPasteData, @Nullable Map pasteFiles) { + PasteService pasteService = PasteServiceFactory.getService(type, true); + + try { + // Upload normally when multi file is supported + if (pasteService.supportsMultiFile()) { + return pasteService.postData(pasteFiles); + } + + // When there is raw paste data, use that + if (rawPasteData != null) { // For the logs + return pasteService.postData(rawPasteData); + } + + // If all we have are files and the paste service does not support multi file then encode them + if (pasteFiles != null) { + return pasteService.postData(this.encodeAsString(pasteFiles)); + } + + // Should never get here + return "No data specified in code"; + + } catch (PasteFailedException e) { + e.printStackTrace(); + return "Error posting to service."; + } catch (NullPointerException e) { + e.printStackTrace(); + return "That service isn't supported yet."; + } + } + + private String encodeAsString(Map files) { + StringBuilder uploadData = new StringBuilder(); + for (Map.Entry entry : files.entrySet()) { + String file = entry.getKey(); + String data = entry.getValue(); + uploadData.append("# ---------- ") + .append(file) + .append(" ----------\n\n") + .append(data) + .append("\n\n"); + } + + return uploadData.toString(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/DumpsService.java b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsService.java new file mode 100644 index 000000000..d92d8eb58 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/DumpsService.java @@ -0,0 +1,141 @@ +package org.mvplugins.multiverse.core.commands; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.MultiversePluginsRegistration; +import org.mvplugins.multiverse.core.commands.DumpsLogPoster.LogsType; +import org.mvplugins.multiverse.core.commands.DumpsLogPoster.UploadType; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.event.MVDumpsDebugInfoEvent; +import org.mvplugins.multiverse.core.utils.FileUtils; +import org.mvplugins.multiverse.core.utils.StringFormatter; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +class DumpsService { + + private final MultiverseCore plugin; + private final WorldManager worldManager; + private final FileUtils fileUtils; + + @Inject + DumpsService(@NotNull MultiverseCore plugin, + @NotNull WorldManager worldManager, + @NotNull FileUtils fileUtils) { + this.plugin = plugin; + this.worldManager = worldManager; + this.fileUtils = fileUtils; + } + + void postLogs(MVCommandIssuer issuer, LogsType logsType, UploadType servicesType, boolean paranoid) { + // Initialise and add info to the debug event + MVDumpsDebugInfoEvent versionEvent = createAndCallDebugInfoEvent(); + + // Add plugin list if user isn't paranoid + if (!paranoid) { + versionEvent.putDetailedDebugInfo("plugins.md", "# Plugins\n\n" + getPluginList()); + } + + new DumpsLogPoster(issuer, logsType, servicesType, paranoid, getLogs(), versionEvent) + .runTaskAsynchronously(plugin); + } + + /** + * Get the contents of the latest.log file. + * + * @return A string containing the latest.log file + */ + private String getLogs() { + // Get the Path of latest.log + Path logsPath = fileUtils.getServerFolder().toPath().resolve("logs/latest.log"); + File logsFile = logsPath.toFile(); + + if (!logsFile.exists()) { + Logging.warning("Could not read logs/latest.log"); + return "Could not find log"; + } + + return readLogsFromFile(logsPath); + } + + private String readLogsFromFile(Path logsPath) { + String logs = "Could not read log"; + + // Try reading as ANSI encoded + try { + logs = Files.readString(logsPath, StandardCharsets.UTF_8); + } catch (IOException e) { + Logging.severe("Could not read logs/latest.log. See below for stack trace"); + e.printStackTrace(); + + try { + logs = Files.readString(logsPath, StandardCharsets.UTF_8); + } catch (IOException ex) { + // It is some other strange encoding + Logging.severe("Could not read ./logs/latest.log. See below for stack trace"); + ex.printStackTrace(); + } + } + + return logs; + } + + private String getDebugInfoString() { + return "# Multiverse-Core Version info" + "\n\n" + + " - Multiverse-Core Version: " + this.plugin.getDescription().getVersion() + '\n' + + " - Bukkit Version: " + this.plugin.getServer().getVersion() + '\n' + + " - Loaded Worlds: " + worldManager.getLoadedWorlds() + '\n' + + " - Multiverse Plugins Loaded: " + StringFormatter.joinAnd(MultiversePluginsRegistration.get().getRegisteredPlugins()) + '\n' + + " - Multiverse Plugins Count: " + MultiversePluginsRegistration.get().getPluginCount() + '\n'; + } + + private MVDumpsDebugInfoEvent createAndCallDebugInfoEvent() { + MVDumpsDebugInfoEvent event = new MVDumpsDebugInfoEvent(); + addDebugInfoToEvent(event); + plugin.getServer().getPluginManager().callEvent(event); + return event; + } + + private void addDebugInfoToEvent(MVDumpsDebugInfoEvent event) { + // Add the legacy file, but as markdown, so it's readable + event.putDetailedDebugInfo("version.md", this.getDebugInfoString()); + + // add config.yml + File configFile = new File(plugin.getDataFolder(), "config.yml"); + event.putDetailedDebugInfo("Multiverse-Core/config.yml", configFile); + + // add worlds.yml + File worldsFile = new File(plugin.getDataFolder(), "worlds.yml"); + event.putDetailedDebugInfo("Multiverse-Core/worlds.yml", worldsFile); + + // Add bukkit.yml if we found it + if (fileUtils.getBukkitConfig() != null) { + event.putDetailedDebugInfo(fileUtils.getBukkitConfig().getPath(), fileUtils.getBukkitConfig()); + } else { + Logging.warning("/mv dumps could not find bukkit.yml. Not including file"); + } + + // Add server.properties if we found it + if (fileUtils.getServerProperties() != null) { + event.putDetailedDebugInfo(fileUtils.getServerProperties().getPath(), fileUtils.getServerProperties()); + } else { + Logging.warning("/mv dumps could not find server.properties. Not including file"); + } + + } + + String getPluginList() { + return " - " + StringUtils.join(plugin.getServer().getPluginManager().getPlugins(), "\n - "); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/GameruleCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/GameruleCommand.java new file mode 100644 index 000000000..770bd6df6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/GameruleCommand.java @@ -0,0 +1,213 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.MessageType; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.context.GameRuleValue; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.MapContentProvider; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +@Service +@CommandAlias("mv") +@Subcommand("gamerule|rule|gamerules|rules") +final class GameruleCommand extends CoreCommand { + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + @Inject + GameruleCommand(@NotNull MVCommandManager commandManager) { + super(commandManager); + } + + @Subcommand("set") + @CommandPermission("multiverse.core.gamerule.set") + @CommandCompletion("@gamerules @gamerulesvalues @mvworlds:multiple|*") + @Syntax(" [World or *]") + @Description("{@@mv-core.gamerule.set.description}") + void onGameruleSetCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.gamerule.set.gamerule.description}") + GameRule gamerule, + + @Syntax("") + @Description("{@@mv-core.gamerule.set.value.description}") + GameRuleValue gameRuleValue, + + @Flags("resolve=issuerAware") + @Syntax("[World or *]") + @Description("{@@mv-core.gamerule.set.world.description}") + LoadedMultiverseWorld[] worlds) { + Object value = gameRuleValue.value(); + boolean success = true; + for (LoadedMultiverseWorld world : worlds) { + // Set gamerules and add false to list if it fails + World bukkitWorld = world.getBukkitWorld().getOrNull(); + if (bukkitWorld == null || !bukkitWorld.setGameRule(gamerule, value)) { + issuer.sendError(MVCorei18n.GAMERULE_SET_FAILED, + Replace.GAMERULE.with(gamerule.getName()), + Replace.VALUE.with(value.toString()), + Replace.WORLD.with(world.getName()), + replace("{type}").with(gamerule.getType().getName())); + success = false; + } + } + // Tell user if it was successful + if (success) { + if (worlds.length == 1) { + issuer.sendInfo(MVCorei18n.GAMERULE_SET_SUCCESS_SINGLE, + Replace.GAMERULE.with(gamerule.getName()), + Replace.VALUE.with(value.toString()), + Replace.WORLD.with(worlds[0].getName())); + } else if (worlds.length > 1) { + issuer.sendInfo(MVCorei18n.GAMERULE_SET_SUCCESS_MULTIPLE, + Replace.GAMERULE.with(gamerule.getName()), + Replace.VALUE.with(value.toString()), + Replace.COUNT.with(String.valueOf(worlds.length))); + } + } + } + + @Subcommand("reset") + @CommandPermission("multiverse.core.gamerule.set") + @CommandCompletion("@gamerules @mvworlds:multiple|*") + @Syntax(" [World or *]") + @Description("{@@mv-core.gamerule.reset.description}") + void onGameruleSetCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.gamerule.reset.gamerule.description}") + GameRule gamerule, + + @Flags("resolve=issuerAware") + @Syntax("[World or *]") + @Description("{@@mv-core.gamerule.reset.world.description}") + LoadedMultiverseWorld[] worlds) { + AtomicBoolean success = new AtomicBoolean(true); + Arrays.stream(worlds) + .forEach(world -> world.getBukkitWorld() + .peek(bukkitWorld -> bukkitWorld.setGameRule(gamerule, bukkitWorld.getGameRuleDefault(gamerule))) + .onEmpty(() -> { + success.set(false); + issuer.sendError(MVCorei18n.GAMERULE_RESET_FAILED, + Replace.GAMERULE.with(gamerule.getName()), + Replace.WORLD.with(world.getName())); + })); + + // Tell user if it was successful + if (success.get()) { + if (worlds.length == 1) { + issuer.sendInfo(MVCorei18n.GAMERULE_RESET_SUCCESS_SINGLE, + Replace.GAMERULE.with(gamerule.getName()), + Replace.WORLD.with(worlds[0].getName())); + } else if (worlds.length > 1) { + issuer.sendInfo(MVCorei18n.GAMERULE_RESET_SUCCESS_MULTIPLE, + Replace.GAMERULE.with(gamerule.getName()), + Replace.COUNT.with(String.valueOf(worlds.length))); + } + } + } + + @Subcommand("list") + @CommandPermission("multiverse.core.gamerule.list") + @CommandCompletion("@mvworlds|@flags:groupName=mvgamerulecommand @flags:groupName=mvgamerulecommand") + @Syntax("[world] [--page ] [--filter ]") + @Description("{@@mv-core.gamerule.list.description}") + void onGameruleListCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerAware") + @Syntax("") + @Description("{@@mv-core.gamerule.list.description.world}") + LoadedMultiverseWorld world, + + @Optional + @Syntax("[--page ] [--filter ]") + @Description("{@@mv-core.gamerule.list.description.page}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + ContentDisplay.create() + .addContent(MapContentProvider.forContent(getGameRuleMap(world.getBukkitWorld().getOrNull())) + .withKeyColor(ChatColor.AQUA) + .withValueColor(ChatColor.WHITE)) + .withSendHandler(PagedSendHandler.create() + .withHeader(this.getListTitle(issuer, world.getBukkitWorld().getOrNull())) + .doPagination(true) + .withTargetPage(parsedFlags.flagValue(pageFlag, 1)) + .withFilter(parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()))) + .send(issuer); + } + + /** + * Gets all the gamerules and their values for a given world. + * + * @param world The world to find gamerules for. + * @return A map of the gamerules and their values + */ + private Map getGameRuleMap(World world) { + Map gameRuleMap = new HashMap<>(); + if (world == null) { + return gameRuleMap; + } + + for (String gamerule : world.getGameRules()) { + GameRule gameruleEnum = GameRule.getByName(gamerule); + if (gameruleEnum == null) { + continue; + } + Object gameruleValue = world.getGameRuleValue(gameruleEnum); + if (gameruleValue == null) { + gameRuleMap.put(gameruleEnum.getName(), "null"); + continue; + } + gameRuleMap.put(gameruleEnum.getName(), gameruleValue.toString()); + } + return gameRuleMap; + } + + private String getListTitle(CommandIssuer issuer, World world) { + return this.commandManager.formatMessage( + issuer, + MessageType.INFO, + MVCorei18n.GAMERULE_LIST_TITLE, + "{world}", world.getName()); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/GeneratorsCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/GeneratorsCommand.java new file mode 100644 index 000000000..bf3b579d1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/GeneratorsCommand.java @@ -0,0 +1,84 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.List; + +import co.aikar.commands.MessageType; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.ListContentProvider; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; + +/** + * List all gamerules in your current or specified world. + */ +@Service +@CommandAlias("mv") +final class GeneratorsCommand extends CoreCommand { + + private final GeneratorProvider generatorProvider; + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + @Inject + GeneratorsCommand(@NotNull MVCommandManager commandManager, @NotNull GeneratorProvider generatorProvider) { + super(commandManager); + this.generatorProvider = generatorProvider; + } + + @CommandAlias("mvgenerators|mvgens") + @Subcommand("generators|gens") + @CommandPermission("multiverse.core.generator") + @CommandCompletion("@flags:groupName=mvgeneratorscommand @flags:groupName=mvgeneratorscommand") + @Syntax("") + @Description("{@@mv-core.generators.description}") + void onGamerulesCommand( + @NotNull MVCommandIssuer issuer, + + @Optional + @Syntax("[--page ] [--filter ]") + @Description("{@@mv-core.generators.description.flags}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + // Get the generators loaded using the command suggestions + List generators = (List) generatorProvider.suggestGeneratorString(""); + + // Tell the user if we cannot find any generator plugins, then abort + if (generators.isEmpty()) { + issuer.sendMessage(commandManager.formatMessage(issuer, MessageType.INFO, MVCorei18n.GENERATORS_EMPTY)); + return; + } + + ContentDisplay.create() + .addContent(ListContentProvider.forContent(generators)) + .withSendHandler(PagedSendHandler.create() + .withHeader("%s====[ Multiverse Generator List ]====", ChatColor.AQUA) + .doPagination(true) + .withTargetPage(parsedFlags.flagValue(pageFlag, 1)) + .withFilter(parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()))) + .send(issuer); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ImportCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ImportCommand.java new file mode 100644 index 000000000..fc9e078cd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ImportCommand.java @@ -0,0 +1,107 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Conditions; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import com.google.common.collect.Lists; +import jakarta.inject.Inject; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; +import org.mvplugins.multiverse.core.world.options.ImportWorldOptions; + +@Service +@CommandAlias("mv") +final class ImportCommand extends CoreCommand { + + private final WorldManager worldManager; + + private GeneratorProvider generatorProvider; + private final CommandValueFlag generatorFlag = flag(CommandValueFlag + .builder("--generator", String.class) + .addAlias("-g") + .completion(input -> generatorProvider.suggestGeneratorString(input)) + .build()); + + private final CommandFlag noAdjustSpawnFlag = flag(CommandFlag.builder("--no-adjust-spawn") + .addAlias("-n") + .build()); + + private final CommandValueFlag biomeFlag = flag(CommandValueFlag.builder("--biome", Biome.class) + .addAlias("-b") + .completion(input -> Lists.newArrayList(Registry.BIOME).stream() + .filter(biome -> biome != Biome.CUSTOM) + .map(biome -> biome.getKey().getKey()) + .toList()) + .context(biomeStr -> Registry.BIOME.get(NamespacedKey.minecraft(biomeStr))) + .build()); + + @Inject + ImportCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull GeneratorProvider generatorProvider) { + super(commandManager); + this.worldManager = worldManager; + this.generatorProvider = generatorProvider; + } + + @CommandAlias("mvimport|mvim") + @Subcommand("import") + @CommandPermission("multiverse.core.import") + @CommandCompletion("@mvworlds:scope=potential @environments @flags:groupName=mvimportcommand") + @Syntax(" [--generator --adjust-spawn --biome ]") + @Description("{@@mv-core.import.description}") + void onImportCommand( + MVCommandIssuer issuer, + + @Conditions("worldname:scope=new") + @Syntax("") + @Description("{@@mv-core.import.name.description}") + String worldName, + + @Syntax("") + @Description("{@@mv-core.import.env.description}") + World.Environment environment, + + @Optional + @Syntax("[--generator --adjust-spawn --biome ]") + @Description("{@@mv-core.import.other.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING, Replace.WORLD.with(worldName)); + worldManager.importWorld(ImportWorldOptions.worldName(worldName) + .biome(parsedFlags.flagValue(biomeFlag, Biome.CUSTOM)) + .environment(environment) + .generator(parsedFlags.flagValue(generatorFlag, String.class)) + .useSpawnAdjust(!parsedFlags.hasFlag(noAdjustSpawnFlag))) + .onSuccess(newWorld -> { + Logging.fine("World import success: " + newWorld); + issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS, Replace.WORLD.with(newWorld.getName())); + }) + .onFailure(failure -> { + Logging.fine("World import failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/InfoCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/InfoCommand.java new file mode 100644 index 000000000..12ac1c76a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/InfoCommand.java @@ -0,0 +1,154 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.LinkedHashMap; +import java.util.Map; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.MapContentProvider; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +@Service +@CommandAlias("mv") +final class InfoCommand extends CoreCommand { + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + private final LocationManipulation locationManipulation; + private final MVEconomist economist; + + @Inject + InfoCommand( + @NotNull MVCommandManager commandManager, + @NotNull LocationManipulation locationManipulation, + @NotNull MVEconomist economist) { + super(commandManager); + this.locationManipulation = locationManipulation; + this.economist = economist; + } + + // TODO: support info for unloaded worlds + @CommandAlias("mvinfo|mvi") + @Subcommand("info") + @CommandPermission("multiverse.core.info") + @CommandCompletion("@mvworlds:scope=both|@flags:groupName=mvinfocommand @flags:groupName=mvinfocommand") + @Syntax("[world] [--page ] [--filter ]") + @Description("{@@mv-core.info.description") + public void onInfoCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerAware") + @Syntax("") + @Description("{@@mv-core.info.description.world}") + LoadedMultiverseWorld world, + + @Optional + @Syntax("[--page ]") + @Description("{@@mv-core.info.description.page}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + ContentDisplay.create() + .addContent(MapContentProvider.forContent(getInfo(world)) + .withKeyColor(ChatColor.AQUA) + .withValueColor(ChatColor.WHITE)) + .withSendHandler(PagedSendHandler.create() + .withHeader(getTitle(world)) + .doPagination(true) + .withTargetPage(parsedFlags.flagValue(pageFlag, 1)) + .withFilter(parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()))) + .send(issuer); + } + + private String getTitle(MultiverseWorld world) { + return "&a&l---- World Info: &f&l%s&a&l ----".formatted(world.getName()); + } + + private Map getInfo(LoadedMultiverseWorld world) { + Map outMap = new LinkedHashMap<>(); + + outMap.put("World Name", world.getName()); + outMap.put("World Alias", world.getAlias()); + outMap.put("World UID", world.getUID().toString()); + outMap.put("Game Mode", world.getGameMode().toString()); + outMap.put("Difficulty", world.getDifficulty().toString()); + outMap.put("Spawn Location", locationManipulation.strCoords(world.getSpawnLocation())); + outMap.put("Seed", String.valueOf(world.getSeed())); + getEntryFeeInfo(outMap, world); + outMap.put("Respawn World", world.getRespawnWorldName()); + outMap.put("World Type", world.getWorldType().get().toString()); + outMap.put("Biome", world.getBiome() == null ? "@vanilla" : world.getBiome().getKey().getKey()); + outMap.put("Generator", world.getGenerator()); + outMap.put("Generate Structures", world.canGenerateStructures().get().toString()); + outMap.put("World Scale", String.valueOf(world.getScale())); + outMap.put("Weather Enabled", String.valueOf(world.getAllowWeather())); + outMap.put("Hunger Depletes", String.valueOf(world.getHunger())); + outMap.put("Keep Spawn In Memory", String.valueOf(world.getKeepSpawnInMemory())); + outMap.put("PVP Enabled", String.valueOf(world.getPvp())); + getAnimalSpawningInfo(outMap, world); + getMonsterSpawningInfo(outMap, world); + + return outMap; + } + + private void getEntryFeeInfo(Map outMap, MultiverseWorld world) { + double price = world.getPrice(); + if (price == 0) { + outMap.put("Entry Fee", "FREE!"); + } else if (price > 0) { + outMap.put("Entry Fee", economist.formatPrice(-price, world.getCurrency())); + } else if (price < 0) { + outMap.put("Entry Reward", economist.formatPrice(price, world.getCurrency())); + } + } + + private void getAnimalSpawningInfo(Map outMap, MultiverseWorld world) { + if (world.getSpawningAnimals()) { + outMap.put("Spawning Animals", "ALL"); + } else { + if (!world.getSpawningAnimalsExceptions().isEmpty()) { + outMap.put("Spawning Animals", world.getSpawningAnimalsExceptions().toString()); + } else { + outMap.put("Spawning Animals", "NONE"); + } + } + } + + private void getMonsterSpawningInfo(Map outMap, MultiverseWorld world) { + if (world.getSpawningMonsters()) { + outMap.put("Spawning Monsters", "ALL"); + } else { + if (!world.getSpawningMonstersExceptions().isEmpty()) { + outMap.put("Spawning Monsters", world.getSpawningMonstersExceptions().toString()); + } else { + outMap.put("Spawning Monsters", "NONE"); + } + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ListCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ListCommand.java new file mode 100644 index 000000000..0f384c5e9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ListCommand.java @@ -0,0 +1,137 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.ArrayList; +import java.util.List; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.ListContentProvider; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryChecker; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider; + +@Service +@CommandAlias("mv") +final class ListCommand extends CoreCommand { + + private final WorldManager worldManager; + private final WorldEntryCheckerProvider worldEntryCheckerProvider; + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + private final CommandFlag rawFlag = flag(CommandFlag.builder("--raw") + .addAlias("-r") + .build()); + + @Inject + ListCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider) { + super(commandManager); + this.worldManager = worldManager; + this.worldEntryCheckerProvider = worldEntryCheckerProvider; + } + + @CommandAlias("mvlist|mvl") + @Subcommand("list") + @CommandPermission("multiverse.core.list.worlds") + @CommandCompletion("@flags:groupName=mvlistcommand") + @Syntax("--filter [filter] --page [page] --raw") + @Description("Displays a listing of all worlds that you can enter.") + public void onListCommand( + MVCommandIssuer issuer, + + @Syntax("[--filter ] [--page ]") + @Description("Filters the list of worlds by the given regex and displays the given page.") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + ContentDisplay.create() + .addContent(ListContentProvider.forContent(getListContents(issuer, parsedFlags.hasFlag(rawFlag)))) + .withSendHandler(PagedSendHandler.create() + .withHeader("%s====[ Multiverse World List ]====", ChatColor.GOLD) + .withTargetPage(parsedFlags.flagValue(pageFlag, 1)) + .withFilter(parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()))) + .send(issuer); + } + + private List getListContents(MVCommandIssuer issuer, boolean useRawNames) { + List worldList = new ArrayList<>(); + WorldEntryChecker worldEntryChecker = worldEntryCheckerProvider.forSender(issuer.getIssuer()); + + worldManager.getLoadedWorlds().stream() + .filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess()) + .filter(world -> canSeeWorld(issuer, world)) + .map(world -> hiddenText(world) + getWorldName(world, useRawNames) + " - " + + parseColouredEnvironment(world.getEnvironment())) + .sorted() + .forEach(worldList::add); + + worldManager.getUnloadedWorlds().stream() + .filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess()) + .map(world -> ChatColor.GRAY + getWorldName(world, useRawNames) + " - UNLOADED") + .sorted() + .forEach(worldList::add); + + return worldList; + } + + /** + * Gets a world's name or alias. + * + * @param world The world to retrieve the name of + * @param useRawNames True to return the name, false to return the alias + * @return The name + */ + private String getWorldName(MultiverseWorld world, boolean useRawNames) { + if (useRawNames) { + return world.getName(); + } + + return world.getAlias(); + } + + private boolean canSeeWorld(MVCommandIssuer issuer, MultiverseWorld world) { + return !world.isHidden() + // TODO: Refactor stray permission check + || issuer.hasPermission("multiverse.core.modify"); + } + + private String hiddenText(MultiverseWorld world) { + return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : ""; + } + + private String parseColouredEnvironment(World.Environment env) { + ChatColor color = switch (env) { + case NETHER -> ChatColor.RED; + case NORMAL -> ChatColor.GREEN; + case THE_END -> ChatColor.AQUA; + default -> ChatColor.GOLD; + }; + return color + env.toString(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/LoadCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/LoadCommand.java new file mode 100644 index 000000000..ba5c8b24f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/LoadCommand.java @@ -0,0 +1,58 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Conditions; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class LoadCommand extends CoreCommand { + + private final WorldManager worldManager; + + @Inject + LoadCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) { + super(commandManager); + this.worldManager = worldManager; + } + + @CommandAlias("mvload") + @Subcommand("load") + @CommandPermission("multiverse.core.load") + @CommandCompletion("@mvworlds:scope=unloaded") + @Syntax("") + @Description("{@@mv-core.load.description}") + void onLoadCommand( + MVCommandIssuer issuer, + + @Single + @Conditions("worldname:scope=unloaded") + @Syntax("") + @Description("{@@mv-core.load.world.description}") + String worldName) { + issuer.sendInfo(MVCorei18n.LOAD_LOADING, Replace.WORLD.with(worldName)); + worldManager.loadWorld(worldName) + .onSuccess(newWorld -> { + Logging.fine("World load success: " + newWorld); + issuer.sendInfo(MVCorei18n.LOAD_SUCCESS, Replace.WORLD.with(newWorld.getName())); + }).onFailure(failure -> { + Logging.fine("World load failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ModifyCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ModifyCommand.java new file mode 100644 index 000000000..a70d4d741 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ModifyCommand.java @@ -0,0 +1,83 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.configuration.handle.PropertyModifyAction; +import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class ModifyCommand extends CoreCommand { + + private final WorldManager worldManager; + + @Inject + ModifyCommand(@NotNull MVCommandManager commandManager, WorldManager worldManager) { + super(commandManager); + this.worldManager = worldManager; + } + + @CommandAlias("mvmodify|mvm") + @Subcommand("modify") + @CommandPermission("multiverse.core.modify") + @CommandCompletion("@mvworlds:scope=both @propsmodifyaction @mvworldpropsname @mvworldpropsvalue") + @Syntax("[world] ") + @Description("") + void onModifyCommand(// SUPPRESS CHECKSTYLE: ParameterNumber + MVCommandIssuer issuer, + + @Flags("resolve=issuerAware") + @Syntax("[world]") + @Description("") + @NotNull MultiverseWorld world, + + @Syntax("") + @Description("") + @NotNull PropertyModifyAction action, + + @Syntax("") + @Description("") + @NotNull String propertyName, + + @Optional + @Single + @Syntax("[value]") + @Description("") + @Nullable String propertyValue) { + if (action.isRequireValue() && propertyValue == null) { + issuer.sendMessage("You must specify a value to " + action.name().toLowerCase() + + " '" + propertyName + "'."); + return; + } + + StringPropertyHandle worldPropertyHandle = world.getStringPropertyHandle(); + worldPropertyHandle.modifyPropertyString(propertyName, propertyValue, action).onSuccess(ignore -> { + issuer.sendMessage("Property %s%s set to %s%s for world %s%s%s.".formatted( + propertyName, ChatColor.BLUE, worldPropertyHandle.getProperty(propertyName).getOrNull(), + ChatColor.BLUE, ChatColor.GRAY, world.getName(), ChatColor.BLUE)); + worldManager.saveWorldsConfig(); + }).onFailure(exception -> { + issuer.sendMessage("Failed to %s%s property %s%s to %s%s for world %s%s.".formatted( + action.name().toLowerCase(), ChatColor.BLUE, propertyName, ChatColor.BLUE, + propertyValue, ChatColor.BLUE, world.getName(), ChatColor.BLUE)); + issuer.sendMessage(exception.getMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/RegenCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/RegenCommand.java new file mode 100644 index 000000000..f371f75c0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/RegenCommand.java @@ -0,0 +1,141 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Collections; +import java.util.List; + +import co.aikar.commands.ACFUtil; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.queue.CommandQueuePayload; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.WorldTickDeferrer; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter; +import org.mvplugins.multiverse.core.world.options.RegenWorldOptions; + +@Service +@CommandAlias("mv") +final class RegenCommand extends CoreCommand { + + private final WorldManager worldManager; + private final PlayerWorldTeleporter playerWorldTeleporter; + private final WorldTickDeferrer worldTickDeferrer; + + private final CommandValueFlag seedFlag = flag(CommandValueFlag.builder("--seed", String.class) + .addAlias("-s") + .completion(input -> Collections.singleton(String.valueOf(ACFUtil.RANDOM.nextLong()))) + .optional() + .build()); + + private final CommandFlag resetWorldConfigFlag = flag(CommandFlag.builder("--reset-world-config") + .addAlias("-wc") + .build()); + + private final CommandFlag resetGamerulesFlag = flag(CommandFlag.builder("--reset-gamerules") + .addAlias("-gm") + .build()); + + private final CommandFlag resetWorldBorderFlag = flag(CommandFlag.builder("--reset-world-border") + .addAlias("-wb") + .build()); + + private final CommandFlag removePlayersFlag = flag(CommandFlag.builder("--remove-players") + .addAlias("-r") + .build()); + + @Inject + RegenCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull PlayerWorldTeleporter playerWorldTeleporter, + @NotNull WorldTickDeferrer worldTickDeferrer) { + super(commandManager); + this.worldManager = worldManager; + this.playerWorldTeleporter = playerWorldTeleporter; + this.worldTickDeferrer = worldTickDeferrer; + } + + @CommandAlias("mvregen") + @Subcommand("regen") + @CommandPermission("multiverse.core.regen") + @CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvregencommand") + @Syntax(" [--seed [seed] --reset-world-config --reset-gamerules --reset-world-border --remove-players]") + @Description("{@@mv-core.regen.description}") + void onRegenCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.regen.world.description}") + LoadedMultiverseWorld world, + + @Optional + @Syntax("[--seed [seed] --reset-world-config --reset-gamerules --reset-world-border --remove-players]") + @Description("{@@mv-core.regen.other.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + this.commandManager.getCommandQueueManager().addToQueue(CommandQueuePayload + .issuer(issuer) + .action(() -> runRegenCommand(issuer, world, parsedFlags)) + .prompt(Message.of(MVCorei18n.REGEN_PROMPT, "", + Replace.WORLD.with(world.getName())))); + } + + private void runRegenCommand(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) { + issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, Replace.WORLD.with(world.getName())); + List worldPlayers = world.getPlayers().getOrElse(Collections.emptyList()); + + var future = parsedFlags.hasFlag(removePlayersFlag) + ? playerWorldTeleporter.removeFromWorld(world) + : Async.completedFuture(Collections.emptyList()); + + // todo: using future will hide stacktrace + future.thenRun(() -> worldTickDeferrer.deferWorldTick(() -> + doWorldRegening(issuer, world, parsedFlags, worldPlayers))); + } + + private void doWorldRegening( + MVCommandIssuer issuer, + LoadedMultiverseWorld world, + ParsedCommandFlags parsedFlags, + List worldPlayers) { + //todo: Change biome on regen + RegenWorldOptions regenWorldOptions = RegenWorldOptions.world(world) + .randomSeed(parsedFlags.hasFlag(seedFlag)) + .seed(parsedFlags.flagValue(seedFlag)) + .keepWorldConfig(!parsedFlags.hasFlag(resetWorldConfigFlag)) + .keepGameRule(!parsedFlags.hasFlag(resetGamerulesFlag)) + .keepWorldBorder(!parsedFlags.hasFlag(resetWorldBorderFlag)); + + worldManager.regenWorld(regenWorldOptions).onSuccess(newWorld -> { + Logging.fine("World regen success: " + newWorld); + issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, Replace.WORLD.with(newWorld.getName())); + if (parsedFlags.hasFlag(removePlayersFlag)) { + playerWorldTeleporter.teleportPlayersToWorld(worldPlayers, newWorld); + } + }).onFailure(failure -> { + Logging.warning("World regen failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/ReloadCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/ReloadCommand.java new file mode 100644 index 000000000..05a7f5037 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/ReloadCommand.java @@ -0,0 +1,72 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.ArrayList; +import java.util.List; + +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import jakarta.inject.Inject; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.event.MVConfigReloadEvent; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class ReloadCommand extends CoreCommand { + + private final MVCoreConfig config; + private final AnchorManager anchorManager; + private final WorldManager worldManager; + private final PluginManager pluginManager; + + @Inject + ReloadCommand( + @NotNull MVCommandManager commandManager, + @NotNull MVCoreConfig config, + @NotNull AnchorManager anchorManager, + @NotNull WorldManager worldManager, + @NotNull PluginManager pluginManager) { + super(commandManager); + this.config = config; + this.anchorManager = anchorManager; + this.worldManager = worldManager; + this.pluginManager = pluginManager; + } + + @CommandAlias("mvreload|mvr") + @Subcommand("reload") + @CommandPermission("multiverse.core.reload") + @Description("{@@mv-core.reload.description}") + void onReloadCommand(@NotNull BukkitCommandIssuer issuer) { + issuer.sendInfo(MVCorei18n.RELOAD_RELOADING); + try { + // TODO: Make this all Try + this.config.load().getOrElseThrow(e -> new RuntimeException("Failed to load config", e)); + this.worldManager.initAllWorlds().getOrElseThrow(e -> new RuntimeException("Failed to init worlds", e)); + this.anchorManager.loadAnchors(); + } catch (Exception e) { + e.printStackTrace(); + } + + List configsLoaded = new ArrayList<>(); + configsLoaded.add("Multiverse-Core - config.yml"); + configsLoaded.add("Multiverse-Core - worlds.yml"); + configsLoaded.add("Multiverse-Core - anchors.yml"); + + MVConfigReloadEvent configReload = new MVConfigReloadEvent(configsLoaded); + this.pluginManager.callEvent(configReload); + + configReload.getAllConfigsLoaded().forEach(issuer::sendMessage); + issuer.sendInfo(MVCorei18n.RELOAD_SUCCESS); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/RemoveCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/RemoveCommand.java new file mode 100644 index 000000000..9ee647f6e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/RemoveCommand.java @@ -0,0 +1,91 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Collections; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Conditions; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter; + +@Service +@CommandAlias("mv") +final class RemoveCommand extends CoreCommand { + + private final WorldManager worldManager; + private final PlayerWorldTeleporter playerWorldTeleporter; + + private final CommandFlag removePlayersFlag = flag(CommandFlag.builder("--remove-players") + .addAlias("-r") + .build()); + + @Inject + RemoveCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull PlayerWorldTeleporter playerWorldTeleporter) { + super(commandManager); + this.worldManager = worldManager; + this.playerWorldTeleporter = playerWorldTeleporter; + } + + @CommandAlias("mvremove") + @Subcommand("remove") + @CommandPermission("multiverse.core.remove") + @CommandCompletion("@mvworlds:scope=both") + @Syntax("") + @Description("{@@mv-core.remove.description}") + void onRemoveCommand( + MVCommandIssuer issuer, + + @Single + @Conditions("mvworlds:scope=both @flags:groupName=mvremovecommand") + @Syntax("") + @Description("{@@mv-core.remove.world.description}") + MultiverseWorld world, + + @Optional + @Syntax("[--remove-players]") + @Description("") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + var future = parsedFlags.hasFlag(removePlayersFlag) + ? worldManager.getLoadedWorld(world) + .map(playerWorldTeleporter::removeFromWorld) + .getOrElse(Async.completedFuture(Collections.emptyList())) + : Async.completedFuture(Collections.emptyList()); + + future.thenRun(() -> doWorldRemoving(issuer, world)); + } + + private void doWorldRemoving(MVCommandIssuer issuer, MultiverseWorld world) { + worldManager.removeWorld(world) + .onSuccess(removedWorldName -> { + Logging.fine("World remove success: " + removedWorldName); + issuer.sendInfo(MVCorei18n.REMOVE_SUCCESS, Replace.WORLD.with(removedWorldName)); + }).onFailure(failure -> { + Logging.fine("World remove failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/RootCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/RootCommand.java new file mode 100644 index 000000000..5194c7446 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/RootCommand.java @@ -0,0 +1,34 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.annotation.CommandAlias; +import jakarta.inject.Inject; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.locale.MVCorei18n; + +@Service +final class RootCommand extends CoreCommand { + + private final Plugin plugin; + + @Inject + RootCommand(@NotNull MVCommandManager commandManager, @NotNull MultiverseCore plugin) { + super(commandManager); + this.plugin = plugin; + } + + @CommandAlias("mv") + void onRootCommand(CommandIssuer issuer) { + PluginDescriptionFile description = this.plugin.getDescription(); + issuer.sendInfo(MVCorei18n.ROOT_TITLE, + "{name}", description.getName(), + "{version}", description.getVersion()); + issuer.sendInfo(MVCorei18n.ROOT_HELP); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/SetSpawnCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/SetSpawnCommand.java new file mode 100644 index 000000000..38bacb4f1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/SetSpawnCommand.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class SetSpawnCommand extends CoreCommand { + + private final WorldManager worldManager; + + @Inject + SetSpawnCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager) { + super(commandManager); + this.worldManager = worldManager; + } + + @CommandAlias("mvsetspawn|mvss") + @Subcommand("setspawn") + @CommandPermission("multiverse.core.spawn.set") + @Syntax("[x],[y],[z],[pitch],[yaw]") + @Description("{@@mv-core.setspawn.description}") + void onSetSpawnCommand( + BukkitCommandIssuer issuer, + + @Optional + @Syntax("") + @Description("{@@mv-core.setspawn.location.description}") + Location location) { + Option.of(location).orElse(() -> { + if (issuer.isPlayer()) { + return Option.of(issuer.getPlayer().getLocation()); + } + return Option.none(); + }).peek(finalLocation -> + worldManager.getLoadedWorld(finalLocation.getWorld()) + .peek(mvWorld -> mvWorld.setSpawnLocation(finalLocation) + .onSuccess(ignore -> issuer.sendMessage( + "Successfully set spawn in " + mvWorld.getName() + " to " + + prettyLocation(mvWorld.getSpawnLocation()))) + .onFailure(e -> issuer.sendMessage(e.getLocalizedMessage()))) + .onEmpty(() -> issuer.sendMessage("That world is not loaded or does not exist!"))) + .onEmpty(() -> issuer.sendMessage("You must specify a location in the format: worldname:x,y,z")); + } + + private String prettyLocation(Location location) { + return location.getX() + ", " + location.getY() + ", " + location.getZ() + ". pitch:" + location.getPitch() + + ", yaw:" + location.getYaw(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/SpawnCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/SpawnCommand.java new file mode 100644 index 000000000..c28c2005c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/SpawnCommand.java @@ -0,0 +1,163 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.TeleportFailureReason; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class SpawnCommand extends CoreCommand { + + private final WorldManager worldManager; + private final AsyncSafetyTeleporter safetyTeleporter; + private final CorePermissionsChecker permissionsChecker; + + private final CommandFlag unsafeFlag = flag(CommandFlag.builder("--unsafe") + .addAlias("-u") + .build()); + + @Inject + SpawnCommand(@NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull AsyncSafetyTeleporter safetyTeleporter, + @NotNull CorePermissionsChecker permissionsChecker) { + super(commandManager); + this.worldManager = worldManager; + this.safetyTeleporter = safetyTeleporter; + this.permissionsChecker = permissionsChecker; + } + + @CommandAlias("mvspawn") + @Subcommand("spawn") + @CommandPermission("@mvspawn") + @CommandCompletion("@playersarray:checkPermissions=@mvspawnother|@flags:groupName=mvspawncommand,resolveUntil=arg1" + + " @flags:groupName=mvspawncommand") + @Syntax("[player]") + @Description("{@@mv-core.spawn.description}") + void onSpawnTpCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerAware") + @Syntax("[player]") + @Description("{@@mv-core.spawn.player.description}") + Player[] players, + + @Optional + @Syntax("[--unsafe]") + @Description("") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + Map> playersByWorld = Arrays.stream(players) + .collect(Collectors.groupingBy(Entity::getWorld)); + playersByWorld.forEach((world, playerList) -> + teleportPlayersToSpawn(issuer, world, playerList, !parsedFlags.hasFlag(unsafeFlag))); + } + + private void teleportPlayersToSpawn(MVCommandIssuer issuer, World world, + List players, boolean checkSafety) { + LoadedMultiverseWorld mvWorld = worldManager.getLoadedWorld(world).getOrNull(); + if (mvWorld == null) { + issuer.sendMessage("The world '" + world.getName() + "' is not a multiverse world!"); + return; + } + + Player selfOrOther = players.stream() + .filter(p -> !p.equals(issuer.getPlayer())) + .findFirst() + .orElse(issuer.getPlayer()); + if (!permissionsChecker.checkSpawnPermission(issuer.getIssuer(), selfOrOther, mvWorld)) { + issuer.sendMessage("You do not have permission to use this command in this world!"); + return; + } + + if (players.size() == 1) { + handleSingleTeleport(issuer, mvWorld, players.get(0), checkSafety); + } else { + handleMultiTeleport(issuer, mvWorld, players, checkSafety); + } + } + + private void handleSingleTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld, + Player player, boolean checkSafety) { + safetyTeleporter.to(mvWorld.getSpawnLocation()) + .by(issuer) + .checkSafety(checkSafety) + .teleport(player) + .onSuccess(() -> issuer.sendInfo(MVCorei18n.SPAWN_SUCCESS, + Replace.PLAYER.with(player.equals(issuer.getPlayer()) + ? Message.of(MVCorei18n.GENERIC_YOU) + : Message.of(player.getName())), + Replace.WORLD.with(mvWorld.getName()))) + .onFailure(failure -> issuer.sendError(MVCorei18n.SPAWN_FAILED, + Replace.PLAYER.with(player.equals(issuer.getPlayer()) + ? Message.of(MVCorei18n.GENERIC_YOU) + : Message.of(player.getName())), + Replace.WORLD.with(mvWorld.getName()), + Replace.REASON.with(failure.getFailureMessage()))); + } + + private void handleMultiTeleport(MVCommandIssuer issuer, LoadedMultiverseWorld mvWorld, + List players, boolean checkSafety) { + safetyTeleporter.to(mvWorld.getSpawnLocation()) + .by(issuer) + .checkSafety(checkSafety) + .teleport(players) + .thenAccept(attempts -> { + int successCount = 0; + Map failures = new EnumMap<>(TeleportFailureReason.class); + for (var attempt : attempts) { + if (attempt.isSuccess()) { + successCount++; + } else { + failures.compute(attempt.getFailureReason(), + (reason, count) -> count == null ? 1 : count + 1); + } + } + if (successCount > 0) { + issuer.sendInfo(MVCorei18n.SPAWN_SUCCESS, + // TODO should use {count} instead of {player} most likely + Replace.PLAYER.with(successCount + " players"), + Replace.WORLD.with(mvWorld.getName())); + } else { + for (var entry : failures.entrySet()) { + issuer.sendError(MVCorei18n.SPAWN_FAILED, + // TODO should use {count} instead of {player} most likely + Replace.PLAYER.with(entry.getValue() + " players"), + Replace.WORLD.with(mvWorld.getName()), + Replace.REASON.with(entry.getKey().getMessageKey())); + } + } + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java new file mode 100644 index 000000000..c8f8b06be --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/TeleportCommand.java @@ -0,0 +1,176 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.TeleportFailureReason; + +@Service +@CommandAlias("mv") +final class TeleportCommand extends CoreCommand { + + private final MVCoreConfig config; + private final CorePermissionsChecker permissionsChecker; + private final AsyncSafetyTeleporter safetyTeleporter; + + private final CommandFlag unsafeFlag = flag(CommandFlag.builder("--unsafe") + .addAlias("-u") + .build()); + + @Inject + TeleportCommand( + @NotNull MVCommandManager commandManager, + @NotNull MVCoreConfig config, + @NotNull CorePermissionsChecker permissionsChecker, + @NotNull AsyncSafetyTeleporter safetyTeleporter) { + super(commandManager); + this.config = config; + this.permissionsChecker = permissionsChecker; + this.safetyTeleporter = safetyTeleporter; + } + + @CommandAlias("mvtp") + @Subcommand("teleport|tp") + @CommandPermission("@mvteleport") + @CommandCompletion( + "@destinations:playerOnly|@playersarray:checkPermissions=@mvteleportother " + + "@destinations:othersOnly|@flags:groupName=mvteleportcommand,resolveUntil=arg2 " + + "@flags:groupName=mvteleportcommand") + @Syntax("[player] [--unsafe]") + @Description("{@@mv-core.teleport.description}") + void onTeleportCommand( + MVCommandIssuer issuer, + + @Flags("resolve=issuerAware") + @Syntax("[player]") + @Description("{@@mv-core.teleport.player.description}") + Player[] players, + + @Syntax("") + @Description("{@@mv-core.teleport.destination.description}") + DestinationInstance destination, + + @Optional + @Syntax("[--unsafe]") + @Description("") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + if (players.length == 1) { + teleportSinglePlayer(issuer, players[0], destination, parsedFlags); + } else if (players.length > config.getConcurrentTeleportLimit()) { + issuer.sendError(MVCorei18n.TELEPORT_TOOMANYPLAYERS, + Replace.COUNT.with(config.getConcurrentTeleportLimit())); + } else { + teleportMultiplePlayers(issuer, players, destination, parsedFlags); + } + } + + private void teleportSinglePlayer(MVCommandIssuer issuer, Player player, + DestinationInstance destination, + ParsedCommandFlags parsedFlags) { + if (!permissionsChecker.checkTeleportPermissions(issuer.getIssuer(), player, destination)) { + // TODO localize + issuer.sendMessage(player == issuer.getPlayer() + ? "You do not have permission to teleport yourself!" + : "You do not have permission to teleport other players!"); + return; + } + + safetyTeleporter.to(destination) + .by(issuer) + .checkSafety(!parsedFlags.hasFlag(unsafeFlag) && destination.checkTeleportSafety()) + .teleport(player) + .onSuccess(() -> issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, + Replace.PLAYER.with(getYouOrName(issuer, player)), + Replace.DESTINATION.with(destination.toString()))) + .onFailure(failure -> issuer.sendError(MVCorei18n.TELEPORT_FAILED, + Replace.PLAYER.with(getYouOrName(issuer, player)), + Replace.DESTINATION.with(destination.toString()), + Replace.REASON.with(failure.getFailureMessage()))); + } + + private Message getYouOrName(MVCommandIssuer issuer, Player player) { + return player == issuer.getPlayer() ? Message.of(MVCorei18n.GENERIC_YOU) : Message.of(player.getName()); + } + + private void teleportMultiplePlayers(MVCommandIssuer issuer, Player[] players, + DestinationInstance destination, + ParsedCommandFlags parsedFlags) { + var selfPlayer = Arrays.stream(players).filter(p -> p == issuer.getPlayer()).findFirst(); + var otherPlayer = Arrays.stream(players).filter(p -> p != issuer.getPlayer()).findFirst(); + if (selfPlayer.isPresent() + && !permissionsChecker.checkTeleportPermissions(issuer.getIssuer(), selfPlayer.get(), destination)) { + // TODO localize + issuer.sendMessage("You do not have permission to teleport yourself!"); + return; + } + if (otherPlayer.isPresent() + && !permissionsChecker.checkTeleportPermissions(issuer.getIssuer(), otherPlayer.get(), destination)) { + // TODO localize + issuer.sendMessage("You do not have permission to teleport other players!"); + return; + } + safetyTeleporter.to(destination) + .by(issuer) + .checkSafety(!parsedFlags.hasFlag(unsafeFlag) && destination.checkTeleportSafety()) + .teleport(List.of(players)) + .thenAccept(attempts -> { + int successCount = 0; + Map failures = new EnumMap<>(TeleportFailureReason.class); + for (var attempt : attempts) { + if (attempt.isSuccess()) { + successCount++; + } else { + failures.compute(attempt.getFailureReason(), + (reason, count) -> count == null ? 1 : count + 1); + } + } + if (successCount > 0) { + Logging.finer("Teleported %s players to %s", successCount, destination); + issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS, + // TODO should use {count} instead of {player} most likely + Replace.PLAYER.with(successCount + " players"), + Replace.DESTINATION.with(destination.toString())); + } + if (!failures.isEmpty()) { + for (var entry : failures.entrySet()) { + Logging.finer("Failed to teleport %s players to %s: %s", + entry.getValue(), destination, entry.getKey()); + issuer.sendError(MVCorei18n.TELEPORT_FAILED, + // TODO should use {count} instead of {player} most likely + Replace.PLAYER.with(entry.getValue() + " players"), + Replace.DESTINATION.with(destination.toString()), + Replace.REASON.with(Message.of(entry.getKey(), ""))); + } + } + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/UnloadCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/UnloadCommand.java new file mode 100644 index 000000000..e57ddf493 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/UnloadCommand.java @@ -0,0 +1,94 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.Collections; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.PlayerWorldTeleporter; +import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions; + +@Service +@CommandAlias("mv") +final class UnloadCommand extends CoreCommand { + + private final WorldManager worldManager; + private final PlayerWorldTeleporter playerWorldTeleporter; + + private final CommandFlag removePlayersFlag = flag(CommandFlag.builder("--remove-players") + .addAlias("-r") + .build()); + + private final CommandFlag noSaveFlag = flag(CommandFlag.builder("--no-save") + .addAlias("-n") + .build()); + + @Inject + UnloadCommand( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull PlayerWorldTeleporter playerWorldTeleporter) { + super(commandManager); + this.worldManager = worldManager; + this.playerWorldTeleporter = playerWorldTeleporter; + } + + @CommandAlias("mvunload") + @Subcommand("unload") + @CommandPermission("multiverse.core.unload") + @CommandCompletion("@mvworlds @flags:groupName=mvunloadcommand") + @Syntax("") + @Description("{@@mv-core.unload.description}") + void onUnloadCommand( + MVCommandIssuer issuer, + + @Syntax("") + @Description("{@@mv-core.unload.world.description}") + LoadedMultiverseWorld world, + + @Optional + @Syntax("[--remove-players] [--no-save]") + @Description("{@@mv-core.gamerules.description.page}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING, Replace.WORLD.with(world.getAlias())); + + var future = parsedFlags.hasFlag(removePlayersFlag) + ? playerWorldTeleporter.removeFromWorld(world) + : Async.completedFuture(Collections.emptyList()); + + future.thenRun(() -> doWorldUnloading(issuer, world, parsedFlags)); + } + + private void doWorldUnloading(MVCommandIssuer issuer, LoadedMultiverseWorld world, ParsedCommandFlags parsedFlags) { + UnloadWorldOptions unloadWorldOptions = UnloadWorldOptions.world(world) + .saveBukkitWorld(!parsedFlags.hasFlag(noSaveFlag)); + worldManager.unloadWorld(unloadWorldOptions) + .onSuccess(loadedWorld -> { + Logging.fine("World unload success: " + loadedWorld); + issuer.sendInfo(MVCorei18n.UNLOAD_SUCCESS, Replace.WORLD.with(loadedWorld.getName())); + }).onFailure(failure -> { + Logging.fine("World unload failure: " + failure); + issuer.sendError(failure.getFailureMessage()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/UsageCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/UsageCommand.java new file mode 100644 index 000000000..9206ea6e3 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/UsageCommand.java @@ -0,0 +1,39 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.CommandHelp; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.HelpCommand; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; + +@Service +@CommandAlias("mv") +final class UsageCommand extends CoreCommand { + + @Inject + UsageCommand(@NotNull MVCommandManager commandManager) { + super(commandManager); + } + + @HelpCommand + @Subcommand("help") + @CommandPermission("multiverse.core.help") + @CommandCompletion("@commands:mv") + @Syntax("[filter] [page]") + @Description("{@@mv-core.usage.description}") + void onUsageCommand(CommandHelp help) { + if (help.getIssuer().isPlayer()) { + // Prevent flooding the chat + help.setPerPage(4); + } + this.commandManager.showUsage(help); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/VersionCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/VersionCommand.java new file mode 100644 index 000000000..12336a61e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/VersionCommand.java @@ -0,0 +1,40 @@ +package org.mvplugins.multiverse.core.commands; + +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.MessageType; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.locale.MVCorei18n; + +@Service +@CommandAlias("mv") +final class VersionCommand extends CoreCommand { + + private final MultiverseCore plugin; + + @Inject + VersionCommand(@NotNull MVCommandManager commandManager, MultiverseCore plugin) { + super(commandManager); + this.plugin = plugin; + } + + @CommandAlias("mvversion") + @Subcommand("version") + @CommandPermission("multiverse.core.version") + @Description("{@@mv-core.version.description}") + void versionCommand(BukkitCommandIssuer issuer) { + issuer.sendMessage(MessageType.INFO, MVCorei18n.VERSION_MV, "{version}", plugin.getDescription().getVersion()); + issuer.sendMessage(MessageType.INFO, MVCorei18n.VERSION_AUTHORS, + "{authors}", String.join(", ", plugin.getDescription().getAuthors())); + // An in joke I don't get... + issuer.sendMessage(MessageType.INFO, MVCorei18n.VERSION_SECRETCODE); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/WhoCommand.java b/src/main/java/org/mvplugins/multiverse/core/commands/WhoCommand.java new file mode 100644 index 000000000..3585b3cb1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/WhoCommand.java @@ -0,0 +1,142 @@ +package org.mvplugins.multiverse.core.commands; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; +import org.mvplugins.multiverse.core.commandtools.flags.FilterCommandFlag; +import org.mvplugins.multiverse.core.commandtools.flags.PageCommandFlag; +import org.mvplugins.multiverse.core.display.ContentDisplay; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.handlers.PagedSendHandler; +import org.mvplugins.multiverse.core.display.parsers.MapContentProvider; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +@CommandAlias("mv") +final class WhoCommand extends CoreCommand { + + private final CommandValueFlag pageFlag = flag(PageCommandFlag.create()); + + private final CommandValueFlag filterFlag = flag(FilterCommandFlag.create()); + + private final WorldManager worldManager; + + @Inject + WhoCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) { + super(commandManager); + this.worldManager = worldManager; + } + + @CommandAlias("mvwhoall") + @Subcommand("whoall") + @CommandPermission("multiverse.core.list.who.all") + @CommandCompletion("@flags:groupName=mvwhocommand") + @Syntax("[--page ] [--filter ]") + @Description("{@@mv-core.who.all.description}") + void onWhoAllCommand( + MVCommandIssuer issuer, + + @Optional + @Syntax("[--page ] [--filter ]") + @Description("{@@mv-core.who.flags.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + // Send the display + getListDisplay(worldManager.getLoadedWorlds(), + parsedFlags.flagValue(pageFlag, 1), + parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()), + true) + .send(issuer); + + } + + @CommandAlias("mvwho|mvw") + @Subcommand("who") + @CommandPermission("multiverse.core.list.who") + @CommandCompletion("@mvworlds:scope=both @flags:groupName=mvwhocommand") + @Syntax(" [--page ] [--filter ]") + @Description("{@@mv-core.who.description}") + void onWhoCommand( + MVCommandIssuer issuer, + @Flags("resolve=issuerAware") + @Optional + @Syntax("") + @Description("{@@mv-core.who.world.description}") + LoadedMultiverseWorld inputtedWorld, + + @Optional + @Syntax("[--page ] [--filter ]") + @Description("{@@mv-core.who.flags.description}") + String[] flags) { + ParsedCommandFlags parsedFlags = parseFlags(flags); + + // Send the display + getListDisplay(inputtedWorld, + parsedFlags.flagValue(pageFlag, 1), + parsedFlags.flagValue(filterFlag, DefaultContentFilter.get()), + false) + .send(issuer); + } + + private String phrasePlayerList(List players) { + return players.stream().map(Player::getName).collect(Collectors.joining(", ")); + } + + private ContentDisplay getListDisplay(LoadedMultiverseWorld world, int page, + ContentFilter filter, boolean ignoreEmptyWorlds) { + Collection listingWorlds = new ArrayList<>(); + listingWorlds.add(world); + return getListDisplay(listingWorlds, page, filter, ignoreEmptyWorlds); + } + + private ContentDisplay getListDisplay(Collection worlds, int page, + ContentFilter filter, boolean ignoreEmptyWorlds) { + Map outMap = new HashMap<>(); + + // Add all the worlds to our hashmap + for (LoadedMultiverseWorld world : worlds) { + @Nullable List players = world.getPlayers().getOrNull(); + + if (players != null && !players.isEmpty()) { + outMap.put(world.getAlias(), phrasePlayerList(players)); + } else if (!ignoreEmptyWorlds) { + // If the world has 0 players in it, say that it is empty + outMap.put(world.getAlias(), ChatColor.RED + "Empty"); + } + } + + return ContentDisplay.create() + .addContent(MapContentProvider.forContent(outMap)) + .withSendHandler(PagedSendHandler.create() + .withHeader("%s====[ Multiverse World Players List ]====", ChatColor.AQUA) + .doPagination(true) + .withTargetPage(page) + .withFilter(filter)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commands/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commands/package-info.java new file mode 100644 index 000000000..351427b4e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commands/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the commands for Multiverse-Core. + */ +package org.mvplugins.multiverse.core.commands; diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/ConfirmMode.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/ConfirmMode.java new file mode 100644 index 000000000..ec2e886e9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/ConfirmMode.java @@ -0,0 +1,31 @@ +package org.mvplugins.multiverse.core.commandtools; + +/** + * Sets whether the command executor needs to confirm before executing a command. + */ +public enum ConfirmMode { + /** + * Every command executor needs to confirm before executing a command. + */ + ENABLE, + + /** + * Only players need to confirm before executing a command. Command blocks and console will not need to confirm. + */ + PLAYER_ONLY, + + /** + * Everyone except command blocks will not need to confirm before executing a command. + */ + DISABLE_COMMAND_BLOCKS, + + /** + * Everyone except console will not need to confirm before executing a command. + */ + DISABLE_CONSOLE, + + /** + * Everyone will not need to confirm before executing a command. + */ + DISABLE, +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandCompletions.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandCompletions.java new file mode 100644 index 000000000..2935bbace --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandCompletions.java @@ -0,0 +1,304 @@ +package org.mvplugins.multiverse.core.commandtools; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import co.aikar.commands.BukkitCommandCompletionContext; +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.PaperCommandCompletions; +import co.aikar.commands.RegisteredCommand; +import co.aikar.commands.RootCommand; +import com.dumptruckman.minecraft.util.Logging; +import com.google.common.collect.Sets; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.configuration.functions.DefaultSuggesterProvider; +import org.mvplugins.multiverse.core.configuration.handle.PropertyModifyAction; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.destination.core.WorldDestination; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +public class MVCommandCompletions extends PaperCommandCompletions { + + private final MVCommandManager commandManager; + private final WorldManager worldManager; + private final DestinationsProvider destinationsProvider; + private final MVCoreConfig config; + private final CorePermissionsChecker corePermissionsChecker; + + @Inject + MVCommandCompletions( + @NotNull MVCommandManager mvCommandManager, + @NotNull WorldManager worldManager, + @NotNull DestinationsProvider destinationsProvider, + @NotNull MVCoreConfig config, + @NotNull CorePermissionsChecker corePermissionsChecker) { + super(mvCommandManager); + this.commandManager = mvCommandManager; + this.worldManager = worldManager; + this.destinationsProvider = destinationsProvider; + this.config = config; + this.corePermissionsChecker = corePermissionsChecker; + + registerAsyncCompletion("commands", this::suggestCommands); + registerAsyncCompletion("destinations", this::suggestDestinations); + registerStaticCompletion("difficulties", suggestEnums(Difficulty.class)); + registerStaticCompletion("environments", suggestEnums(World.Environment.class)); + registerAsyncCompletion("flags", this::suggestFlags); + registerStaticCompletion("gamemodes", suggestEnums(GameMode.class)); + registerStaticCompletion("gamerules", this::suggestGamerules); + registerAsyncCompletion("gamerulesvalues", this::suggestGamerulesValues); + registerStaticCompletion("mvconfigs", config.getStringPropertyHandle().getAllPropertyNames()); + registerAsyncCompletion("mvconfigvalues", this::suggestMVConfigValues); + registerAsyncCompletion("mvworlds", this::suggestMVWorlds); + registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName); + registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue); + registerAsyncCompletion("playersarray", this::suggestPlayersArray); + registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class)); + + setDefaultCompletion("destinations", DestinationInstance.class); + setDefaultCompletion("difficulties", Difficulty.class); + setDefaultCompletion("environments", World.Environment.class); + setDefaultCompletion("flags", String[].class); + setDefaultCompletion("gamemodes", GameMode.class); + setDefaultCompletion("gamerules", GameRule.class); + setDefaultCompletion("mvworlds", LoadedMultiverseWorld.class); + } + + @Override + public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) { + return super.registerCompletion(id, context -> + completeWithPreconditions(context, handler)); + } + + @Override + public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler handler) { + return super.registerAsyncCompletion(id, context -> + completeWithPreconditions(context, handler)); + } + + private Collection completeWithPreconditions( + BukkitCommandCompletionContext context, + CommandCompletionHandler handler) { + if (context.hasConfig("playerOnly") && !context.getIssuer().isPlayer()) { + return Collections.emptyList(); + } + if (context.hasConfig("resolveUntil")) { + if (!Try.run(() -> context.getContextValueByName(Object.class, context.getConfig("resolveUntil"))).isSuccess()) { + return Collections.emptyList(); + } + } + if (context.hasConfig("checkPermissions")) { + for (String permission : context.getConfig("checkPermissions").split(";")) { + if (!commandManager.getCommandPermissions().hasPermission(context.getIssuer(), permission)) { + return Collections.emptyList(); + } + } + } + return handler.getCompletions(context); + } + + /** + * Shortcut to suggest enums values + * + * @param enumClass The enum class with values + * @return A collection of possible string values + * @param The enum type + */ + public > Collection suggestEnums(Class enumClass) { + return EnumSet.allOf(enumClass).stream() + .map(Enum::name) + .map(String::toLowerCase) + .toList(); + } + + private Collection suggestCommands(BukkitCommandCompletionContext context) { + String rootCmdName = context.getConfig(); + if (rootCmdName == null) { + return Collections.emptyList(); + } + + RootCommand rootCommand = this.commandManager.getRegisteredRootCommands().stream() + .unordered() + .filter(c -> c.getCommandName().equals(rootCmdName)) + .findFirst() + .orElse(null); + + if (rootCommand == null) { + return Collections.emptyList(); + } + + return rootCommand.getSubCommands().entries().stream() + .filter(entry -> checkPerms(context.getIssuer(), entry.getValue())) + .map(Map.Entry::getKey) + .filter(cmdName -> !cmdName.startsWith("__")) + .collect(Collectors.toList()); + } + + private boolean checkPerms(CommandIssuer issuer, RegisteredCommand command) { + return this.commandManager.hasPermission(issuer, command.getRequiredPermissions()); + } + + private Collection suggestDestinations(BukkitCommandCompletionContext context) { + return Try.of(() -> context.getContextValue(Player[].class)) + .map(players -> { + Player player = Arrays.stream(players) + .filter(p -> !Objects.equals(p, context.getPlayer())) + .findFirst() + .orElse(context.getPlayer()); + if (player == null) { + // Most likely console did not specify a player + return Collections.emptyList(); + } + if (context.hasConfig("othersOnly") && player.equals(context.getPlayer())) { + return Collections.emptyList(); + } + return suggestDestinationsWithPerms(context.getIssuer().getIssuer(), player, context.getInput()); + }) + .getOrElse(Collections.emptyList()); + } + + private Collection suggestDestinationsWithPerms(CommandSender teleporter, Player teleportee, String deststring) { + return destinationsProvider.getDestinations().stream() + .filter(destination -> corePermissionsChecker.hasDestinationPermission(teleporter, teleportee, destination)) + .flatMap(destination -> destination.suggestDestinations(teleporter, deststring).stream() + .filter(packet -> corePermissionsChecker.hasFinerDestinationPermission( + teleporter, teleportee, destination, packet.finerPermissionSuffix())) + .map(packet -> destination instanceof WorldDestination + ? packet.destinationString() + : destination.getIdentifier() + ":" + packet.destinationString())) + .toList(); + } + + private Collection suggestFlags(@NotNull BukkitCommandCompletionContext context) { + String groupName = context.getConfig("groupName", ""); + + return Try.of(() -> context.getContextValue(String[].class)) + .map(flags -> commandManager.getFlagsManager().suggest(groupName, flags)) + .getOrElse(Collections.emptyList()); + } + + private Collection suggestGamerules() { + return Arrays.stream(GameRule.values()).map(GameRule::getName).collect(Collectors.toList()); + } + + private Collection suggestGamerulesValues(BukkitCommandCompletionContext context) { + return Try.of(() -> context.getContextValue(GameRule.class)) + // Just use our suggester from configuration lib since gamerules are only boolean or int + .mapTry(gamerule -> DefaultSuggesterProvider.getDefaultSuggester(gamerule.getType()).suggest(context.getInput())) + .getOrElse(Collections.emptyList()); + } + + private Collection suggestMVConfigValues(BukkitCommandCompletionContext context) { + return Try.of(() -> context.getContextValue(String.class)) + .map(propertyName -> config.getStringPropertyHandle() + .getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET)) + .getOrElse(Collections.emptyList()); + } + + private Collection suggestMVWorlds(BukkitCommandCompletionContext context) { + if (!context.hasConfig("multiple")) { + return getMVWorldNames(context); + } + return addonToCommaSeperated(context.getInput(), getMVWorldNames(context)); + } + + private List getMVWorldNames(BukkitCommandCompletionContext context) { + String scope = context.getConfig("scope", "loaded"); + switch (scope) { + case "both" -> { + return worldManager.getWorlds().stream().map(this::getWorldNameOrAlias).toList(); + } + case "loaded" -> { + return worldManager.getLoadedWorlds() + .stream() + .map(this::getWorldNameOrAlias) + .toList(); + } + case "unloaded" -> { + return worldManager.getUnloadedWorlds().stream() + .map(this::getWorldNameOrAlias) + .toList(); + } + case "potential" -> { + return worldManager.getPotentialWorlds(); + } + } + Logging.severe("Invalid MVWorld scope: " + scope); + return Collections.emptyList(); + } + + private String getWorldNameOrAlias(MultiverseWorld world) { + return config.getResolveAliasName() ? world.getAlias() : world.getName(); + } + + private Collection suggestMVWorldPropsName(BukkitCommandCompletionContext context) { + return Try.of(() -> { + MultiverseWorld world = context.getContextValue(MultiverseWorld.class); + PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class); + return world.getStringPropertyHandle().getModifiablePropertyNames(action); + }).getOrElse(Collections.emptyList()); + } + + private Collection suggestMVWorldPropsValue(BukkitCommandCompletionContext context) { + return Try.of(() -> { + MultiverseWorld world = context.getContextValue(MultiverseWorld.class); + PropertyModifyAction action = context.getContextValue(PropertyModifyAction.class); + String propertyName = context.getContextValue(String.class); + return world.getStringPropertyHandle().getSuggestedPropertyValue(propertyName, context.getInput(), action); + }).getOrElse(Collections.emptyList()); + } + + + private Collection suggestPlayersArray(BukkitCommandCompletionContext context) { + CommandSender sender = context.getSender(); + Validate.notNull(sender, "Sender cannot be null"); + Player senderPlayer = sender instanceof Player ? (Player)sender : null; + List matchedPlayers = new ArrayList<>(); + + for(Player player : Bukkit.getOnlinePlayers()) { + String name = player.getName(); + if ((senderPlayer == null || senderPlayer.canSee(player)) + && (!context.hasConfig("excludeSelf") || !player.equals(senderPlayer))) { + matchedPlayers.add(name); + } + } + return addonToCommaSeperated(context.getInput(), matchedPlayers); + } + + private Collection addonToCommaSeperated(String input, Collection addons) { + int lastComma = input.lastIndexOf(','); + String previousInputs = input.substring(0, lastComma + 1); + Set inputSet = Sets.newHashSet(input.split(",")); + return addons.stream() + .filter(suggestion -> !inputSet.contains(suggestion)) + .map(suggestion -> previousInputs + suggestion) + .toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandConditions.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandConditions.java new file mode 100644 index 000000000..f10ecf17a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandConditions.java @@ -0,0 +1,88 @@ +package org.mvplugins.multiverse.core.commandtools; + +import co.aikar.commands.BukkitCommandExecutionContext; +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.BukkitConditionContext; +import co.aikar.commands.CommandConditions; +import co.aikar.commands.ConditionContext; +import co.aikar.commands.ConditionFailedException; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker; + +public class MVCommandConditions { + static void load( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull WorldNameChecker worldNameChecker) { + MVCommandConditions mvCommandConditions = new MVCommandConditions(commandManager, worldManager, worldNameChecker); + mvCommandConditions.registerConditions(); + } + + private final WorldManager worldManager; + private final MVCommandManager commandManager; + private final WorldNameChecker worldNameChecker; + + private MVCommandConditions( + @NotNull MVCommandManager commandManager, + @NotNull WorldManager worldManager, + @NotNull WorldNameChecker worldNameChecker) { + this.worldManager = worldManager; + this.commandManager = commandManager; + this.worldNameChecker = worldNameChecker; + } + + private void registerConditions() { + CommandConditions conditions + = commandManager.getCommandConditions(); + + conditions.addCondition(String.class, "worldname", this::checkWorldname); + } + + private void checkWorldname( + ConditionContext context, + BukkitCommandExecutionContext executionContext, + String worldName) { + String scope = context.getConfigValue("scope", "loaded"); + + switch (scope) { + // Worlds that are loaded + case "loaded": + if (!this.worldManager.isLoadedWorld(worldName)) { + throw new ConditionFailedException("World with name '" + worldName + "' does not exist or is not loaded!"); + } + break; + // Worlds that are unloaded + case "unloaded": + if (!this.worldManager.isUnloadedWorld(worldName)) { + if (this.worldManager.isLoadedWorld(worldName)) { + throw new ConditionFailedException("World with name '" + worldName + "' is loaded already!"); + } + throw new ConditionFailedException("World with name '" + worldName + "' does not exist!"); + } + break; + // World that are loaded or unloaded + case "both": + if (!this.worldManager.isWorld(worldName)) { + throw new ConditionFailedException("World with name '" + worldName + "' does not exist!"); + } + break; + // World that are does not exist + case "new": + if (this.worldManager.isWorld(worldName)) { + throw new ConditionFailedException("World with name '" + worldName + "' already exists!"); + } + switch (worldNameChecker.checkName(worldName)) { + case INVALID_CHARS -> + throw new ConditionFailedException("World name '" + worldName + "' contains invalid characters!"); + case BLACKLISTED -> + throw new ConditionFailedException("World name '" + worldName + "' is used for critical server operations and is blacklisted!"); + } + break; + // Probably a typo happened here + default: + throw new ConditionFailedException("Unknown scope '" + scope + "'!"); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandContexts.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandContexts.java new file mode 100644 index 000000000..6a895b89c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandContexts.java @@ -0,0 +1,374 @@ +package org.mvplugins.multiverse.core.commandtools; + +import java.util.HashSet; +import java.util.Set; + +import co.aikar.commands.BukkitCommandExecutionContext; +import co.aikar.commands.BukkitCommandIssuer; +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.PaperCommandContexts; +import co.aikar.commands.contexts.ContextResolver; +import com.google.common.base.Strings; +import jakarta.inject.Inject; +import org.bukkit.GameRule; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.commandtools.context.GameRuleValue; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; +import org.mvplugins.multiverse.core.display.filters.RegexContentFilter; +import org.mvplugins.multiverse.core.utils.PlayerFinder; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +public class MVCommandContexts extends PaperCommandContexts { + + private final MVCommandManager mvCommandManager; + private final DestinationsProvider destinationsProvider; + private final WorldManager worldManager; + private final MVCoreConfig config; + + @Inject + MVCommandContexts( + MVCommandManager mvCommandManager, + DestinationsProvider destinationsProvider, + WorldManager worldManager, + MVCoreConfig config) { + super(mvCommandManager); + this.mvCommandManager = mvCommandManager; + this.destinationsProvider = destinationsProvider; + this.worldManager = worldManager; + this.config = config; + + registerIssuerOnlyContext(BukkitCommandIssuer.class, BukkitCommandExecutionContext::getIssuer); + registerIssuerOnlyContext(MVCommandIssuer.class, this::parseMVCommandIssuer); + registerOptionalContext(ContentFilter.class, this::parseContentFilter); + registerContext(DestinationInstance.class, this::parseDestination); + registerContext(GameRule.class, this::parseGameRule); + registerContext(GameRuleValue.class, this::parseGameRuleValue); + registerIssuerAwareContext(LoadedMultiverseWorld.class, this::parseLoadedMultiverseWorld); + registerIssuerAwareContext(LoadedMultiverseWorld[].class, this::parseLoadedMultiverseWorldArray); + registerIssuerAwareContext(MultiverseWorld.class, this::parseMultiverseWorld); + registerIssuerAwareContext(Player.class, this::parsePlayer); + registerIssuerAwareContext(Player[].class, this::parsePlayerArray); + } + + private MVCommandIssuer parseMVCommandIssuer(BukkitCommandExecutionContext context) { + if (context.getIssuer() instanceof MVCommandIssuer) { + return (MVCommandIssuer) context.getIssuer(); + } + return mvCommandManager.getCommandIssuer(context.getSender()); + } + + private ContentFilter parseContentFilter(BukkitCommandExecutionContext context) { + if (Strings.isNullOrEmpty(context.getFirstArg())) { + return DefaultContentFilter.get(); + } + String filterString = context.popFirstArg(); + return RegexContentFilter.fromString(filterString); + } + + private DestinationInstance parseDestination(BukkitCommandExecutionContext context) { + String destination = context.popFirstArg(); + if (Strings.isNullOrEmpty(destination)) { + throw new InvalidCommandArgument("No destination specified."); + } + + return destinationsProvider.parseDestination(destination) + .getOrElseThrow(() -> new InvalidCommandArgument("The destination " + destination + " is not valid.")); + } + + private GameRule parseGameRule(BukkitCommandExecutionContext context) { + String gameRuleName = context.popFirstArg(); + if (Strings.isNullOrEmpty(gameRuleName)) { + throw new InvalidCommandArgument("No game rule specified."); + } + + GameRule gameRule = GameRule.getByName(gameRuleName); + if (gameRule == null) { + throw new InvalidCommandArgument("The game rule " + gameRuleName + " is not valid."); + } + + return gameRule; + } + + private GameRuleValue parseGameRuleValue(BukkitCommandExecutionContext context) { + GameRule gameRule = (GameRule) context.getResolvedArg(GameRule.class); + if (gameRule == null) { + throw new InvalidCommandArgument("No game rule specified."); + } + String valueString = context.getFirstArg(); + if (Strings.isNullOrEmpty(valueString)) { + throw new InvalidCommandArgument("No game rule value specified."); + } + + ContextResolver resolver = getResolver(gameRule.getType()); + if (resolver == null) { + return new GameRuleValue(valueString); + } + + Object resolvedValue = resolver.getContext(context); + if (resolvedValue == null) { + throw new InvalidCommandArgument("The game rule value " + valueString + " is not valid for game rule " + gameRule.getName() + "."); + } + + return new GameRuleValue(resolvedValue); + } + + private LoadedMultiverseWorld parseLoadedMultiverseWorld(BukkitCommandExecutionContext context) { + String resolve = context.getFlagValue("resolve", ""); + + // Get world based on sender only + if (resolve.equals("issuerOnly")) { + if (context.getIssuer().isPlayer()) { + return worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()).getOrNull(); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("This command can only be used by a player in a Multiverse World."); + } + + String worldName = context.getFirstArg(); + LoadedMultiverseWorld world = getLoadedMultiverseWorld(worldName); + + // Get world based on input, fallback to sender if input is not a world + if (resolve.equals("issuerAware")) { + if (world != null) { + context.popFirstArg(); + return world; + } + if (context.getIssuer().isPlayer()) { + return worldManager.getLoadedWorld(context.getPlayer().getWorld()) + .getOrElseThrow(() -> new InvalidCommandArgument("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world.")); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("World '" + worldName + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."); + } + + // Get world based on input only + if (world != null) { + context.popFirstArg(); + return world; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world."); + } + + private LoadedMultiverseWorld[] parseLoadedMultiverseWorldArray(BukkitCommandExecutionContext context) { + String resolve = context.getFlagValue("resolve", ""); + + // Get world based on sender only + if (resolve.equals("issuerOnly")) { + if (context.getIssuer().isPlayer()) { + LoadedMultiverseWorld playerWorld = worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()) + .getOrElseThrow(() -> new InvalidCommandArgument("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world.")); + return new LoadedMultiverseWorld[]{playerWorld}; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("This command can only be used by a player in a Multiverse World."); + } + + String worldStrings = context.getFirstArg(); + String[] worldNames = worldStrings == null ? new String[0] : worldStrings.split(","); + Set worlds = new HashSet<>(worldNames.length); + for (String worldName : worldNames) { + if ("*".equals(worldName)) { + worlds.addAll(worldManager.getLoadedWorlds()); + break; + } + LoadedMultiverseWorld world = getLoadedMultiverseWorld(worldName); + if (world == null) { + throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world."); + } + worlds.add(world); + } + + // Get world based on input, fallback to sender if input is not a world + if (resolve.equals("issuerAware")) { + if (!worlds.isEmpty()) { + context.popFirstArg(); + return worlds.toArray(new LoadedMultiverseWorld[0]); + } + if (context.getIssuer().isPlayer()) { + LoadedMultiverseWorld playerWorld = worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()) + .getOrElseThrow(() -> new InvalidCommandArgument("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world.")); + return new LoadedMultiverseWorld[]{playerWorld}; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("Worlds '" + worldStrings + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."); + } + + // Get world based on input only + if (!worlds.isEmpty()) { + context.popFirstArg(); + return worlds.toArray(new LoadedMultiverseWorld[0]); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("World " + worldStrings + " is not a loaded multiverse world."); + } + + @Nullable + private LoadedMultiverseWorld getLoadedMultiverseWorld(String worldName) { + return config.getResolveAliasName() + ? worldManager.getLoadedWorldByNameOrAlias(worldName).getOrNull() + : worldManager.getLoadedWorld(worldName).getOrNull(); + } + + private MultiverseWorld parseMultiverseWorld(BukkitCommandExecutionContext context) { + String resolve = context.getFlagValue("resolve", ""); + + // Get world based on sender only + if (resolve.equals("issuerOnly")) { + if (context.getIssuer().isPlayer()) { + return worldManager.getWorld(context.getIssuer().getPlayer().getWorld()).getOrNull(); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("This command can only be used by a player in a Multiverse World."); + } + + String worldName = context.getFirstArg(); + MultiverseWorld world = getMultiverseWorld(worldName); + + // Get world based on input, fallback to sender if input is not a world + if (resolve.equals("issuerAware")) { + if (world != null) { + context.popFirstArg(); + return world; + } + if (context.getIssuer().isPlayer()) { + return worldManager.getWorld(context.getPlayer().getWorld()) + .getOrElseThrow(() -> new InvalidCommandArgument("You are not in a multiverse world. Either specify a multiverse world name or use this command in a multiverse world.")); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("World '" + worldName + "' is not a loaded multiverse world. Remember to specify the world name when using this command in console."); + } + + // Get world based on input only + if (world != null) { + context.popFirstArg(); + return world; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world."); + } + + @Nullable + private MultiverseWorld getMultiverseWorld(String worldName) { + return config.getResolveAliasName() + ? worldManager.getWorldByNameOrAlias(worldName).getOrNull() + : worldManager.getWorld(worldName).getOrNull(); + } + + private Player parsePlayer(BukkitCommandExecutionContext context) { + String resolve = context.getFlagValue("resolve", ""); + + // Get player based on sender only + if (resolve.equals("issuerOnly")) { + if (context.getIssuer().isPlayer()) { + return context.getIssuer().getPlayer(); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("This command can only be used by a player."); + } + + String playerIdentifier = context.getFirstArg(); + Player player = PlayerFinder.get(playerIdentifier, context.getSender()); + + // Get player based on input, fallback to sender if input is not a player + if (resolve.equals("issuerAware")) { + if (player != null) { + context.popFirstArg(); + return player; + } + if (context.getIssuer().isPlayer()) { + return context.getIssuer().getPlayer(); + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("Invalid player: " + playerIdentifier + + ". Either specify an online player or use this command as a player."); + } + + // Get player based on input only + if (player != null) { + context.popFirstArg(); + return player; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("Player " + playerIdentifier + " not found."); + } + + + private Player[] parsePlayerArray(BukkitCommandExecutionContext context) { + String resolve = context.getFlagValue("resolve", ""); + + // Get player based on sender only + if (resolve.equals("issuerOnly")) { + if (context.getIssuer().isPlayer()) { + return new Player[]{context.getIssuer().getPlayer()}; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("This command can only be used by a player."); + } + + String playerIdentifier = context.getFirstArg(); + Player[] players = PlayerFinder.getMulti(playerIdentifier, context.getSender()).toArray(new Player[0]); + + // Get player based on input, fallback to sender if input is not a player + if (resolve.equals("issuerAware")) { + if (players.length > 0) { + context.popFirstArg(); + return players; + } + if (context.getIssuer().isPlayer()) { + return new Player[]{context.getIssuer().getPlayer()}; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("Invalid player: " + playerIdentifier + + ". Either specify an online player or use this command as a player."); + } + + // Get player based on input only + if (players.length > 0) { + context.popFirstArg(); + return players; + } + if (context.isOptional()) { + return null; + } + throw new InvalidCommandArgument("Player " + playerIdentifier + " not found."); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandIssuer.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandIssuer.java new file mode 100644 index 000000000..3dfc97021 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandIssuer.java @@ -0,0 +1,104 @@ +package org.mvplugins.multiverse.core.commandtools; + +import co.aikar.commands.MessageKeys; +import co.aikar.commands.MessageType; +import co.aikar.commands.OpenBukkitCommandIssuer; +import co.aikar.locales.MessageKeyProvider; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; + +public class MVCommandIssuer extends OpenBukkitCommandIssuer { + + private final MVCommandManager commandManager; + + MVCommandIssuer(@NotNull MVCommandManager commandManager, @NotNull CommandSender sender) { + super(commandManager, sender); + this.commandManager = commandManager; + } + + @Override + public MVCommandManager getManager() { + return commandManager; + } + + public void sendError(String message) { + sendMessage(MessageType.INFO, MessageKeys.INFO_MESSAGE, "{message}", message); + } + + public void sendSyntax(String message) { + sendMessage(MessageType.SYNTAX, MessageKeys.INFO_MESSAGE, "{message}", message); + } + + public void sendInfo(String message) { + sendMessage(MessageType.INFO, MessageKeys.INFO_MESSAGE, "{message}", message); + } + + public void sendMessage(MessageType messageType, String message) { + var formatter = getManager().getFormat(messageType); + if (formatter != null) { + sendMessage(formatter.format(message)); + } else { + sendMessage(message); + } + } + + public void sendError(MessageKeyProvider key) { + sendMessage(MessageType.ERROR, key, new String[0]); + } + + public void sendSyntax(MessageKeyProvider key) { + sendMessage(MessageType.SYNTAX, key, new String[0]); + } + + public void sendInfo(MessageKeyProvider key) { + sendMessage(MessageType.INFO, key, new String[0]); + } + + public void sendError(MessageKeyProvider key, MessageReplacement... replacements) { + sendMessage(MessageType.ERROR, key, replacements); + } + + public void sendSyntax(MessageKeyProvider key, MessageReplacement... replacements) { + sendMessage(MessageType.SYNTAX, key, replacements); + } + + public void sendInfo(MessageKeyProvider key, MessageReplacement... replacements) { + sendMessage(MessageType.INFO, key, replacements); + } + + private void sendMessage(MessageType messageType, MessageKeyProvider key, MessageReplacement... replacements) { + sendMessage(messageType, Message.of( + key, + "{error_key: %s}".formatted(key.getMessageKey().getKey()), + replacements)); + } + + public void sendError(Message message) { + sendMessage(MessageType.ERROR, message); + } + + public void sendSyntax(Message message) { + sendMessage(MessageType.SYNTAX, message); + } + + public void sendInfo(Message message) { + sendMessage(MessageType.INFO, message); + } + + public void sendMessage(MessageType messageType, Message message) { + if (message instanceof MessageKeyProvider) { + sendMessage(messageType, (MessageKeyProvider) message, + message.getReplacements(getManager().getLocales(), this)); + } else { + var formatter = getManager().getFormat(messageType); + if (formatter != null) { + sendMessage(formatter.format(message.formatted())); + } else { + sendMessage(message.formatted()); + } + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandManager.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandManager.java new file mode 100644 index 000000000..582186780 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandManager.java @@ -0,0 +1,149 @@ +package org.mvplugins.multiverse.core.commandtools; + +import java.util.List; + +import co.aikar.commands.CommandCompletions; +import co.aikar.commands.CommandContexts; +import co.aikar.commands.CommandHelp; +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.HelpEntry; +import co.aikar.commands.PaperCommandManager; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlagsManager; +import org.mvplugins.multiverse.core.commandtools.queue.CommandQueueManager; +import org.mvplugins.multiverse.core.locale.PluginLocales; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker; + +/** + * Main class to manage permissions. + */ +@Service +public class MVCommandManager extends PaperCommandManager { + + private final CommandFlagsManager flagsManager; + private final CommandQueueManager commandQueueManager; + private final Provider commandContextsProvider; + private final Provider commandCompletionsProvider; + private final MVCommandPermissions commandPermissions; + private final PluginLocales pluginLocales; + + @Inject + MVCommandManager( + @NotNull MultiverseCore plugin, + @NotNull CommandFlagsManager flagsManager, + @NotNull CommandQueueManager commandQueueManager, + @NotNull Provider commandContextsProvider, + @NotNull Provider commandCompletionsProvider, + @NotNull WorldManager worldManager, + @NotNull WorldNameChecker worldNameChecker, + @NotNull MVCommandPermissions commandPermissions) { + super(plugin); + this.flagsManager = flagsManager; + this.commandQueueManager = commandQueueManager; + this.commandContextsProvider = commandContextsProvider; + this.commandCompletionsProvider = commandCompletionsProvider; + this.commandPermissions = commandPermissions; + this.pluginLocales = new PluginLocales(this); + this.locales = this.pluginLocales; + this.pluginLocales.loadLanguages(); + + MVCommandConditions.load(this, worldManager, worldNameChecker); + this.enableUnstableAPI("help"); + } + + /** + * {@inheritDoc} + */ + @Override + public PluginLocales getLocales() { + return this.pluginLocales; + } + + /** + * Gets class responsible for flag handling. + * + * @return A not-null {@link CommandFlagsManager}. + */ + public synchronized @NotNull CommandFlagsManager getFlagsManager() { + return flagsManager; + } + + /** + * Manager for command that requires /mv confirm before execution. + * + * @return A non-null {@link CommandQueueManager}. + */ + public synchronized @NotNull CommandQueueManager getCommandQueueManager() { + return commandQueueManager; + } + + public synchronized @NotNull MVCommandPermissions getCommandPermissions() { + return commandPermissions; + } + + /** + * Gets class responsible for parsing string args into objects. + * + * @return A not-null {@link CommandContexts}. + */ + @Override + public synchronized @NotNull MVCommandContexts getCommandContexts() { + if (this.contexts == null) { + this.contexts = commandContextsProvider.get(); + } + return (MVCommandContexts) this.contexts; + } + + /** + * Gets class responsible for tab-completion handling. + * + * @return A not-null {@link CommandCompletions}. + */ + @Override + public synchronized @NotNull MVCommandCompletions getCommandCompletions() { + if (this.completions == null) { + this.completions = commandCompletionsProvider.get(); + } + return (MVCommandCompletions) this.completions; + } + + @Override + public boolean hasPermission(CommandIssuer issuer, String permission) { + return commandPermissions.hasPermission(issuer, permission); + } + + /** + * Standardise usage command formatting for all mv modules. + * + * @param help The target {@link CommandHelp}. + */ + public void showUsage(@NotNull CommandHelp help) { + List entries = help.getHelpEntries(); + if (entries.size() == 1) { + getHelpFormatter().showDetailedHelp(help, entries.get(0)); + return; + } + help.showHelp(); + } + + public @NotNull MVCommandIssuer getConsoleCommandIssuer() { + return getCommandIssuer(Bukkit.getConsoleSender()); + } + + @Override + public @NotNull MVCommandIssuer getCommandIssuer(Object issuer) { + if (!(issuer instanceof CommandSender)) { + throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer."); + } else { + return new MVCommandIssuer(this, (CommandSender)issuer); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandPermissions.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandPermissions.java new file mode 100644 index 000000000..da4b9a61d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MVCommandPermissions.java @@ -0,0 +1,60 @@ +package org.mvplugins.multiverse.core.commandtools; + +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.annotation.CommandPermission; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.permissions.PermissionUtils; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Predicate; + +/** + * Maps permission checking to custom logic for commands, to allow more complex permission checking. + */ +@Service +public class MVCommandPermissions { + private final Map> permissionsCheckMap; + + @Inject + MVCommandPermissions(@NotNull CorePermissionsChecker permissionsChecker) { + this.permissionsCheckMap = new HashMap<>(); + + registerPermissionChecker("mvteleport", issuer -> permissionsChecker.hasAnyTeleportPermission(issuer.getIssuer())); + registerPermissionChecker("mvteleportother", issuer -> permissionsChecker.hasTeleportOtherPermission(issuer.getIssuer())); + registerPermissionChecker("mvspawn", issuer -> permissionsChecker.hasMinimumSpawnPermission(issuer.getIssuer())); + registerPermissionChecker("mvspawnother", issuer -> permissionsChecker.hasSpawnOtherPermission(issuer.getIssuer())); + } + + /** + * Registers a custom permission checker callback. Use `@id-name` in {@link CommandPermission} decorator to use + * the callback instead of the default permission string checking. + * + * @param id The permission id + * @param checker The permission checker callback + */ + public void registerPermissionChecker(String id, Predicate checker) { + permissionsCheckMap.put(prepareId(id), checker); + } + + private static @NotNull String prepareId(String id) { + return (id.startsWith("@") ? "" : "@") + id.toLowerCase(Locale.ENGLISH); + } + + /** + * Check if the issuer has the given permission. + * @param issuer The issuer + * @param permission The permission to check + * @return True if the issuer has the permission + */ + boolean hasPermission(CommandIssuer issuer, String permission) { + return Option.of(permissionsCheckMap.get(permission)) + .map(checker -> checker.test(issuer)) + .getOrElse(() -> PermissionUtils.hasPermission(issuer.getIssuer(), permission)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/MultiverseCommand.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/MultiverseCommand.java new file mode 100644 index 000000000..88f014fd1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/MultiverseCommand.java @@ -0,0 +1,80 @@ +package org.mvplugins.multiverse.core.commandtools; + +import co.aikar.commands.BaseCommand; +import com.dumptruckman.minecraft.util.Logging; +import jakarta.annotation.PostConstruct; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Contract; + +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlag; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlagGroup; +import org.mvplugins.multiverse.core.commandtools.flag.CommandFlagsManager; +import org.mvplugins.multiverse.core.commandtools.flag.ParsedCommandFlags; + +/** + * Base class for all Multiverse commands. + */ +@Contract +public abstract class MultiverseCommand extends BaseCommand { + + /** + * The command manager with multiverse specific methods. + */ + protected final MVCommandManager commandManager; + /** + * The flags manager for the above command manager. + */ + protected final CommandFlagsManager flagsManager; + private final String flagGroupPrefix; + private String flagGroupName; + private CommandFlagGroup.Builder flagGroupBuilder; + + protected MultiverseCommand(@NotNull MVCommandManager commandManager, @NotNull String flagGroupPrefix) { + this.commandManager = commandManager; + this.flagsManager = commandManager.getFlagsManager(); + this.flagGroupPrefix = flagGroupPrefix; + } + + @PostConstruct + private void postConstruct() { + if (flagGroupBuilder != null) { + registerFlagGroup(flagGroupBuilder.build()); + flagGroupBuilder = null; + } + } + + private void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) { + if (flagGroupName != null) { + throw new IllegalStateException("Flag group already registered! (name: " + flagGroupName + ")"); + } + flagsManager.registerFlagGroup(flagGroup); + flagGroupName = flagGroup.getName(); + Logging.finest("Registered flag group: " + flagGroupName); + } + + /** + * Add a new flag to the flag builder. + * + * @param flag The flag to add. + * @param The type of the flag. + * @return The flag. + */ + protected T flag(T flag) { + if (flagGroupBuilder == null) { + flagGroupBuilder = CommandFlagGroup.builder(flagGroupPrefix + getClass().getSimpleName().toLowerCase()); + } + flagGroupBuilder.add(flag); + Logging.finest("Registered flag: " + flag); + return flag; + } + + /** + * Parses flags. + * + * @param flags The raw string array to parse into flags. + * @return The parsed flags. + */ + protected @NotNull ParsedCommandFlags parseFlags(@NotNull String[] flags) { + return flagsManager.parse(flagGroupName, flags); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/context/GameRuleValue.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/context/GameRuleValue.java new file mode 100644 index 000000000..79a24cdfe --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/context/GameRuleValue.java @@ -0,0 +1,7 @@ +package org.mvplugins.multiverse.core.commandtools.context; + +/** + * Simple wrapper for game rule value, as they may be different types. + */ +public record GameRuleValue(Object value) { +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/context/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/context/package-info.java new file mode 100644 index 000000000..9d89f5d06 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/context/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.commandtools.context; diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlag.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlag.java new file mode 100644 index 000000000..5da125612 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlag.java @@ -0,0 +1,105 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a flag. + */ +public class CommandFlag { + /** + * A builder for a flag. + * + * @param key The key for the new flag. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String key){ + return new Builder<>(key); + } + + private final String key; + private final List aliases; + + /** + * Creates a new flag. + * + * @param key The key for the new flag. + * @param aliases The aliases that also refer to this flag. + */ + protected CommandFlag(@NotNull String key, @NotNull List aliases) { + this.key = key; + this.aliases = aliases; + } + + /** + * Get the key of this flag. + * + * @return The key of this flag. + */ + public @NotNull String getKey() { + return key; + } + + /** + * Get the aliases of this flag. + * + * @return The aliases of this flag. + */ + public @NotNull List getAliases() { + return aliases; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Builder{" + + "key='" + key + '\'' + + ", aliases=" + aliases + + '}'; + } + + /** + * A builder for a flag. + * + * @param The type of the builder. + */ + public static class Builder> { + protected final String key; + protected final List aliases; + + /** + * Create a new builder. + * + * @param key The key for the new flag. + */ + public Builder(@NotNull String key) { + this.key = key; + aliases = new ArrayList<>(); + } + + /** + * Add one or more alias to the flag. + * + * @param alias The alias to add. + * @return The builder. + */ + public @NotNull S addAlias(@NotNull String...alias){ + Collections.addAll(this.aliases, alias); + return (S) this; + } + + /** + * Build the flag. + * + * @return The flag. + */ + public @NotNull CommandFlag build(){ + return new CommandFlag(key, aliases); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagGroup.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagGroup.java new file mode 100644 index 000000000..0e112be15 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagGroup.java @@ -0,0 +1,129 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A group of flags. + */ +public class CommandFlagGroup { + /** + * A builder for a flag group. + * + * @param name The name of the group. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String name) { + return new Builder(name); + } + + private final String name; + private final List keys; + private final Map keysFlagMap; + + /** + * Creates a new flag group. + * + * @param builder The builder. + */ + protected CommandFlagGroup(@NotNull Builder builder) { + name = builder.name; + keys = builder.keys; + keysFlagMap = builder.keysFlagMap; + } + + /** + * Get the name of this group. + * + * @return The name of this group. + */ + public @NotNull String getName() { + return name; + } + + /** + * Check if this group contains a flag with the given key. Works with alias keys. + * + * @param key The key to check. + * @return True if the group contains a flag with the given key, false otherwise. + */ + public boolean hasKey(@Nullable String key) { + return keysFlagMap.containsKey(key); + } + + /** + * Get the remaining keys after the given flags have been removed. Works with alias keys. + * + * @param flags The flags to remove. + * @return The remaining keys. + */ + public @NotNull Set getRemainingKeys(@NotNull String[] flags) { + Set keysRemaining = new HashSet<>(this.keys); + for (String flag : flags) { + CommandFlag mvFlag = this.getFlagByKey(flag); + if (mvFlag != null) { + keysRemaining.remove(mvFlag.getKey()); + } + } + return keysRemaining; + } + + /** + * Get a flag by its key. Alias keys are supported as well. + * + * @param key The key of the flag. + * @return The flag if found, null otherwise. + */ + public @Nullable CommandFlag getFlagByKey(String key) { + return keysFlagMap.get(key); + } + + /** + * A builder for {@link CommandFlagGroup}. + */ + public static class Builder { + private final String name; + private final List keys; + private final Map keysFlagMap; + + /** + * Creates a new builder. + * + * @param name The name of the flag group. + */ + public Builder(@NotNull String name) { + this.name = name; + this.keys = new ArrayList<>(); + this.keysFlagMap = new HashMap<>(); + } + + /** + * Adds a flag to the group. + * + * @param flag The flag to add. + * @return The builder. + */ + public @NotNull Builder add(CommandFlag flag) { + keys.add(flag.getKey()); + keysFlagMap.put(flag.getKey(), flag); + flag.getAliases().forEach((alias) -> keysFlagMap.put(alias, flag)); + return this; + } + + /** + * Builds the flag group. + * + * @return The flag group. + */ + public @NotNull CommandFlagGroup build() { + return new CommandFlagGroup(this); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsManager.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsManager.java new file mode 100644 index 000000000..85afe3edd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsManager.java @@ -0,0 +1,96 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import co.aikar.commands.InvalidCommandArgument; +import com.dumptruckman.minecraft.util.Logging; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +/** + * Manages all the flag groups and parsing. + */ +@Service +public class CommandFlagsManager { + private final Map flagGroupMap; + + /** + * Creates a new FlagsManager. + */ + public CommandFlagsManager() { + flagGroupMap = new HashMap<>(); + } + + /** + * Registers a flag group. + * + * @param flagGroup The target flag group to register. + */ + public void registerFlagGroup(@NotNull CommandFlagGroup flagGroup) { + flagGroupMap.put(flagGroup.getName(), flagGroup); + } + + /** + * Get a flag group by name. + * + * @param groupName The target flag group name. + * @return The flag group if found, null otherwise. + */ + public @Nullable CommandFlagGroup getFlagGroup(@Nullable String groupName) { + return this.flagGroupMap.get(groupName); + } + + /** + * Autocompletes suggestions for flags. + * + * @param groupName The target flag group name. + * @param flags The current flags so far. + * @return The list of suggestions. + */ + public @NotNull Collection suggest(@Nullable String groupName, @NotNull String[] flags) { + CommandFlagGroup flagGroup = this.getFlagGroup(groupName); + if (flagGroup == null) { + Logging.warning("Unknown flag group: " + groupName); + return Collections.emptyList(); + } + + Collection suggestions = new ArrayList<>(); + CommandFlag currentFlag = (flags.length <= 1) ? null : flagGroup.getFlagByKey(flags[flags.length - 2]); + + if (currentFlag instanceof CommandValueFlag valueFlag) { + if (valueFlag.getCompletion() != null) { + suggestions.addAll(valueFlag.getCompletion().apply(flags[flags.length - 1])); + } + if (valueFlag.isOptional()) { + suggestions.addAll(flagGroup.getRemainingKeys(flags)); + } + } else { + suggestions.addAll(flagGroup.getRemainingKeys(flags)); + } + + return suggestions; + } + + /** + * Parses the flags. + * + * @param groupName The target flag group name. + * @param flags The flags to parse. + * @return The parsed flags. + * + * @throws InvalidCommandArgument If the flags are invalid. + */ + public @NotNull ParsedCommandFlags parse(@Nullable String groupName, @NotNull String[] flags) { + CommandFlagGroup flagGroup = this.getFlagGroup(groupName); + if (flagGroup == null) { + return ParsedCommandFlags.EMPTY; + } + + return new CommandFlagsParser(this.getFlagGroup(groupName), flags).parse(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsParser.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsParser.java new file mode 100644 index 000000000..6ec7a3911 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandFlagsParser.java @@ -0,0 +1,118 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import co.aikar.commands.InvalidCommandArgument; + +/** + * Parses flags from a string array. Each parser should only be used once. + */ +public class CommandFlagsParser { + private final CommandFlagGroup flagGroup; + private final String[] flags; + + private ParsedCommandFlags parsedFlags; + private boolean nextArgMayBeKey; + private boolean nextArgMayBeValue; + private CommandFlag currentFlag; + + /** + * Creates a new CommandFlagsParser. + * + * @param flagGroup The flag group to parse flags for. + * @param flags The flags to parse. + */ + public CommandFlagsParser(CommandFlagGroup flagGroup, String[] flags) { + this.flagGroup = flagGroup; + this.flags = flags; + } + + /** + * Parses the flags. + * + * @return The parsed flags. + */ + public ParsedCommandFlags parse() { + parsedFlags = new ParsedCommandFlags(); + + // First argument is always a key + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + + for (String flag : flags) { + if (this.nextArgMayBeKey) { + if (parseKey(flag)) continue; + } + if (this.nextArgMayBeValue) { + if (parseValue(flag)) continue; + } + throw new InvalidCommandArgument(flag + " is not a valid flag."); + } + + if (!this.nextArgMayBeKey && this.nextArgMayBeValue) { + throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!"); + } + + return parsedFlags; + } + + /** + * Parses a key. + * + * @param flag The flag to parse. + * @return True if the flag was parsed as a key, false otherwise. + */ + private boolean parseKey(String flag) { + CommandFlag potentialFlag = flagGroup.getFlagByKey(flag); + if (potentialFlag == null) { + return false; + } + + this.currentFlag = potentialFlag; + + if (this.currentFlag instanceof CommandValueFlag) { + CommandValueFlag valueFlag = (CommandValueFlag) this.currentFlag; + + if (valueFlag.isOptional()) { + parsedFlags.addFlagResult(valueFlag.getKey(), valueFlag.getDefaultValue()); + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = true; + return true; + } + + this.nextArgMayBeKey = false; + this.nextArgMayBeValue = true; + return true; + } + + parsedFlags.addFlagResult(this.currentFlag.getKey(), null); + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + + return true; + } + + /** + * Parses a value. + * + * @param flag The flag to parse. + * @return True if the flag was parsed as a value, false otherwise. + */ + private boolean parseValue(String flag) { + if (this.currentFlag == null) { + throw new InvalidCommandArgument("Some flag logic error occurred at " + flag + ""); + } + if (flagGroup.hasKey(flag)) { + throw new InvalidCommandArgument(currentFlag.getKey() + " requires a value!"); + } + + Object flagValue; + CommandValueFlag valueFlag = (CommandValueFlag) this.currentFlag; + flagValue = valueFlag.getContext() != null ? valueFlag.getContext().apply(flag) : flag; + parsedFlags.addFlagResult(valueFlag.getKey(), flagValue); + + // After a value, the next argument must be a key + this.nextArgMayBeKey = true; + this.nextArgMayBeValue = false; + this.currentFlag = null; + return true; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandValueFlag.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandValueFlag.java new file mode 100644 index 000000000..b03abeec9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/CommandValueFlag.java @@ -0,0 +1,270 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import co.aikar.commands.InvalidCommandArgument; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a flag with a value. + * + * @param The type of the value. + */ +public class CommandValueFlag extends CommandFlag { + /** + * A builder for a flag. + * + * @param key The key for the new flag. + * @param type The type of the value. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull String key, @NotNull Class type) { + return new Builder<>(key, type); + } + + /** + * A builder for a flag with enum value. + * + * @param key The key for the new flag. + * @param type The type of the value, must be enum. + * @return The builder. + */ + public static @NotNull > EnumBuilder enumBuilder(@NotNull String key, @NotNull Class type) { + return new EnumBuilder<>(key, type); + } + + private final Class type; + private final boolean optional; + private final T defaultValue; + private final Function context; + private final Function> completion; + + /** + * Creates a new flag. + * + * @param key The key for the new flag. + * @param aliases The aliases that also refer to this flag. + * @param type The type of the value. + * @param optional Allow for flag without value. + * @param defaultValue The default value if optional is true and user does not specify a value. + * @param context Function to parse string into value type. + * @param completion Function to get completion for this flag. + */ + protected CommandValueFlag( + @NotNull String key, + @NotNull List aliases, + @NotNull Class type, + boolean optional, + @Nullable T defaultValue, + @Nullable Function context, + @Nullable Function> completion + ) { + super(key, aliases); + this.type = type; + this.optional = optional; + this.defaultValue = defaultValue; + this.context = context; + this.completion = completion; + } + + /** + * Get the type of the value. + * + * @return The type of the value. + */ + public @NotNull Class getType() { + return type; + } + + /** + * Check if it is optional for users to specify a value. + * + * @return True if the value is optional, false otherwise. + */ + public boolean isOptional() { + return optional; + } + + /** + * Get the default value. May be null. + * + * @return The default value. + */ + public @Nullable T getDefaultValue() { + return defaultValue; + } + + /** + * Get the context. May be null for {@link String} value type. + * + * @return The context. + */ + public @Nullable Function getContext() { + return context; + } + + /** + * Get the completion. May be null. + * + * @return The completion. + */ + public @Nullable Function> getCompletion() { + return completion; + } + + /** + * A builder for a flag. + * + * @param The type of the value. + * @param The type of the builder. + */ + public static class Builder> extends CommandFlag.Builder { + protected final Class type; + protected boolean optional = false; + protected T defaultValue = null; + protected Function context = null; + protected Function> completion = null; + + /** + * Create a new builder. + * + * @param key The key for the new flag. + * @param type The type of the value. + */ + public Builder(@NotNull String key, @NotNull Class type) { + super(key); + this.type = type; + } + + /** + * Set the flag as optional for users to specify a value. + * + * @return The builder. + */ + public @NotNull S optional() { + this.optional = true; + return (S) this; + } + + /** + * Set the default value. Used if optional is true and user does not specify a value. + * + * @param defaultValue The default value. + * @return The builder. + */ + public @NotNull S defaultValue(@NotNull T defaultValue) { + this.defaultValue = defaultValue; + return (S) this; + } + + /** + * Set the context callback for parsing string into value type. + * + * @param context The context. + * @return The builder. + */ + public @NotNull S context(@NotNull Function context) { + this.context = context; + return (S) this; + } + + /** + * Set the completion callback for autocomplete. + * + * @param completion The completion. Input is the current input string, and output is the list of suggestions. + * @return The builder. + */ + public @NotNull S completion(@NotNull Function> completion) { + this.completion = completion; + return (S) this; + } + + /** + * Build the flag. + * + * @return The flag. + */ + @Override + public @NotNull CommandValueFlag build() { + if (context == null && !String.class.equals(type)) { + throw new IllegalStateException("Context is required for non-string value flags"); + } + return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion); + } + } + + /** + * Specific builder for a flag with enum value. + * + * @param The type of the value. + * @param The type of the builder. + */ + public static class EnumBuilder, S extends EnumBuilder> extends CommandFlag.Builder { + protected final Class type; + protected boolean optional = false; + protected T defaultValue = null; + protected Function context = null; + protected Function> completion = null; + + public EnumBuilder(@NotNull String key, @NotNull Class type) { + super(key); + this.type = type; + setEnumContext(); + setEnumCompletion(); + } + + private void setEnumContext() { + this.context = (String value) -> { + try { + return Enum.valueOf(type, value.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new InvalidCommandArgument("Invalid value for argument " + key + ": " + value); + } + }; + } + + private void setEnumCompletion() { + List types = Arrays.stream(type.getEnumConstants()) + .map(typeClass -> typeClass.name().toLowerCase()) + .toList(); + + this.completion = (input) -> types; + } + + /** + * Set the flag as optional for users to specify a value. + * + * @return The builder. + */ + public @NotNull S optional() { + this.optional = true; + return (S) this; + } + + /** + * Set the default value. Used if optional is true and user does not specify a value. + * + * @param defaultValue The default value. + * @return The builder. + */ + public @NotNull S defaultValue(@NotNull T defaultValue) { + this.defaultValue = defaultValue; + return (S) this; + } + + /** + * Build the flag. + * + * @return The flag. + */ + @Override + public @NotNull CommandValueFlag build() { + return new CommandValueFlag<>(key, aliases, type, optional, defaultValue, context, completion); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/ParsedCommandFlags.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/ParsedCommandFlags.java new file mode 100644 index 000000000..718629957 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/ParsedCommandFlags.java @@ -0,0 +1,122 @@ +package org.mvplugins.multiverse.core.commandtools.flag; + +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Object to contain the results of the flags present and its values. + */ +public class ParsedCommandFlags +{ + public static final ParsedCommandFlags EMPTY = new ParsedCommandFlags(); + + private final Map flagValues; + + ParsedCommandFlags() { + flagValues = new HashMap<>(); + } + + /** + * Add a flag result to the parsed flags. + * + * @param key The key of the flag. + * @param value The value of the flag. + */ + void addFlagResult(@NotNull String key, @Nullable Object value) { + flagValues.put(key, value); + } + + /** + * Check if a flag is present. + * + * @param flag The flag to check. + * @return True if the flag is present, false otherwise. + */ + public boolean hasFlag(@NotNull CommandFlag flag) { + return hasFlag(flag.getKey()); + } + + /** + * Check if a flag is present. + * + * @param key The key of the flag. + * @return True if the flag is present, false otherwise. + */ + public boolean hasFlag(@Nullable String key) { + return this.flagValues.containsKey(key); + } + + /** + * Check if a flag is present and has a value. + * + * @param key The key of the flag. + * @return True if the flag is present and has a value, false otherwise. + */ + public boolean hasFlagValue(@Nullable String key) { + return flagValue(key, Object.class) != null; + } + + /** + * Get the value of a flag. + * + * @param The type of the value. + * @param flag The flag to get the value of. + * @param type The type of the value. + * @return The value of the flag, null if flag does not exist or no value. + */ + public @Nullable T flagValue(@NotNull CommandFlag flag, @NotNull Class type) { + return flagValue(flag.getKey(), type); + } + + /** + * Get the value of a flag. + * + * @param key The key of the flag to get the value of. + * @param type The type of the value. + * @return The value of the flag, null if flag does not exist or no value. + */ + public @Nullable T flagValue(@Nullable String key, @NotNull Class type) { + Object value = this.flagValues.get(key); + return (T) value; + } + + /** + * Get the value of a flag. + * + * @param The type of the value. + * @param flag The flag to get the value of. + * @return The value of the flag, default value if flag does not exist or no value. + */ + public @Nullable T flagValue(@NotNull CommandValueFlag flag) { + return flagValue(flag.getKey(), flag.getType()); + } + + /** + * Get the value of a flag. + * + * @param The type of the value. + * @param flag The flag to get the value of. + * @param defaultValue The default value if flag does not exist or no value. + * @return The value of the flag, default value if flag does not exist or no value. + */ + public @NotNull T flagValue(@NotNull CommandValueFlag flag, @NotNull T defaultValue) { + return flagValue(flag.getKey(), defaultValue, flag.getType()); + } + + /** + * Get the value of a flag. + * + * @param The type of the value. + * @param key The key of the flag to get the value of. + * @param defaultValue The default value if flag does not exist or no value. + * @param type The type of the value. + * @return The value of the flag, default value if flag does not exist or no value. + */ + public @NotNull T flagValue(@Nullable String key, @NotNull T defaultValue, @NotNull Class type) { + T value = flagValue(key, type); + return value != null ? value : defaultValue; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/package-info.java new file mode 100644 index 000000000..2f13a0192 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flag/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains classes related to parsing command flags. + */ +package org.mvplugins.multiverse.core.commandtools.flag; diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/FilterCommandFlag.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/FilterCommandFlag.java new file mode 100644 index 000000000..9953c581d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/FilterCommandFlag.java @@ -0,0 +1,41 @@ +package org.mvplugins.multiverse.core.commandtools.flags; + +import java.util.List; +import java.util.function.Function; + +import co.aikar.commands.InvalidCommandArgument; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.RegexContentFilter; + +/** + * A command flag for regex filtering. + *
+ * Parses the value of the --filter (or -f) flag as a {@link ContentFilter}. + */ +public final class FilterCommandFlag extends CommandValueFlag { + + private static final Function VALUE_PARSER = value -> { + try { + return RegexContentFilter.fromString(value); + } catch (IllegalArgumentException e) { + throw new InvalidCommandArgument("Invalid filter: " + value); + } + }; + + /** + * Creates a new instance of this flag. + * + * @return The new instance. + */ + @NotNull + public static FilterCommandFlag create() { + return new FilterCommandFlag(); + } + + private FilterCommandFlag() { + super("--filter", List.of("-f"), ContentFilter.class, false, null, VALUE_PARSER, null); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/PageCommandFlag.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/PageCommandFlag.java new file mode 100644 index 000000000..f51c35b7e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/PageCommandFlag.java @@ -0,0 +1,38 @@ +package org.mvplugins.multiverse.core.commandtools.flags; + +import java.util.List; +import java.util.function.Function; + +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.commandtools.flag.CommandValueFlag; + +/** + * A command flag for page number. + *
+ * Parses the value of the --page (or -p) flag as an integer. + */ +public final class PageCommandFlag extends CommandValueFlag { + + private static final Function VALUE_PARSER = value -> { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid page number: " + value); + } + }; + + /** + * Creates a new instance of this flag. + * + * @return The new instance. + */ + @NotNull + public static PageCommandFlag create() { + return new PageCommandFlag(); + } + + private PageCommandFlag() { + super("--page", List.of("-p"), Integer.class, false, null, VALUE_PARSER, null); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/package-info.java new file mode 100644 index 000000000..05ccd0377 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/flags/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains predefined flags for commands. + */ +package org.mvplugins.multiverse.core.commandtools.flags; diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/package-info.java new file mode 100644 index 000000000..b11747073 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains the classes that manages command handling. + */ +package org.mvplugins.multiverse.core.commandtools; diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueueManager.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueueManager.java new file mode 100644 index 000000000..0d4a5ff7d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueueManager.java @@ -0,0 +1,189 @@ +/****************************************************************************** + * 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 org.mvplugins.multiverse.core.commandtools.queue; + +import java.util.Map; +import java.util.WeakHashMap; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import jakarta.inject.Inject; +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.command.ConsoleCommandSender; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.config.MVCoreConfig; + +/** + *

Manages the queuing of dangerous commands that require {@code /mv confirm} before executing.

+ * + *

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.

+ */ +@Service +public class CommandQueueManager { + + private static final String CONSOLE_NAME = "@console"; + private static final String COMMAND_BLOCK_NAME = "@commandblock"; + private static final long TICKS_PER_SECOND = 20; + + private final Plugin plugin; + private final MVCoreConfig config; + private final Map queuedCommandMap; + + @Inject + CommandQueueManager(@NotNull MultiverseCore plugin, @NotNull MVCoreConfig config) { + this.plugin = plugin; + this.config = config; + this.queuedCommandMap = new WeakHashMap<>(); + } + + /** + * Adds a {@link CommandQueuePayload} into queue. + * + * @param payload The command queue payload to add. + */ + public void addToQueue(CommandQueuePayload payload) { + String senderName = parseSenderName(payload.issuer()); + if (canRunImmediately(senderName)) { + payload.action().run(); + return; + } + + this.removeFromQueue(senderName); + + Logging.finer("Add new command to queue for sender %s.", senderName); + this.queuedCommandMap.put(senderName, payload); + payload.expireTask(runExpireLater(senderName, payload.validDuration())); + + payload.issuer().sendInfo(payload.prompt()); + var confirmCommand = "/mv confirm"; + if (config.getUseConfirmOtp()) { + confirmCommand += " " + payload.otp(); + } + payload.issuer().sendMessage(String.format("Run %s%s %sto continue. This will expire in %s seconds.", + ChatColor.GREEN, confirmCommand, ChatColor.WHITE, payload.validDuration())); + } + + /** + * Check if sender does not need to queue before running. + * + * @param senderName The name of sender. + * @return True if sender does not need to queue before running. + */ + private boolean canRunImmediately(@NotNull String senderName) { + return switch (config.getConfirmMode()) { + case ENABLE -> false; + case PLAYER_ONLY -> senderName.equals(CONSOLE_NAME) || senderName.equals(COMMAND_BLOCK_NAME); + case DISABLE_COMMAND_BLOCKS -> senderName.equals(COMMAND_BLOCK_NAME); + case DISABLE_CONSOLE -> senderName.equals(CONSOLE_NAME); + case DISABLE -> true; + }; + } + + /** + * Expire task that removes a {@link CommandQueuePayload} from queue after valid duration defined. + * + * @param senderName The name of the sender. + * @return The expire {@link BukkitTask}. + */ + @NotNull + private BukkitTask runExpireLater(@NotNull String senderName, int validDuration) { + return Bukkit.getScheduler().runTaskLater( + this.plugin, + expireRunnable(senderName), + validDuration * TICKS_PER_SECOND); + } + + /** + * Runnable responsible for expiring the queued command. + * + * @param senderName The name of the sender. + * @return The expire runnable. + */ + @NotNull + private Runnable expireRunnable(@NotNull String senderName) { + return () -> { + CommandQueuePayload payload = this.queuedCommandMap.remove(senderName); + if (payload == null) { + // Payload already removed + return; + } + payload.issuer().sendMessage("Your queued command has expired."); + }; + } + + /** + * Runs the command in queue for the given sender, if any. + * + * @param issuer Sender that confirmed the command. + * @return True if queued command ran successfully, else false. + */ + public boolean runQueuedCommand(@NotNull MVCommandIssuer issuer, int otp) { + String senderName = parseSenderName(issuer); + CommandQueuePayload payload = this.queuedCommandMap.get(senderName); + if (payload == null) { + issuer.sendMessage(ChatColor.RED + "You do not have any commands in queue."); + return false; + } + if (config.getUseConfirmOtp() && payload.otp() != otp) { + issuer.sendMessage(ChatColor.RED + "Invalid OTP number. Please try again..."); + return false; + } + Logging.finer("Running queued command..."); + payload.action().run(); + return removeFromQueue(senderName); + } + + /** + * Since only one command is stored in queue per sender, we remove the old one. + * + * @param senderName The sender that executed the command. + * @return True if queue command is removed from sender successfully, else false. + */ + public boolean removeFromQueue(@NotNull String senderName) { + CommandQueuePayload payload = this.queuedCommandMap.remove(senderName); + if (payload == null) { + Logging.finer("No queue command to remove for sender %s.", senderName); + return false; + } + Option.of(payload.expireTask()).peek(BukkitTask::cancel); + Logging.finer("Removed queue command for sender %s.", senderName); + return true; + } + + private String parseSenderName(MVCommandIssuer issuer) { + CommandSender sender = issuer.getIssuer(); + if (isCommandBlock(sender)) { + return COMMAND_BLOCK_NAME; + } else if (sender instanceof ConsoleCommandSender) { + return CONSOLE_NAME; + } + return sender.getName(); + } + + /** + * 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; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueuePayload.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueuePayload.java new file mode 100644 index 000000000..cdd463c4d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/CommandQueuePayload.java @@ -0,0 +1,127 @@ +package org.mvplugins.multiverse.core.commandtools.queue; + +import co.aikar.commands.ACFUtil; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.commandtools.MVCommandIssuer; +import org.mvplugins.multiverse.core.locale.message.Message; + +/** + * Represents a single command used in {@link CommandQueueManager} for confirming before running potentially + * dangerous action. + */ +public class CommandQueuePayload { + + /** + * Creates a new {@link CommandQueuePayload} + * + * @param issuer The issuer of the command + * @return The new {@link CommandQueuePayload} + */ + public static CommandQueuePayload issuer(@NotNull MVCommandIssuer issuer) { + return new CommandQueuePayload(issuer); + } + + private static final String DEFAULT_PROMPT_MESSAGE = "The command you are trying to run is deemed dangerous."; // todo: localize + private static final int DEFAULT_VALID_TIME = 10; + + private final int otp; + private final MVCommandIssuer issuer; + private Runnable action = () -> {}; + private int validDuration = DEFAULT_VALID_TIME; + private Message prompt = Message.of(DEFAULT_PROMPT_MESSAGE); + private BukkitTask expireTask; + + protected CommandQueuePayload(@NotNull MVCommandIssuer issuer) { + this.otp = ACFUtil.rand(100, 999); + this.issuer = issuer; + } + + /** + * Gets the issuer of the command. + * @return The issuer + */ + @NotNull + public MVCommandIssuer issuer() { + return issuer; + } + + /** + * Sets the logic to execute upon confirming. + * @param action The logic to execute + * @return The same {@link CommandQueuePayload} for method chaining + */ + public CommandQueuePayload action(@NotNull Runnable action) { + this.action = action; + return this; + } + + /** + * Gets the logic to execute upon confirming. + * + * @return The logic to execute + */ + @NotNull + public Runnable action() { + return action; + } + + /** + * Gets the OTP number for validation. + * + * @return The OTP number + */ + public int otp() { + return otp; + } + + /** + * Sets the duration in which the command is valid for confirm in seconds. + * + * @param validDuration The target duration in seconds. + * @return The same {@link CommandQueuePayload} for method chaining. + */ + public CommandQueuePayload validDuration(int validDuration) { + this.validDuration = validDuration; + return this; + } + + /** + * Gets the duration in which the command is valid for confirm in seconds. + * + * @return The duration in seconds. + */ + public int validDuration() { + return validDuration; + } + + /** + * Sets the question to ask sender to confirm. + * + * @param prompt The prompt message. + * @return The same {@link CommandQueuePayload} for method chaining. + */ + public CommandQueuePayload prompt(Message prompt) { + this.prompt = prompt; + return this; + } + + /** + * Gets the question to ask sender to confirm. + * + * @return The prompt message. + */ + public Message prompt() { + return prompt; + } + + void expireTask(BukkitTask expireTask) { + this.expireTask = expireTask; + } + + @Nullable + BukkitTask expireTask() { + return expireTask; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/package-info.java b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/package-info.java similarity index 55% rename from src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/package-info.java rename to src/main/java/org/mvplugins/multiverse/core/commandtools/queue/package-info.java index 075ff0dfe..65b223697 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commandtools/queue/package-info.java +++ b/src/main/java/org/mvplugins/multiverse/core/commandtools/queue/package-info.java @@ -1,4 +1,4 @@ /** * Manager queuing of dangerous commands in need of confirmation. */ -package com.onarandombox.MultiverseCore.commandtools.queue; \ No newline at end of file +package org.mvplugins.multiverse.core.commandtools.queue; diff --git a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java new file mode 100644 index 000000000..e638351e8 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfig.java @@ -0,0 +1,534 @@ +package org.mvplugins.multiverse.core.config; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.ConfirmMode; +import org.mvplugins.multiverse.core.configuration.handle.CommentedConfigurationHandle; +import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.configuration.migration.BooleanMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.migration.IntegerMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.InvertBoolMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.MoveMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.VersionMigrator; + +@Service +public class MVCoreConfig { + public static final String CONFIG_FILENAME = "config.yml"; + + private final Path configPath; + private final MVCoreConfigNodes configNodes; + private final CommentedConfigurationHandle configHandle; + private final StringPropertyHandle stringPropertyHandle; + + @Inject + MVCoreConfig( + @NotNull MultiverseCore core, + @NotNull PluginManager pluginManager, + @NotNull Provider commandManager // config needs to be instantiated before the command manager + ) { + this.configPath = Path.of(core.getDataFolder().getPath(), CONFIG_FILENAME); + this.configNodes = new MVCoreConfigNodes(pluginManager, commandManager); + this.configHandle = CommentedConfigurationHandle.builder(configPath, configNodes.getNodes()) + .logger(Logging.getLogger()) + .migrator(ConfigMigrator.builder(configNodes.VERSION) + .addVersionMigrator(VersionMigrator.builder(5.0) + .addAction(MoveMigratorAction.of("multiverse-configuration.enforceaccess", "world.enforce-access")) + .addAction(BooleanMigratorAction.of("world.enforce-access")) + .addAction(MoveMigratorAction.of("multiverse-configuration.prefixchat", "messaging.enable-chat-prefix")) + .addAction(BooleanMigratorAction.of("messaging.enable-chat-prefix")) + .addAction(MoveMigratorAction.of("multiverse-configuration.prefixchatformat", "messaging.chat-prefix-format")) + .addAction(MoveMigratorAction.of("multiverse-configuration.teleportintercept", "world.teleport-intercept")) + .addAction(BooleanMigratorAction.of("world.teleport-intercept")) + .addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnoverride", "spawn.first-spawn-override")) + .addAction(BooleanMigratorAction.of("spawn.first-spawn-override")) + //.addAction(MoveMigratorAction.of("multiverse-configuration.displaypermerrors", "")) + .addAction(MoveMigratorAction.of("multiverse-configuration.globaldebug", "misc.global-debug")) + .addAction(IntegerMigratorAction.of("misc.global-debug")) + .addAction(MoveMigratorAction.of("multiverse-configuration.silentstart", "misc.silent-start")) + .addAction(BooleanMigratorAction.of("misc.silent-start")) + .addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnworld", "spawn.first-spawn-location")) + .addAction(MoveMigratorAction.of("multiverse-configuration.defaultportalsearch", "portal.use-custom-portal-search")) + .addAction(BooleanMigratorAction.of("portal.use-custom-portal-search")) + .addAction(InvertBoolMigratorAction.of("portal.use-custom-portal-search")) + .addAction(MoveMigratorAction.of("multiverse-configuration.portalsearchradius", "portal.custom-portal-search-radius")) + .addAction(IntegerMigratorAction.of("portal.custom-portal-search-radius")) + .addAction(MoveMigratorAction.of("multiverse-configuration.autopurge", "world.auto-purge-entities")) + .addAction(BooleanMigratorAction.of("world.auto-purge-entities")) + .addAction(MoveMigratorAction.of("multiverse-configuration.idonotwanttodonate", "misc.show-donation-message")) + .addAction(BooleanMigratorAction.of("misc.show-donation-message")) + .addAction(InvertBoolMigratorAction.of("misc.show-donation-message")) + .build()) + .addVersionMigrator(VersionMigrator.builder(5.1) + .addAction(MoveMigratorAction.of("world.teleport-intercept", "teleport.teleport-intercept")) + .addAction(MoveMigratorAction.of("world.resolve-alias-name", "command.resolve-alias-name")) + .build()) + .addVersionMigrator(VersionMigrator.builder(5.2) + .addAction(MoveMigratorAction.of("spawn.default-respawn-to-world-spawn", "world.enforce-respawn-at-world-spawn")) + .build()) + .build()) + .build(); + this.stringPropertyHandle = new StringPropertyHandle(configHandle); + } + + private void migrateFromOldConfigFile() { + String content; + try { + content = Files.readString(configPath); + } catch (IOException e) { + return; + } + // Remove the old config section if it is still in the old ConfigurationSerializable. + content = content.replace("==: com.onarandombox.MultiverseCore.MultiverseCoreConfiguration", ""); + try { + Files.writeString(configPath, content); + } catch (IOException e) { + // ignore + } + } + + public Try load() { + return Try.run(this::migrateFromOldConfigFile) + .flatMap(ignore -> configHandle.load()) + .onFailure(e -> { + Logging.severe("Failed to load Multiverse-Core config.yml!"); + e.printStackTrace(); + }); + } + + /** + * {@inheritDoc} + */ + public boolean isLoaded() { + return configHandle.isLoaded(); + } + + /** + * {@inheritDoc} + */ + public Try save() { + return configHandle.save(); + } + + /** + * {@inheritDoc} + */ + public StringPropertyHandle getStringPropertyHandle() { + return stringPropertyHandle; + } + + /** + * {@inheritDoc} + */ + public Try setEnforceAccess(boolean enforceAccess) { + return configHandle.set(configNodes.ENFORCE_ACCESS, enforceAccess); + } + + /** + * {@inheritDoc} + */ + public boolean getEnforceAccess() { + return configHandle.get(configNodes.ENFORCE_ACCESS); + } + + /** + * {@inheritDoc} + */ + public Try setEnforceGameMode(boolean enforceGameMode) { + return configHandle.set(configNodes.ENFORCE_GAMEMODE, enforceGameMode); + } + + /** + * {@inheritDoc} + */ + public boolean getEnforceGameMode() { + return configHandle.get(configNodes.ENFORCE_GAMEMODE); + } + + /** + * {@inheritDoc} + */ + public Try setAutoPurgeEntities(boolean autopurge) { + return configHandle.set(configNodes.AUTO_PURGE_ENTITIES, autopurge); + } + + /** + * {@inheritDoc} + */ + public boolean isAutoPurgeEntities() { + return configHandle.get(configNodes.AUTO_PURGE_ENTITIES); + } + + /** + * {@inheritDoc} + */ + public Try setUseFinerTeleportPermissions(boolean useFinerTeleportPermissions) { + return configHandle.set(configNodes.USE_FINER_TELEPORT_PERMISSIONS, useFinerTeleportPermissions); + } + + /** + * {@inheritDoc} + */ + public boolean getUseFinerTeleportPermissions() { + return configHandle.get(configNodes.USE_FINER_TELEPORT_PERMISSIONS); + } + + /** + * {@inheritDoc} + */ + public Try setConcurrentTeleportLimit(int concurrentTeleportLimit) { + return configHandle.set(configNodes.CONCURRENT_TELEPORT_LIMIT, concurrentTeleportLimit); + } + + /** + * {@inheritDoc} + */ + public int getConcurrentTeleportLimit() { + return configHandle.get(configNodes.CONCURRENT_TELEPORT_LIMIT); + } + + /** + * {@inheritDoc} + */ + public Try setTeleportIntercept(boolean teleportIntercept) { + return configHandle.set(configNodes.TELEPORT_INTERCEPT, teleportIntercept); + } + + /** + * {@inheritDoc} + */ + public boolean getTeleportIntercept() { + return configHandle.get(configNodes.TELEPORT_INTERCEPT); + } + + /** + * {@inheritDoc} + */ + public Try setFirstSpawnOverride(boolean firstSpawnOverride) { + return configHandle.set(configNodes.FIRST_SPAWN_OVERRIDE, firstSpawnOverride); + } + + /** + * {@inheritDoc} + */ + public Try setSafeLocationHorizontalSearchRadius(int searchRadius) { + return configHandle.set(configNodes.SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS, searchRadius); + } + + /** + * {@inheritDoc} + */ + public int getSafeLocationHorizontalSearchRadius() { + return configHandle.get(configNodes.SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS); + } + + /** + * {@inheritDoc} + */ + public Try setSafeLocationVerticalSearchRadius(int searchRadius) { + return configHandle.set(configNodes.SAFE_LOCATION_VERTICAL_SEARCH_RADIUS, searchRadius); + } + + /** + * {@inheritDoc} + */ + public int getSafeLocationVerticalSearchRadius() { + return configHandle.get(configNodes.SAFE_LOCATION_VERTICAL_SEARCH_RADIUS); + } + + /** + * {@inheritDoc} + */ + public boolean getFirstSpawnOverride() { + return configHandle.get(configNodes.FIRST_SPAWN_OVERRIDE); + } + + /** + * {@inheritDoc} + */ + public Try setFirstSpawnLocation(String firstSpawnWorld) { + return configHandle.set(configNodes.FIRST_SPAWN_LOCATION, firstSpawnWorld); + } + + /** + * {@inheritDoc} + */ + public String getFirstSpawnLocation() { + return configHandle.get(configNodes.FIRST_SPAWN_LOCATION); + } + + /** + * {@inheritDoc} + */ + public Try setEnableJoinDestination(boolean enableJoinDestination) { + return configHandle.set(configNodes.ENABLE_JOIN_DESTINATION, enableJoinDestination); + } + + /** + * {@inheritDoc} + */ + public boolean getEnableJoinDestination() { + return configHandle.get(configNodes.ENABLE_JOIN_DESTINATION); + } + + /** + * {@inheritDoc} + */ + public Try setJoinDestination(String alwaysSpawnDestination) { + return configHandle.set(configNodes.JOIN_DESTINATION, alwaysSpawnDestination); + } + + /** + * {@inheritDoc} + */ + public String getJoinDestination() { + return configHandle.get(configNodes.JOIN_DESTINATION); + } + + /** + * {@inheritDoc} + */ + public Try setDefaultRespawnWithinSameWorld(boolean defaultRespawnToWorldSpawn) { + return configHandle.set(configNodes.DEFAULT_RESPAWN_WITHIN_SAME_WORLD, defaultRespawnToWorldSpawn); + } + + /** + * {@inheritDoc} + */ + public boolean getDefaultRespawnWithinSameWorld() { + return configHandle.get(configNodes.DEFAULT_RESPAWN_WITHIN_SAME_WORLD); + } + + /** + * {@inheritDoc} + */ + public Try setEnforceRespawnAtWorldSpawn(boolean enforceRespawnAtWorldSpawn) { + return configHandle.set(configNodes.ENFORCE_RESPAWN_AT_WORLD_SPAWN, enforceRespawnAtWorldSpawn); + } + + /** + * {@inheritDoc} + */ + public boolean getEnforceRespawnAtWorldSpawn() { + return configHandle.get(configNodes.ENFORCE_RESPAWN_AT_WORLD_SPAWN); + } + + /** + * {@inheritDoc} + */ + public Try setUseCustomPortalSearch(boolean useDefaultPortalSearch) { + return configHandle.set(configNodes.USE_CUSTOM_PORTAL_SEARCH, useDefaultPortalSearch); + } + + /** + * {@inheritDoc} + */ + public boolean isUsingCustomPortalSearch() { + return configHandle.get(configNodes.USE_CUSTOM_PORTAL_SEARCH); + } + + /** + * {@inheritDoc} + */ + public Try setCustomPortalSearchRadius(int searchRadius) { + return configHandle.set(configNodes.CUSTOM_PORTAL_SEARCH_RADIUS, searchRadius); + } + + /** + * {@inheritDoc} + */ + public int getCustomPortalSearchRadius() { + return configHandle.get(configNodes.CUSTOM_PORTAL_SEARCH_RADIUS); + } + + /** + * {@inheritDoc} + */ + public Try setEnablePrefixChat(boolean prefixChat) { + return configHandle.set(configNodes.ENABLE_CHAT_PREFIX, prefixChat); + } + + /** + * {@inheritDoc} + */ + public boolean isEnablePrefixChat() { + return configHandle.get(configNodes.ENABLE_CHAT_PREFIX); + } + + /** + * {@inheritDoc} + */ + public Try setPrefixChatFormat(String prefixChatFormat) { + return configHandle.set(configNodes.CHAT_PREFIX_FORMAT, prefixChatFormat); + } + + /** + * {@inheritDoc} + */ + public String getPrefixChatFormat() { + return configHandle.get(configNodes.CHAT_PREFIX_FORMAT); + } + + /** + * {@inheritDoc} + */ + public Try setRegisterPapiHook(boolean registerPapiHook) { + return configHandle.set(configNodes.REGISTER_PAPI_HOOK, registerPapiHook); + } + + /** + * {@inheritDoc} + */ + public boolean isRegisterPapiHook() { + return configHandle.get(configNodes.REGISTER_PAPI_HOOK); + } + + /** + * {@inheritDoc} + */ + public Try setDefaultLocale(Locale defaultLocale) { + return configHandle.set(configNodes.DEFAULT_LOCALE, defaultLocale); + } + + /** + * {@inheritDoc} + */ + public Locale getDefaultLocale() { + return configHandle.get(configNodes.DEFAULT_LOCALE); + } + + /** + * {@inheritDoc} + */ + public Try setPerPlayerLocale(boolean perPlayerLocale) { + return configHandle.set(configNodes.PER_PLAYER_LOCALE, perPlayerLocale); + } + + /** + * {@inheritDoc} + */ + public boolean getPerPlayerLocale() { + return configHandle.get(configNodes.PER_PLAYER_LOCALE); + } + + /** + * {@inheritDoc} + */ + public Try setResolveAliasName(boolean resolveAliasInCommands) { + return configHandle.set(configNodes.RESOLVE_ALIAS_NAME, resolveAliasInCommands); + } + + /** + * {@inheritDoc} + */ + public boolean getResolveAliasName() { + return configHandle.get(configNodes.RESOLVE_ALIAS_NAME); + } + + /** + * {@inheritDoc} + */ + public Try setConfirmMode(ConfirmMode confirmMode) { + return configHandle.set(configNodes.CONFIRM_MODE, confirmMode); + } + + /** + * {@inheritDoc} + */ + public ConfirmMode getConfirmMode() { + return configHandle.get(configNodes.CONFIRM_MODE); + } + + /** + * {@inheritDoc} + */ + public Try setUseConfirmOtp(boolean useConfirmOtp) { + return configHandle.set(configNodes.USE_CONFIRM_OTP, useConfirmOtp); + } + + /** + * {@inheritDoc} + */ + public boolean getUseConfirmOtp() { + return configHandle.get(configNodes.USE_CONFIRM_OTP); + } + + /** + * {@inheritDoc} + */ + public Try setGlobalDebug(int globalDebug) { + return configHandle.set(configNodes.GLOBAL_DEBUG, globalDebug); + } + + /** + * {@inheritDoc} + */ + public int getGlobalDebug() { + return configHandle.get(configNodes.GLOBAL_DEBUG); + } + + /** + * {@inheritDoc} + */ + public Try setDebugPermissions(boolean debugPermissions) { + return configHandle.set(configNodes.DEBUG_PERMISSIONS, debugPermissions); + } + + /** + * {@inheritDoc} + */ + public boolean getDebugPermissions() { + return configHandle.get(configNodes.DEBUG_PERMISSIONS); + } + + /** + * {@inheritDoc} + */ + public Try setSilentStart(boolean silentStart) { + return configHandle.set(configNodes.SILENT_START, silentStart); + } + + /** + * {@inheritDoc} + */ + public boolean getSilentStart() { + return configHandle.get(configNodes.SILENT_START); + } + + /** + * {@inheritDoc} + */ + public Try setShowDonateMessage(boolean showDonateMessage) { + return configHandle.set(configNodes.SHOW_DONATION_MESSAGE, showDonateMessage); + } + + /** + * {@inheritDoc} + */ + public boolean isShowingDonateMessage() { + return configHandle.get(configNodes.SHOW_DONATION_MESSAGE); + } + + /** + * Gets the underlying config file object + * + * @return The config file + */ + public FileConfiguration getConfig() { + return configHandle.getConfig(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java new file mode 100644 index 000000000..a82e4fe53 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/MVCoreConfigNodes.java @@ -0,0 +1,388 @@ +package org.mvplugins.multiverse.core.config; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Provider; +import org.bukkit.plugin.PluginManager; + +import org.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.commandtools.ConfirmMode; +import org.mvplugins.multiverse.core.configuration.node.ConfigHeaderNode; +import org.mvplugins.multiverse.core.configuration.node.ConfigNode; +import org.mvplugins.multiverse.core.configuration.node.Node; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; +import org.mvplugins.multiverse.core.event.MVDebugModeEvent; +import org.mvplugins.multiverse.core.exceptions.MultiverseException; +import org.mvplugins.multiverse.core.permissions.PermissionUtils; + +import java.util.Locale; + +final class MVCoreConfigNodes { + + private final NodeGroup nodes = new NodeGroup(); + private PluginManager pluginManager; + private Provider commandManager; + + MVCoreConfigNodes(@NotNull PluginManager pluginManager, @NotNull Provider commandManager) { + this.pluginManager = pluginManager; + this.commandManager = commandManager; + } + + NodeGroup getNodes() { + return nodes; + } + + private N node(N node) { + nodes.add(node); + return node; + } + + // BEGIN CHECKSTYLE-SUPPRESSION: Javadoc + // BEGIN CHECKSTYLE-SUPPRESSION: MemberName + // BEGIN CHECKSTYLE-SUPPRESSION: Abbreviation + // BEGIN CHECKSTYLE-SUPPRESSION: VisibilityModifier + // BEGIN CHECKSTYLE-SUPPRESSION: MultipleStringLiterals + // BEGIN CHECKSTYLE-SUPPRESSION: LineLength + + private final ConfigHeaderNode HEADER = node(ConfigHeaderNode.builder("world") + .comment("####################################################################################################") + .comment("# #") + .comment("# █▀▄▀█ █░█ █░░ ▀█▀ █ █░█ █▀▀ █▀█ █▀ █▀▀   █▀▀ █▀█ █▀█ █▀▀ #") + .comment("# █░▀░█ █▄█ █▄▄ ░█░ █ ▀▄▀ ██▄ █▀▄ ▄█ ██▄   █▄▄ █▄█ █▀▄ ██▄ #") + .comment("# #") + .comment("# #") + .comment("# WIKI: https://github.com/Multiverse/Multiverse-Core/wiki #") + .comment("# DISCORD: https://discord.gg/NZtfKky #") + .comment("# BUG REPORTS: https://github.com/Multiverse/Multiverse-Core/issues #") + .comment("# #") + .comment("# #") + .comment("# Each option in this file is documented and explained here: #") + .comment("# ==> https://github.com/Multiverse/Multiverse-Core/wiki/config.yml #") + .comment("# #") + .comment("# #") + .comment("# New options are added to this file automatically. If you manually made changes #") + .comment("# to this file while your server is running, please run `/mv reload` command. #") + .comment("# #") + .comment("####################################################################################################") + .comment("") + .comment("") + .build()); + + //private final ConfigHeaderNode WORLD_HEADER = node(ConfigHeaderNode.builder("world") + // .comment("") + // .comment("") + // .build()); + + final ConfigNode ENFORCE_ACCESS = node(ConfigNode.builder("world.enforce-access", Boolean.class) + .comment("This setting will prevent players from entering worlds they don't have access to.") + .comment("If this is set to false, players will be able to enter any world they want.") + .comment("If this is set to true, players will only be able to enter worlds they have") + .comment("the `mv.access.` permission.") + .defaultValue(false) + .name("enforce-access") + .build()); + + final ConfigNode ENFORCE_GAMEMODE = node(ConfigNode.builder("world.enforce-gamemode", Boolean.class) + .comment("") + .comment("Sets whether Multiverse will should enforce gamemode on world change.") + .comment("If enabled, players will be forced into the gamemode of the world they are entering, unless they have") + .comment("the `mv.bypass.gamemode.` permission.") + .defaultValue(true) + .name("enforce-gamemode") + .build()); + + final ConfigNode AUTO_PURGE_ENTITIES = node(ConfigNode.builder("world.auto-purge-entities", Boolean.class) + .comment("") + .comment("Sets whether Multiverse will purge mobs and entities automatically.") + .defaultValue(false) + .name("auto-purge-entities") + .build()); + + private final ConfigHeaderNode TELEPORT_HEADER = node(ConfigHeaderNode.builder("teleport") + .comment("") + .comment("") + .build()); + + final ConfigNode USE_FINER_TELEPORT_PERMISSIONS = node(ConfigNode.builder("teleport.use-finer-teleport-permissions", Boolean.class) + .comment("Sets whether Multiverse will use more fine-grained teleport permissions.") + .comment("If enabled, Multiverse will use the `multiverse.teleport...` permission to determine whether") + .comment("a player can teleport to a world. If disabled, Multiverse will use the `mulitverse.teleport..`") + .comment("permission to determine whether a player can teleport to a world.") + .comment("For example, if `multiverse.teleport.self.w.world2` is set, Multiverse will only allow the player to teleport to the world2") + .defaultValue(true) + .name("use-finer-teleport-permissions") + .build()); + + final ConfigNode CONCURRENT_TELEPORT_LIMIT = node(ConfigNode.builder("teleport.concurrent-teleport-limit", Integer.class) + .comment("") + .comment("Sets the maximum number of players allowed to be teleported at once with `/mv teleport` command") + .defaultValue(50) + .name("concurrent-teleport-limit") + .build()); + + final ConfigNode TELEPORT_INTERCEPT = node(ConfigNode.builder("teleport.teleport-intercept", Boolean.class) + .comment("") + .comment("If this is set to true, Multiverse will enforce access permissions for all teleportation,") + .comment("including teleportation from other plugins. You should not disable this unless you are facing") + .comment("conflict with another plugin handling teleportation.") + .defaultValue(true) + .name("teleport-intercept") + .build()); + + final ConfigNode SAFE_LOCATION_HORIZONTAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-horizontal-search-radius", Integer.class) + .comment("") + .comment("Sets the horizontal (x and z-axis) search radius for finding a safe location to teleport to.") + .comment("Increasing this value will widen the search area at the cost of performance.") + .comment("To disable, set to 0.") + .defaultValue(3) + .name("safe-location-horizontal-search-radius") + .build()); + + final ConfigNode SAFE_LOCATION_VERTICAL_SEARCH_RADIUS = node(ConfigNode.builder("teleport.safe-location-vertical-search-radius", Integer.class) + .comment("") + .comment("Sets the vertical (y-axis) search radius for finding a safe location to teleport to.") + .comment("Increasing this value will widen the search area at the cost of performance.") + .comment("To disable, set to 0.") + .defaultValue(3) + .name("safe-location-vertical-search-radius") + .build()); + + private final ConfigHeaderNode SPAWN_HEADER = node(ConfigHeaderNode.builder("spawn") + .comment("") + .comment("") + .build()); + + final ConfigNode FIRST_SPAWN_OVERRIDE = node(ConfigNode.builder("spawn.first-spawn-override", Boolean.class) + .comment("Sets whether Multiverse will override the first spawn location of a world.") + .comment("If enabled, Multiverse will set the first spawn location of a world to the spawn location of the world.") + .comment("If disabled, it will default to server.properties settings.") + .defaultValue(true) + .name("first-spawn-override") + .build()); + + final ConfigNode FIRST_SPAWN_LOCATION = node(ConfigNode.builder("spawn.first-spawn-location", String.class) + .comment("") + .comment("Sets the world that Multiverse will use as the location for players that first join the server.") + .comment("This only applies if first-spawn-override is set to true.") + .defaultValue("") + .name("first-spawn-location") + .build()); + + final ConfigNode ENABLE_JOIN_DESTINATION = node(ConfigNode.builder("spawn.enable-join-destination", Boolean.class) + .comment("") + .comment("Enables join-destination below.") + .defaultValue(false) + .name("enable-join-destination") + .build()); + + final ConfigNode JOIN_DESTINATION = node(ConfigNode.builder("spawn.join-destination", String.class) + .comment("") + .comment("Sets the destination that Multiverse will use to spawn players on every login") + .comment("Set the above enable-join-destination to false to disable") + .defaultValue("") + .name("join-destination") + .build()); + + final ConfigNode DEFAULT_RESPAWN_WITHIN_SAME_WORLD = node(ConfigNode.builder("spawn.default-respawn-within-same-world", Boolean.class) + .comment("") + .comment("This only applies if the `respawn-world` property is not set for the world that the player died in,") + .comment("and the player does not have bed or anchor set.") + .comment("----") + .comment("When this option is enabled, players will respawn in the same world's that they died in.") + .comment("If the /spawnpoint is already within that world and `enforce-respawn-at-world-spawn` is disabled,") + .comment("Multiverse will use that spawn location, else it will use the world's spawn where the player died in.") + .comment("----") + .comment("You can set `respawn-world` property with the command: `/mv modify set respawn-world `") + .comment("You can reset `respawn-world` property with the command: `/mv modify reset respawn-world`") + .comment("----") + .comment("Set this to false if you want another plugin to handle respawning.") + .defaultValue(true) + .name("default-respawn-within-same-world") + .build()); + + final ConfigNode ENFORCE_RESPAWN_AT_WORLD_SPAWN = node(ConfigNode.builder("spawn.enforce-respawn-at-world-spawn", Boolean.class) + .comment("") + .comment("This config will only apply if `respawn-world` is set, or `default-respawn-within-same-world` is enabled.") + .comment("----") + .comment("When this option is enabled, players will always respawn at the world's spawn location of `respawn-world`,") + .comment("unless bed or anchor is set and `bed-respawn` or `anchor-spawn` is enabled respectively.") + .comment("If respawn-world is set, Multiverse will use that world's spawn location, else it will use the world's spawn where the player died in.") + .comment("----") + .comment("Set this to false if you want to use the /spawnpoint instead of the world's spawn location.") + .defaultValue(true) + .name("enforce-respawn-at-world-spawn") + .build()); + + private final ConfigHeaderNode PORTAL_HEADER = node(ConfigHeaderNode.builder("portal") + .comment("") + .comment("") + .build()); + + final ConfigNode USE_CUSTOM_PORTAL_SEARCH = node(ConfigNode.builder("portal.use-custom-portal-search", Boolean.class) + .comment("This config option defines whether or not Multiverse should interfere with's Bukkit's default portal search radius.") + .comment("Setting it to false would mean you want to simply let Bukkit decides the search radius itself.") + .defaultValue(false) + .name("use-custom-portal-search") + .build()); + + final ConfigNode CUSTOM_PORTAL_SEARCH_RADIUS = node(ConfigNode.builder("portal.custom-portal-search-radius", Integer.class) + .comment("") + .comment("This config option defines the search radius Multiverse should use when searching for a portal.") + .comment("This only applies if use-custom-portal-search is set to true.") + .defaultValue(128) + .name("custom-portal-search-radius") + .validator(value -> value < 0 + ? Try.failure(new MultiverseException("The value must be greater than or equal to 0.", null)) + : Try.success(null)) + .build()); + + private final ConfigHeaderNode MESSAGING_HEADER = node(ConfigHeaderNode.builder("messaging") + .comment("") + .comment("") + .build()); + + final ConfigNode ENABLE_CHAT_PREFIX = node(ConfigNode.builder("messaging.enable-chat-prefix", Boolean.class) + .comment("This config option defines whether or not Multiverse should prefix the chat with the world name.") + .comment("This only applies if use-custom-portal-search is set to true.") + .defaultValue(false) + .name("enable-chat-prefix") + .build()); + + final ConfigNode CHAT_PREFIX_FORMAT = node(ConfigNode.builder("messaging.chat-prefix-format", String.class) + .comment("") + .comment("This config option defines the format Multiverse should use when prefixing the chat with the world name.") + .comment("This only applies if enable-chat-prefix is set to true.") + .defaultValue("[%world%]%chat%") + .name("chat-prefix-format") + .build()); + + final ConfigNode REGISTER_PAPI_HOOK = node(ConfigNode.builder("messaging.register-papi-hook", Boolean.class) + .comment("") + .comment("This config option defines whether or not Multiverse should register the PlaceholderAPI hook.") + .comment("This only applies if PlaceholderAPI is installed.") + .defaultValue(true) + .name("register-papi-hook") + .build()); + + final ConfigNode DEFAULT_LOCALE = node(ConfigNode.builder("messaging.default-locale", Locale.class) + .comment("") + .comment("This config option defines the default language Multiverse should use.") + .defaultValue(Locale.ENGLISH) + .name("default-locale") + .onSetValue((oldValue, newValue) -> { + commandManager.get().getLocales().setDefaultLocale(newValue); + }) + .build()); + + final ConfigNode PER_PLAYER_LOCALE = node(ConfigNode.builder("messaging.per-player-locale", Boolean.class) + .comment("") + .comment("This config option defines if Multiverse should use the player's language based on their client's language.") + .comment("If the player's language does not have a translation, it will use the default language set above instead.") + .defaultValue(true) + .name("per-player-locale") + .onSetValue((oldValue, newValue) -> { + // autoDetectFromClient will be done by MVLocalesListener instead + commandManager.get().usePerIssuerLocale(newValue, false); + }) + .build()); + + private final ConfigHeaderNode COMMAND_HEADER = node(ConfigHeaderNode.builder("command") + .comment("") + .comment("") + .build()); + + final ConfigNode RESOLVE_ALIAS_NAME = node(ConfigNode.builder("command.resolve-alias-name", Boolean.class) + .comment("If this is set to true, Multiverse will resolve world based on their alias names for commands and destinations.") + .comment("Normal world names will still be accepted.") + .comment("In the event you have multiple worlds with the same alias name, the first world found will be used.") + .defaultValue(true) + .name("resolve-alias-name") + .build()); + + final ConfigNode CONFIRM_MODE = node(ConfigNode.builder("command.confirm-mode", ConfirmMode.class) + .comment("") + .comment("This config option defines whether `/mv confirm` is needed before running a DANGEROUS action.") + .comment(" enable: `/mv confirm` is required.") + .comment(" player_only: `/mv confirm` only required when running command as a player.") + .comment(" disable_command_blocks: `/mv confirm` not required for command blocks.") + .comment(" disable_console: `/mv confirm` not required for the console.") + .comment(" disable: `/mv confirm` is not required.") + .defaultValue(ConfirmMode.ENABLE) + .name("confirm-mode") + .build()); + + final ConfigNode USE_CONFIRM_OTP = node(ConfigNode.builder("command.use-confirm-otp", Boolean.class) + .comment("") + .comment("If this is set to true, `/mv confirm` will include a 3 digit random number that must be entered to confirm the command.") + .comment("For example: `/mv confirm 726`") + .defaultValue(true) + .name("use-confirm-otp") + .build()); + + private final ConfigHeaderNode MISC_HEADER = node(ConfigHeaderNode.builder("misc") + .comment("") + .comment("") + .build()); + + final ConfigNode GLOBAL_DEBUG = node(ConfigNode.builder("misc.global-debug", Integer.class) + .comment("This is our debug flag to help identify issues with Multiverse.") + .comment("If you are having issues with Multiverse, please set this to 3 and then post your log to pastebin.com") + .comment("Otherwise, there's no need to touch this. If not instructed by a wiki page or developer.") + .comment(" 0 = Off, No debug messages") + .comment(" 1 = fine") + .comment(" 2 = finer") + .comment(" 3 = finest") + .defaultValue(0) + .name("global-debug") + .validator(value -> (value < 0 || value > 3) + ? Try.failure(new MultiverseException("Debug level must be between 0 and 3.")) + : Try.success(null)) + .onSetValue((oldValue, newValue) -> { + int level = Logging.getDebugLevel(); + Logging.setDebugLevel(newValue); + if (level != Logging.getDebugLevel()) { + pluginManager.callEvent(new MVDebugModeEvent(level)); + } + }) + .build()); + + final ConfigNode DEBUG_PERMISSIONS = node(ConfigNode.builder("misc.debug-permissions", Boolean.class) + .comment("Sets whether console will log every permission check done by all multiverse plugins.") + .comment("This will only work if the above 'global-debug' is set to 1 or more.") + .defaultValue(false) + .name("debug-permissions") + .onSetValue((oldValue, newValue) -> PermissionUtils.setDebugPermissions(newValue)) + .build()); + + final ConfigNode SILENT_START = node(ConfigNode.builder("misc.silent-start", Boolean.class) + .comment("") + .comment("If true, the startup console messages will no longer show.") + .defaultValue(false) + .name("silent-start") + .onSetValue((oldValue, newValue) -> Logging.setShowingConfig(!newValue)) + .build()); + + final ConfigNode SHOW_DONATION_MESSAGE = node(ConfigNode.builder("misc.show-donation-message", Boolean.class) + .comment("") + .comment("If you don't want to donate, you can set this to false and Multiverse will stop nagging you.") + .defaultValue(true) + .name("show-donation-message") + .build()); + + final ConfigNode VERSION = node(ConfigNode.builder("version", Double.class) + .comment("") + .comment("") + .comment("This just signifies the version number so we can see what version of config you have.") + .comment("NEVER TOUCH THIS VALUE") + .defaultValue(0.0) + .name(null) + .build()); + + // END CHECKSTYLE-SUPPRESSION: Javadoc + // END CHECKSTYLE-SUPPRESSION: MemberName + // END CHECKSTYLE-SUPPRESSION: Abbreviation + // END CHECKSTYLE-SUPPRESSION: VisibilityModifier + // END CHECKSTYLE-SUPPRESSION: MultipleStringLiterals + // END CHECKSTYLE-SUPPRESSION: LineLength +} diff --git a/src/main/java/org/mvplugins/multiverse/core/config/package-info.java b/src/main/java/org/mvplugins/multiverse/core/config/package-info.java new file mode 100644 index 000000000..87558785a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.config; diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSerializerProvider.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSerializerProvider.java new file mode 100644 index 000000000..f2eec7098 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSerializerProvider.java @@ -0,0 +1,187 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import com.dumptruckman.minecraft.util.Logging; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Provides default serializers for common types. + */ +public final class DefaultSerializerProvider { + + private static final Map, NodeSerializer> SERIALIZERS = new HashMap<>(); + + /** + * Adds a default serializer for the given type. + * + * @param type The type. + * @param serializer The serializer. + * @param The type. + */ + public static void addDefaultSerializer(@NotNull Class type, @NotNull NodeSerializer serializer) { + SERIALIZERS.put(type, serializer); + } + + /** + * Gets the default serializer for the given type. + * + * @param type The type. + * @param The type. + * @return The default serializer for the given type, or null if no default serializer exists. + */ + public static @Nullable NodeSerializer getDefaultSerializer(Class type) { + if (type.isEnum()) { + // Special case for enums + return (NodeSerializer) ENUM_SERIALIZER; + } + return (NodeSerializer) SERIALIZERS.get(type); + } + + private static final NodeSerializer ENUM_SERIALIZER = new NodeSerializer<>() { + @Override + public Enum deserialize(Object object, Class type) { + if (type.isInstance(object)) { + return (Enum) object; + } + return Enum.valueOf(type, String.valueOf(object).toUpperCase()); + } + + @Override + public Object serialize(Enum object, Class type) { + return object.name().toLowerCase(); + } + }; + + private static final NodeSerializer STRING_SERIALIZER = new NodeSerializer<>() { + @Override + public String deserialize(Object object, Class type) { + if (object instanceof String) { + return (String) object; + } + return String.valueOf(object); + } + + @Override + public Object serialize(String object, Class type) { + return object; + } + }; + + private static final NodeSerializer BOOLEAN_SERIALIZER = new NodeSerializer<>() { + @Override + public Boolean deserialize(Object object, Class type) { + if (object instanceof Boolean) { + return (Boolean) object; + } + String input = String.valueOf(object); + //todo: this is a copy from string parser + return switch (input.toLowerCase()) { + case "t", "true", "on", "y", "yes", "1", "allow" -> true; + case "f", "false", "off", "n", "no", "0", "deny" -> false; + default -> throw new RuntimeException("Unable to convert '" + input + "' to boolean."); + }; + } + + @Override + public Object serialize(Boolean object, Class type) { + return object; + } + }; + + private static final NodeSerializer INTEGER_SERIALIZER = new NodeSerializer<>() { + @Override + public Integer deserialize(Object object, Class type) { + if (object instanceof Integer) { + return (Integer) object; + } + return Integer.parseInt(String.valueOf(object)); + } + + @Override + public Object serialize(Integer object, Class type) { + return object; + } + }; + + private static final NodeSerializer DOUBLE_SERIALIZER = new NodeSerializer<>() { + @Override + public Double deserialize(Object object, Class type) { + if (object instanceof Double number) { + return number; + } + Logging.finer("Converting %s to double", object); + return Double.parseDouble(String.valueOf(object)); + } + + @Override + public Object serialize(Double object, Class type) { + return object; + } + }; + + private static final NodeSerializer FLOAT_SERIALIZER = new NodeSerializer<>() { + @Override + public Float deserialize(Object object, Class type) { + if (object instanceof Float number) { + return number; + } + Logging.finer("Converting %s to float", object); + return Float.parseFloat(String.valueOf(object)); + } + + @Override + public Object serialize(Float object, Class type) { + return object; + } + }; + + private static final NodeSerializer LONG_SERIALIZER = new NodeSerializer<>() { + @Override + public Long deserialize(Object object, Class type) { + if (object instanceof Long number) { + return number; + } + Logging.finer("Converting %s to long", object); + return Long.parseLong(String.valueOf(object)); + } + + @Override + public Object serialize(Long object, Class type) { + return object; + } + }; + + private static final NodeSerializer LOCALE_SERIALIZER = new NodeSerializer<>() { + @Override + public Locale deserialize(Object object, Class type) { + if (object instanceof Locale) { + return (Locale) object; + } + String[] split = String.valueOf(object).split("_", 2); + return split.length > 1 ? new Locale(split[0], split[1]) : new Locale(split[0]); + } + + @Override + public Object serialize(Locale object, Class type) { + return object.toLanguageTag(); + } + }; + + static { + addDefaultSerializer(String.class, STRING_SERIALIZER); + addDefaultSerializer(Boolean.class, BOOLEAN_SERIALIZER); + addDefaultSerializer(Integer.class, INTEGER_SERIALIZER); + addDefaultSerializer(Double.class, DOUBLE_SERIALIZER); + addDefaultSerializer(Float.class, FLOAT_SERIALIZER); + addDefaultSerializer(Long.class, LONG_SERIALIZER); + addDefaultSerializer(Locale.class, LOCALE_SERIALIZER); + } + + private DefaultSerializerProvider() { + // Prevent instantiation as this is a static utility class + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultStringParserProvider.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultStringParserProvider.java new file mode 100644 index 000000000..464ebaeac --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultStringParserProvider.java @@ -0,0 +1,84 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +import java.util.HashMap; +import java.util.Map; + +import co.aikar.commands.ACFUtil; +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.mvplugins.multiverse.core.exceptions.MultiverseException; + +/** + * Provides default string parsers for common types. + */ +public final class DefaultStringParserProvider { + + private static final Map, NodeStringParser> PARSERS = new HashMap<>(); + + /** + * Adds a default string parser for the given type. + * + * @param clazz The type. + * @param parser The string parser. + */ + public static void addDefaultStringParser(Class clazz, NodeStringParser parser) { + PARSERS.put(clazz, parser); + } + + /** + * Gets the default string parser for the given type. + * + * @param clazz The type. + * @param The type. + * @return The default string parser for the given type, or null if no default string parser exists. + */ + public static NodeStringParser getDefaultStringParser(Class clazz) { + if (clazz.isEnum()) { + // Special case for enums + return (NodeStringParser) ENUM_STRING_PARSER; + } + return (NodeStringParser) PARSERS.get(clazz); + } + + private static final NodeStringParser ENUM_STRING_PARSER = (input, type) -> Try.of( + () -> Enum.valueOf(type, input.toUpperCase())); + + private static final NodeStringParser STRING_STRING_PARSER = (input, type) -> Try.of( + () -> input); + + private static final NodeStringParser BOOLEAN_STRING_PARSER = (input, type) -> Try.of( + () -> switch (String.valueOf(input).toLowerCase()) { + case "t", "true", "on", "y", "yes", "1", "allow" -> true; + case "f", "false", "off", "n", "no", "0", "deny" -> false; + default -> throw new MultiverseException("Unable to convert '" + input + "' to boolean. Please use 'true' or 'false'"); + }); + + private static final NodeStringParser INTEGER_STRING_PARSER = (input, type) -> Try.of( + () -> ACFUtil.parseInt(input)) + .flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (integer)"))); + + private static final NodeStringParser DOUBLE_STRING_PARSER = (input, type) -> Try.of( + () -> ACFUtil.parseDouble(input)) + .flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (double)"))); + + private static final NodeStringParser FLOAT_STRING_PARSER = (input, type) -> Try.of( + () -> ACFUtil.parseFloat(input)) + .flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (float)"))); + + private static final NodeStringParser LONG_STRING_PARSER = (input, type) -> Try.of( + () -> ACFUtil.parseLong(input)) + .flatMap(number -> Option.of(number).toTry(() -> new MultiverseException("Unable to convert '" + input + "' to number. (long)"))); + + static { + addDefaultStringParser(String.class, STRING_STRING_PARSER); + addDefaultStringParser(Boolean.class, BOOLEAN_STRING_PARSER); + addDefaultStringParser(Integer.class, INTEGER_STRING_PARSER); + addDefaultStringParser(Double.class, DOUBLE_STRING_PARSER); + addDefaultStringParser(Float.class, FLOAT_STRING_PARSER); + addDefaultStringParser(Long.class, LONG_STRING_PARSER); + } + + private DefaultStringParserProvider() { + // Prevent instantiation as this is a static utility class + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSuggesterProvider.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSuggesterProvider.java new file mode 100644 index 000000000..62bce4d05 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/DefaultSuggesterProvider.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.jetbrains.annotations.Nullable; + +/** + * Provides default suggestions for common types. + */ +public final class DefaultSuggesterProvider { + + private static final Map, NodeSuggester> SUGGESTERS = new HashMap<>(); + + /** + * Adds a default suggester for the given type. + * + * @param clazz The type. + * @param suggester The suggester. + */ + public static void addDefaultSuggester(Class clazz, NodeSuggester suggester) { + SUGGESTERS.put(clazz, suggester); + } + + /** + * Gets the default suggester for the given type. + * + * @param clazz The type. + * @return The default suggester for the given type, or null if no default suggester exists. + */ + public static @Nullable NodeSuggester getDefaultSuggester(Class clazz) { + if (clazz.isEnum()) { + // Special case for enums + return enumSuggester(clazz); + } + return SUGGESTERS.get(clazz); + } + + private static NodeSuggester enumSuggester(Class clazz) { + return input -> Arrays.stream(clazz.getEnumConstants()) + .map(Object::toString) + .map(String::toLowerCase) + .collect(Collectors.toList()); + } + + private static final NodeSuggester BOOLEAN_SUGGESTER = input -> List.of("true", "false"); + + private static final NodeSuggester INTEGER_SUGGESTER = input -> IntStream.range(1, 10) + .boxed() + .map(String::valueOf) + .collect(Collectors.toList()); + + static { + addDefaultSuggester(Boolean.class, BOOLEAN_SUGGESTER); + addDefaultSuggester(Integer.class, INTEGER_SUGGESTER); + } + + private DefaultSuggesterProvider() { + // Prevent instantiation as this is a static utility class + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSerializer.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSerializer.java new file mode 100644 index 000000000..ef3c71bae --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSerializer.java @@ -0,0 +1,26 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +/** + * A function that serializes and deserializes objects to and from YAML. + * + * @param The type of the object to serialize and deserialize. + */ +public interface NodeSerializer { + /** + * Deserializes an object from YAML. + * + * @param object The object to deserialize. + * @param type The type of the object to deserialize. + * @return The deserialized typed value. + */ + T deserialize(Object object, Class type); + + /** + * Serializes an object to YAML. + * + * @param object The object to serialize. + * @param type The type of the object to serialize. + * @return The serialized object. + */ + Object serialize(T object, Class type); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeStringParser.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeStringParser.java new file mode 100644 index 000000000..72c83443c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeStringParser.java @@ -0,0 +1,22 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A function that parses a string into a node value object of type {@link T}. + * + * @param The type of the object to parse. + */ +@FunctionalInterface +public interface NodeStringParser { + /** + * Parses a string into a node value object of type {@link T}. + * + * @param string The string to parse. + * @param type The type of the object to parse. + * @return The parsed object, or {@link Try.Failure} if the string could not be parsed. + */ + @NotNull Try parse(@Nullable String string, @NotNull Class type); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSuggester.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSuggester.java new file mode 100644 index 000000000..43176fbfe --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/NodeSuggester.java @@ -0,0 +1,20 @@ +package org.mvplugins.multiverse.core.configuration.functions; + +import java.util.Collection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A function that suggests possible values for a node value. + */ +@FunctionalInterface +public interface NodeSuggester { + /** + * Suggests possible values for a node value. Generated based on the current user input. + * + * @param input The current partial user input + * @return The possible values. + */ + @NotNull Collection suggest(@Nullable String input); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/functions/package-info.java b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/package-info.java new file mode 100644 index 000000000..c9e8c3aaa --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/functions/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.configuration.functions; diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/BaseConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/BaseConfigurationHandle.java new file mode 100644 index 000000000..e085b2761 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/BaseConfigurationHandle.java @@ -0,0 +1,255 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.util.List; +import java.util.logging.Logger; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.ListValueNode; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; +import org.mvplugins.multiverse.core.configuration.node.ValueNode; + +/** + * Generic configuration handle for all ConfigurationSection types. + * + * @param The configuration type. + */ +public abstract class BaseConfigurationHandle { + + protected final @Nullable Logger logger; + protected final @NotNull NodeGroup nodes; + protected final @Nullable ConfigMigrator migrator; + + protected C config; + + protected BaseConfigurationHandle( + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + this.logger = logger; + this.nodes = nodes; + this.migrator = migrator; + } + + /** + * Loads the configuration. + * + * @return Whether the configuration was loaded or its given error. + */ + public Try load() { + return Try.run(() -> { + migrateConfig(); + setUpNodes(); + }); + } + + /** + * Migrates the configuration. + */ + protected void migrateConfig() { + if (migrator != null) { + migrator.migrate(config); + } + } + + /** + * Sets up the nodes. + */ + protected void setUpNodes() { + if (nodes == null || nodes.isEmpty()) { + return; + } + + nodes.forEach(node -> { + if (node instanceof ValueNode valueNode) { + set(valueNode, get(valueNode)); + } + }); + } + + /** + * Gets the value of a node, if the node has a default value, it will be returned if the node is not found. + * + * @param node The node to get the value of. + * @return The value of the node. + */ + public T get(@NotNull ValueNode node) { + if (node.getSerializer() == null) { + return Option.of(config.getObject(node.getPath(), node.getType())).getOrElse(node::getDefaultValue); + } + return Try.of(() -> node.getSerializer() + .deserialize(config.get(node.getPath(), node.getDefaultValue()), node.getType())) + .onFailure(e -> Logging.warning("Failed to deserialize node %s: %s", node.getPath(), e.getMessage())) + .getOrElse(node::getDefaultValue); + } + + /** + * Sets the value of a node, if the validator is not null, it will be tested first. + * + * @param node The node to set the value of. + * @param value The value to set. + * @param The type of the node value. + * @return Empty try if the value was set, try containing an error otherwise. + */ + public Try set(@NotNull ValueNode node, T value) { + return node.validate(value).map(ignore -> { + T oldValue = get(node); + if (node.getSerializer() != null) { + var serialized = node.getSerializer().serialize(value, node.getType()); + config.set(node.getPath(), serialized); + } else { + config.set(node.getPath(), value); + } + node.onSetValue(oldValue, get(node)); + return null; + }); + } + + /** + * Adds an item to a list node. + * + * @param node The list node to add the item to. + * @param itemValue The value of the item to add. + * @param The type of the list item. + * @return Empty try if the item was added, try containing an error otherwise. + */ + public Try add(@NotNull ListValueNode node, I itemValue) { + return node.validateItem(itemValue).map(ignore -> { + var serialized = node.getItemSerializer() != null + ? node.getItemSerializer().serialize(itemValue, node.getItemType()) + : itemValue; + List valueList = config.getList(node.getPath()); + if (valueList == null) { + throw new IllegalArgumentException("Cannot add item to non-list node"); + } + valueList.add(serialized); + config.set(node.getPath(), valueList); + node.onSetItemValue(null, itemValue); + return null; + }); + } + + /** + * Removes an item from a list node. + * + * @param node The list node to remove the item from. + * @param itemValue The value of the item to remove. + * @param The type of the list item. + * @return Empty try if the item was removed, try containing an error otherwise. + */ + public Try remove(@NotNull ListValueNode node, I itemValue) { + return node.validateItem(itemValue).map(ignore -> { + var serialized = node.getItemSerializer() != null + ? node.getItemSerializer().serialize(itemValue, node.getItemType()) + : itemValue; + List valueList = config.getList(node.getPath()); + if (valueList == null) { + throw new IllegalArgumentException("Cannot remove item from non-list node"); + } + if (!valueList.remove(serialized)) { + throw new IllegalArgumentException("Cannot remove item from list node"); + } + config.set(node.getPath(), valueList); + node.onSetItemValue(itemValue, null); + return null; + }); + } + + /** + * Sets the default value of a node. + * + * @param node The node to set the default value of. + * @param The type of the node value. + * @return Empty try if the value was set, try containing an error otherwise. + */ + public Try reset(@NotNull ValueNode node) { + return Try.run(() -> set(node, node.getDefaultValue())); + } + + /** + * Gets the configuration. + * + * @return The configuration. + */ + public C getConfig() { + return config; + } + + /** + * Gets the configuration. Mainly used for {@link StringPropertyHandle}. + * + * @return The configuration. + */ + @NotNull NodeGroup getNodes() { + return nodes; + } + + /** + * Abstract builder for {@link BaseConfigurationHandle}. + * + * @param The configuration type. + * @param The builder type. + */ + public abstract static class Builder> { + + protected final @NotNull NodeGroup nodes; + protected @Nullable Logger logger; + protected @Nullable ConfigMigrator migrator; + + protected Builder(@NotNull NodeGroup nodes) { + this.nodes = nodes; + } + + /** + * Sets the logger. + * + * @param logger The logger. + * @return The builder. + */ + public B logger(@Nullable Logger logger) { + this.logger = logger; + return self(); + } + + /** + * Sets the logger. + * + * @param plugin The plugin to get the logger from. + * @return The builder. + */ + public B logger(Plugin plugin) { + this.logger = plugin.getLogger(); + return self(); + } + + /** + * Sets the migrator. + * + * @param migrator The migrator. + * @return The builder. + */ + public B migrator(@Nullable ConfigMigrator migrator) { + this.migrator = migrator; + return self(); + } + + /** + * Builds the configuration handle. + * + * @return The configuration handle. + */ + public abstract @NotNull BaseConfigurationHandle build(); + + @SuppressWarnings("unchecked") + protected B self() { + return (B) this; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/CommentedConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/CommentedConfigurationHandle.java new file mode 100644 index 000000000..d5b0c2a0c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/CommentedConfigurationHandle.java @@ -0,0 +1,110 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration; +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.CommentedNode; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; +import org.mvplugins.multiverse.core.configuration.node.ValueNode; + +/** + * Configuration handle for commented YAML files. + */ +public class CommentedConfigurationHandle extends FileConfigurationHandle { + + /** + * Creates a new builder for a {@link CommentedConfigurationHandle}. + * + * @param configPath The path to the config file. + * @param nodes The nodes. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull Path configPath, @NotNull NodeGroup nodes) { + return new Builder(configPath, nodes); + } + + protected CommentedConfigurationHandle( + @NotNull Path configPath, + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + super(configPath, logger, nodes, migrator); + } + + /** + * {@inheritDoc} + */ + @Override + protected void loadConfigObject() throws IOException { + config = new CommentedConfiguration(configPath, logger); + if (!config.load()) { + throw new IOException("Failed to load commented config file " + configPath + + ". See console for details."); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void setUpNodes() { + if (nodes == null || nodes.isEmpty()) { + return; + } + + CommentedConfiguration oldConfig = config; + this.config = new CommentedConfiguration(configPath, logger); + + nodes.forEach(node -> { + if (node instanceof CommentedNode typedNode) { + if (typedNode.getComments().length > 0) { + config.addComment(typedNode.getPath(), typedNode.getComments()); + } + } + if (node instanceof ValueNode valueNode) { + //noinspection unchecked + Option.of(oldConfig.get(valueNode.getPath())) + .peek(oldValue -> { + this.config.set(valueNode.getPath(), oldValue); + set(valueNode, get(valueNode)); + }) + .onEmpty(() -> reset(valueNode)); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public Try save() { + // TODO: There is no way to check if the save was successful. + return Try.run(() -> config.save()); + } + + /** + * Builder for {@link CommentedConfigurationHandle}. + */ + public static class Builder extends FileConfigurationHandle.Builder { + + protected Builder(@NotNull Path configPath, @NotNull NodeGroup nodes) { + super(configPath, nodes); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull CommentedConfigurationHandle build() { + return new CommentedConfigurationHandle(configPath, logger, nodes, migrator); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/ConfigurationSectionHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/ConfigurationSectionHandle.java new file mode 100644 index 000000000..ad641a8a2 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/ConfigurationSectionHandle.java @@ -0,0 +1,70 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.util.logging.Logger; + +import io.vavr.control.Try; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; + +/** + * Configuration handle for a single configuration section. + */ +public class ConfigurationSectionHandle extends BaseConfigurationHandle { + /** + * Creates a new builder for a {@link ConfigurationSectionHandle}. + * + * @param configurationSection The configuration section. + * @param nodes The nodes. + * @return The builder. + */ + public static Builder> builder( + @NotNull C configurationSection, @NotNull NodeGroup nodes) { + return new Builder<>(configurationSection, nodes); + } + + protected ConfigurationSectionHandle( + @NotNull C configurationSection, + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + super(logger, nodes, migrator); + this.config = configurationSection; + } + + /** + * Loads the configuration with a new configuration section. + * + * @param section The configuration section. + * @return Whether the configuration was loaded or its given error. + */ + public Try load(@NotNull C section) { + this.config = section; + return load(); + } + + /** + * Builder for {@link ConfigurationSectionHandle}. + * + * @param The builder type. + */ + public static class Builder> extends BaseConfigurationHandle.Builder { + protected final C configurationSection; + + protected Builder(@NotNull C configurationSection, @NotNull NodeGroup nodes) { + super(nodes); + this.configurationSection = configurationSection; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull ConfigurationSectionHandle build() { + return new ConfigurationSectionHandle<>(configurationSection, logger, nodes, migrator); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java new file mode 100644 index 000000000..e95e34e32 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/FileConfigurationHandle.java @@ -0,0 +1,135 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Logger; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; + +/** + * Generic configuration handle for file based configurations. + * + * @param The configuration type. + */ +public abstract class FileConfigurationHandle extends BaseConfigurationHandle { + + protected final @NotNull Path configPath; + protected final @NotNull File configFile; + + protected FileConfigurationHandle( + @NotNull Path configPath, + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + super(logger, nodes, migrator); + this.configPath = configPath; + this.configFile = configPath.toFile(); + } + + /** + * {@inheritDoc} + */ + @Override + public Try load() { + return tryLoadConfigFile().andThenTry(() -> { + migrateConfig(); + setUpNodes(); + }); + } + + private Try tryLoadConfigFile() { + return Try.run(() -> { + createConfigFile(); + loadConfigObject(); + }).fold(this::handleLoadConfigFailure, Try::success); + } + + private @NotNull Try handleLoadConfigFailure(Throwable throwable) { + Logging.severe("Failed to load config file: " + configFile.getName(), throwable); + throwable.printStackTrace(); + return Try.run(() -> { + Path brokenConfigPath = configPath.resolveSibling(configFile.getName() + ".broken." + System.currentTimeMillis()); + Logging.severe("Moving broken config file to: " + brokenConfigPath.getFileName()); + Files.copy(configPath, brokenConfigPath); + Files.delete(configPath); + }).andThenTry(() -> { + Logging.severe("Multiverse-Core will now regenerate a fresh config file with all default options!"); + createConfigFile(); + loadConfigObject(); + }); + } + + /** + * Create a new config file if file does not exist. + * + * @return Whether the file was created or its given error. + */ + protected Try createConfigFile() { + return Try.run(() -> { + if (configFile.exists()) { + return; + } + if (!configFile.createNewFile()) { + throw new IOException("Failed to create config file: " + configFile.getName()); + } + }); + } + + /** + * Loads the configuration object. + */ + protected abstract void loadConfigObject() throws IOException, InvalidConfigurationException; + + /** + * Saves the configuration. + */ + public abstract Try save(); + + /** + * Checks if the configuration is loaded. + * + * @return True if the configuration is loaded, false otherwise. + */ + public boolean isLoaded() { + return config != null; + } + + /** + * Abstract builder for {@link FileConfigurationHandle}. + * + * @param The configuration type. + * @param The builder type. + */ + public abstract static class Builder> + extends BaseConfigurationHandle.Builder { + + protected @NotNull Path configPath; + + protected Builder(@NotNull Path configPath, @NotNull NodeGroup nodes) { + super(nodes); + this.configPath = configPath; + } + + /** + * Builds the configuration handle. + * + * @return The configuration handle. + */ + public abstract @NotNull FileConfigurationHandle build(); + + @SuppressWarnings("unchecked") + protected B self() { + return (B) this; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/MemoryConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/MemoryConfigurationHandle.java new file mode 100644 index 000000000..7625156c9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/MemoryConfigurationHandle.java @@ -0,0 +1,78 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import io.vavr.control.Option; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemoryConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; +import org.mvplugins.multiverse.core.configuration.node.ValueNode; + +import java.util.logging.Logger; + +public class MemoryConfigurationHandle extends ConfigurationSectionHandle { + + /** + * Creates a new builder for a {@link ConfigurationSectionHandle}. + * + * @param configurationSection The configuration section. + * @param nodes The nodes. + * @return The builder. + */ + public static MemoryConfigurationHandle.Builder> builder( + @NotNull ConfigurationSection configurationSection, @NotNull NodeGroup nodes) { + return new MemoryConfigurationHandle.Builder<>(configurationSection, nodes); + } + + protected MemoryConfigurationHandle( + @NotNull ConfigurationSection configurationSection, + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + super(configurationSection, logger, nodes, migrator); + } + + @Override + protected void setUpNodes() { + if (nodes == null || nodes.isEmpty()) { + return; + } + + ConfigurationSection oldConfig = config; + config = new MemoryConfiguration(); + + nodes.forEach(node -> { + if (!(node instanceof ValueNode valueNode)) { + return; + } + //todo: this is a copied from CommentedConfigurationHandle + Option.of(oldConfig.get(valueNode.getPath())) + .peek(oldValue -> { + this.config.set(valueNode.getPath(), oldValue); + set(valueNode, get(valueNode)); + }) + .onEmpty(() -> reset(valueNode)); + }); + } + + /** + * Builder for {@link MemoryConfigurationHandle}. + * + * @param The builder type. + */ + public static class Builder> extends ConfigurationSectionHandle.Builder { + + protected Builder(@NotNull ConfigurationSection configurationSection, @NotNull NodeGroup nodes) { + super(configurationSection, nodes); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull MemoryConfigurationHandle build() { + return new MemoryConfigurationHandle(configurationSection, logger, nodes, migrator); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/PropertyModifyAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/PropertyModifyAction.java new file mode 100644 index 000000000..017c4a5f1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/PropertyModifyAction.java @@ -0,0 +1,42 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +/** + * The type of modification to a config. + */ +public enum PropertyModifyAction { + /** + * Sets a new value based on the provided input. + */ + SET(true), + + /** + * Add an item to a list. Only applies to values of type List. + */ + ADD(true), + + /** + * Remove an item from a list. Only applies to values of type List. + */ + REMOVE(true), + + /** + * Reset the value to the default. + */ + RESET(false), + ; + + private final boolean requireValue; + + PropertyModifyAction(boolean requireValue) { + this.requireValue = requireValue; + } + + /** + * Whether this action requires a value. + * + * @return True if this action requires a value, false otherwise. + */ + public boolean isRequireValue() { + return requireValue; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/StringPropertyHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/StringPropertyHandle.java new file mode 100644 index 000000000..5776afb15 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/StringPropertyHandle.java @@ -0,0 +1,140 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.util.Collection; +import java.util.Collections; + +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.node.ConfigNodeNotFoundException; +import org.mvplugins.multiverse.core.configuration.node.ListValueNode; +import org.mvplugins.multiverse.core.configuration.node.Node; +import org.mvplugins.multiverse.core.configuration.node.ValueNode; + +public class StringPropertyHandle { + private final @NotNull BaseConfigurationHandle handle; + + /** + * Creates a new string property handle. + * + * @param handle The handle to wrap. + */ + public StringPropertyHandle(@NotNull BaseConfigurationHandle handle) { + this.handle = handle; + } + + public Collection getAllPropertyNames() { + return handle.getNodes().getNames(); + } + + public Collection getModifiablePropertyNames(PropertyModifyAction action) { + return switch (action) { + case SET, RESET -> handle.getNodes().getNames(); + + case ADD, REMOVE -> handle.getNodes().stream() + .filter(node -> node instanceof ListValueNode) + .map(node -> ((ValueNode) node).getName()) + .filter(Option::isDefined) + .map(Option::get) + .toList(); + + default -> Collections.emptyList(); + }; + } + + public Try> getPropertyType(@Nullable String name) { + return findNode(name, ValueNode.class).map(ValueNode::getType); + } + + public Collection getSuggestedPropertyValue( + @Nullable String name, @Nullable String input, @NotNull PropertyModifyAction action) { + return switch (action) { + case SET -> findNode(name, ValueNode.class) + .map(node -> node.suggest(input)) + .getOrElse(Collections.emptyList()); + + case ADD -> findNode(name, ListValueNode.class) + .map(node -> node.suggestItem(input)) + .getOrElse(Collections.emptyList()); + + case REMOVE -> findNode(name, ListValueNode.class) + .map(node -> handle.get((ListValueNode) node)) + .map(valueList -> valueList.stream() + .map(String::valueOf) + .toList()) + .getOrElse(Collections.emptyList()); + + default -> Collections.emptyList(); + }; + } + + public Try getProperty(@Nullable String name) { + return findNode(name, ValueNode.class).map(node -> handle.get(node)); + } + + public Try setProperty(@Nullable String name, @Nullable Object value) { + return findNode(name, ValueNode.class).flatMap(node -> handle.set(node, value)); + } + + public Try addProperty(@Nullable String name, @Nullable Object value) { + return findNode(name, ListValueNode.class).flatMap(node -> handle.add(node, value)); + } + + public Try removeProperty(@Nullable String name, @Nullable Object value) { + return findNode(name, ListValueNode.class).flatMap(node -> handle.remove(node, value)); + } + + public Try resetProperty(@Nullable String name) { + return findNode(name, ValueNode.class).flatMap(node -> handle.reset(node)); + } + + public Try modifyProperty( + @Nullable String name, @Nullable Object value, @NotNull PropertyModifyAction action) { + return switch (action) { + case SET -> setProperty(name, value); + case ADD -> addProperty(name, value); + case REMOVE -> removeProperty(name, value); + case RESET -> resetProperty(name); + default -> Try.failure(new IllegalArgumentException("Unknown action: " + action)); + }; + } + + public Try setPropertyString(@Nullable String name, @Nullable String value) { + return findNode(name, ValueNode.class) + .flatMap(node -> node.parseFromString(value) + .flatMap(parsedValue -> handle.set(node, parsedValue))); + } + + public Try addPropertyString(@Nullable String name, @Nullable String value) { + return findNode(name, ListValueNode.class) + .flatMap(node -> node.parseItemFromString(value) + .flatMap(parsedValue -> handle.add(node, parsedValue))); + } + + public Try removePropertyString(@Nullable String name, @Nullable String value) { + return findNode(name, ListValueNode.class) + .flatMap(node -> node.parseItemFromString(value) + .flatMap(parsedValue -> handle.remove(node, parsedValue))); + } + + public Try modifyPropertyString( + @Nullable String name, @Nullable String value, @NotNull PropertyModifyAction action) { + if (action.isRequireValue() && (value == null)) { + return Try.failure(new IllegalArgumentException("Value is required for PropertyModifyAction: " + action)); + } + return switch (action) { + case SET -> setPropertyString(name, value); + case ADD -> addPropertyString(name, value); + case REMOVE -> removePropertyString(name, value); + case RESET -> resetProperty(name); + default -> Try.failure(new IllegalArgumentException("Unknown action: " + action)); + }; + } + + private Try findNode(@Nullable String name, @NotNull Class type) { + return handle.getNodes().findNode(name, type) + .toTry(() -> new ConfigNodeNotFoundException(name)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/YamlConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/YamlConfigurationHandle.java new file mode 100644 index 000000000..c387d4306 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/YamlConfigurationHandle.java @@ -0,0 +1,76 @@ +package org.mvplugins.multiverse.core.configuration.handle; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import io.vavr.control.Try; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; + +/** + * Configuration handle for YAML files. + */ +public class YamlConfigurationHandle extends FileConfigurationHandle { + + /** + * Creates a new builder for {@link YamlConfigurationHandle}. + * + * @param configPath The path to the config file. + * @param nodes The nodes. + * @return The builder. + */ + public static @NotNull Builder builder(@NotNull Path configPath, @NotNull NodeGroup nodes) { + return new Builder<>(configPath, nodes); + } + + protected YamlConfigurationHandle( + @NotNull Path configPath, + @Nullable Logger logger, + @NotNull NodeGroup nodes, + @Nullable ConfigMigrator migrator) { + super(configPath, logger, nodes, migrator); + } + + /** + * {@inheritDoc} + */ + @Override + protected void loadConfigObject() throws IOException, InvalidConfigurationException { + config = new YamlConfiguration(); + config.load(configFile); + } + + /** + * {@inheritDoc} + */ + @Override + public Try save() { + return Try.run(() -> config.save(configFile)); + } + + /** + * Builder for {@link YamlConfigurationHandle}. + * + * @param The type of the builder. + */ + public static class Builder> extends FileConfigurationHandle.Builder { + + protected Builder(@NotNull Path configPath, @NotNull NodeGroup nodes) { + super(configPath, nodes); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull YamlConfigurationHandle build() { + return new YamlConfigurationHandle(configPath, logger, nodes, migrator); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/handle/package-info.java b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/package-info.java new file mode 100644 index 000000000..23277a801 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/handle/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.configuration.handle; diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/BooleanMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/BooleanMigratorAction.java new file mode 100644 index 000000000..bcca444bb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/BooleanMigratorAction.java @@ -0,0 +1,28 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import co.aikar.commands.ACFUtil; +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action that converts a string value to a boolean. + */ +public class BooleanMigratorAction implements MigratorAction { + + public static BooleanMigratorAction of(String path) { + return new BooleanMigratorAction(path); + } + + private final String path; + + protected BooleanMigratorAction(String path) { + this.path = path; + } + + @Override + public void migrate(ConfigurationSection config) { + config.set(path, ACFUtil.isTruthy(config.getString(path, ""))); + Logging.info("Converted %s to boolean %s", path, config.getBoolean(path)); + } +} + diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/ConfigMigrator.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/ConfigMigrator.java new file mode 100644 index 000000000..3e1bb6d6f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/ConfigMigrator.java @@ -0,0 +1,107 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import java.util.ArrayList; +import java.util.List; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +import org.mvplugins.multiverse.core.configuration.node.ValueNode; + +/** + * Helper class for migrating configs to the latest config version. + */ +public class ConfigMigrator { + + /** + * Creates a new builder for a ConfigMigrator. + * + * @param versionNode The node that stores the version number of the config. + * Default value should be the current latest version number. + * @return The builder instance. + */ + public static Builder builder(ValueNode versionNode) { + return new Builder(versionNode); + } + + private final ValueNode versionNode; + private final List versionMigrators; + + protected ConfigMigrator(ValueNode versionNode, List versionMigrators) { + this.versionNode = versionNode; + this.versionMigrators = versionMigrators; + } + + /** + * Migrates the config to the latest version if necessary. + * + * @param config The target settings instance to migrate. + */ + public void migrate(ConfigurationSection config) { + if (config.getKeys(false).isEmpty()) { + config.set(versionNode.getPath(), getLatestVersion()); + return; + } + + double versionNumber = config.getDouble(versionNode.getPath()); + for (VersionMigrator versionMigrator : versionMigrators) { + if (versionNumber < versionMigrator.getVersion()) { + Logging.info("Migrating config from version %s to %s...", versionNumber, versionMigrator.getVersion()); + versionMigrator.migrate(config); + // Set the version number to the latest version number + config.set(versionNode.getPath(), versionMigrator.getVersion()); + } + } + } + + /** + * Gets the latest version number of the config. + * + * @return The latest version number. + */ + private double getLatestVersion() { + if (versionMigrators.isEmpty()) { + return 0.0; + } + return versionMigrators.get(versionMigrators.size() - 1).getVersion(); + } + + /** + * A builder for a ConfigMigrator. + */ + public static class Builder { + private final ValueNode versionNode; + private final List versionMigrators; + + /** + * Creates a new builder for a ConfigMigrator. + * + * @param versionNode The node that stores the version number of the config. + * Default value should be the current latest version number. + */ + public Builder(ValueNode versionNode) { + this.versionNode = versionNode; + this.versionMigrators = new ArrayList<>(); + } + + /** + * Adds a version migrator to the list of migrators. + * + * @param versionMigrator The migrator to add. + * @return The builder instance. + */ + public Builder addVersionMigrator(VersionMigrator versionMigrator) { + versionMigrators.add(versionMigrator); + return this; + } + + /** + * Builds the ConfigMigrator. + * + * @return The built ConfigMigrator. + */ + public ConfigMigrator build() { + return new ConfigMigrator(versionNode, versionMigrators); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DeleteMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DeleteMigratorAction.java new file mode 100644 index 000000000..f1177200b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DeleteMigratorAction.java @@ -0,0 +1,23 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +public class DeleteMigratorAction implements MigratorAction { + + public static DeleteMigratorAction of(String path) { + return new DeleteMigratorAction(path); + } + + private final String path; + + DeleteMigratorAction(String path) { + this.path = path; + } + + @Override + public void migrate(ConfigurationSection config) { + config.set(path, null); + Logging.info("Deleted %s", path); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DoubleMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DoubleMigratorAction.java new file mode 100644 index 000000000..8be1238b1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/DoubleMigratorAction.java @@ -0,0 +1,24 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import co.aikar.commands.ACFUtil; +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +public class DoubleMigratorAction implements MigratorAction { + + public static DoubleMigratorAction of(String path) { + return new DoubleMigratorAction(path); + } + + private final String path; + + public DoubleMigratorAction(String path) { + this.path = path; + } + + @Override + public void migrate(ConfigurationSection config) { + config.set(path, ACFUtil.parseDouble(config.getString(path))); + Logging.info("Converted %s to double %s", path, config.getDouble(path)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/IntegerMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/IntegerMigratorAction.java new file mode 100644 index 000000000..a2f6b590b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/IntegerMigratorAction.java @@ -0,0 +1,27 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import co.aikar.commands.ACFUtil; +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action that converts a string value to an integer. + */ +public class IntegerMigratorAction implements MigratorAction { + + public static IntegerMigratorAction of(String path) { + return new IntegerMigratorAction(path); + } + + private final String path; + + public IntegerMigratorAction(String path) { + this.path = path; + } + + @Override + public void migrate(ConfigurationSection config) { + config.set(path, ACFUtil.parseInt(config.getString(path))); + Logging.info("Converted %s to integer %s", path, config.getInt(path)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/InvertBoolMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/InvertBoolMigratorAction.java new file mode 100644 index 000000000..0520c71a0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/InvertBoolMigratorAction.java @@ -0,0 +1,36 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action that inverts a boolean value for a given path. + */ +public class InvertBoolMigratorAction implements MigratorAction { + + /** + * Creates a new migrator action that inverts a boolean value for a given path. + * + * @param path The path to invert value of. + * @return The new migrator action. + */ + public static InvertBoolMigratorAction of(String path) { + return new InvertBoolMigratorAction(path); + } + + private final String path; + + protected InvertBoolMigratorAction(String path) { + this.path = path; + } + + /** + * {@inheritDoc} + */ + @Override + public void migrate(ConfigurationSection config) { + boolean boolValue = !config.getBoolean(path); + config.set(path, boolValue); + Logging.info("Inverted %s to boolean %s", path, boolValue); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/LongMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/LongMigratorAction.java new file mode 100644 index 000000000..14f762793 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/LongMigratorAction.java @@ -0,0 +1,30 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import co.aikar.commands.ACFUtil; +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action that converts a string value to a long. + */ +public class LongMigratorAction implements MigratorAction { + + public static LongMigratorAction of(String path) { + return new LongMigratorAction(path); + } + + private final String path; + + LongMigratorAction(String path) { + this.path = path; + } + + /** + * {@inheritDoc} + */ + @Override + public void migrate(ConfigurationSection config) { + config.set(path, ACFUtil.parseLong(config.getString(path))); + Logging.info("Converted %s to long %s", path, config.getLong(path)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MigratorAction.java new file mode 100644 index 000000000..c0596f4d9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MigratorAction.java @@ -0,0 +1,16 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import org.bukkit.configuration.ConfigurationSection; + +/** + * A migrator action is a single action that is performed when migrating a config. + */ +public interface MigratorAction { + + /** + * Performs the migration action. + * + * @param config The target settings instance to migrate. + */ + void migrate(ConfigurationSection config); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MoveMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MoveMigratorAction.java new file mode 100644 index 000000000..989d8a61f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/MoveMigratorAction.java @@ -0,0 +1,44 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import java.util.Optional; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action that moves a value from one path to another. + */ +public class MoveMigratorAction implements MigratorAction { + + /** + * Creates a new migrator action that moves a value from one path to another. + * + * @param fromPath The path to move value from. + * @param toPath The path to move value to. + * @return The new migrator action. + */ + public static MoveMigratorAction of(String fromPath, String toPath) { + return new MoveMigratorAction(fromPath, toPath); + } + + private final String fromPath; + private final String toPath; + + protected MoveMigratorAction(String fromPath, String toPath) { + this.fromPath = fromPath; + this.toPath = toPath; + } + + /** + * {@inheritDoc} + */ + @Override + public void migrate(ConfigurationSection config) { + Optional.ofNullable(config.get(fromPath)) + .ifPresent(value -> { + config.set(fromPath, null); + config.set(toPath, value); + Logging.config("Moved path %s to %s", fromPath, toPath); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/NullStringMigratorAction.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/NullStringMigratorAction.java new file mode 100644 index 000000000..4d15b19bb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/NullStringMigratorAction.java @@ -0,0 +1,29 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; + +/** + * Single migrator action changes a string value of "null" to an empty string. + */ +public class NullStringMigratorAction implements MigratorAction { + + public static NullStringMigratorAction of(String path) { + return new NullStringMigratorAction(path); + } + + private final String path; + + protected NullStringMigratorAction(String path) { + this.path = path; + } + + /** + * {@inheritDoc} + */ + @Override + public void migrate(ConfigurationSection config) { + config.set(path, "null".equals(config.getString(path)) ? "" : config.getString(path)); + Logging.info("Converted %s to %s", path, config.getString(path)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/VersionMigrator.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/VersionMigrator.java new file mode 100644 index 000000000..dcb2c5d32 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/VersionMigrator.java @@ -0,0 +1,85 @@ +package org.mvplugins.multiverse.core.configuration.migration; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.configuration.ConfigurationSection; + +/** + * A version migrator is a collection of migrator actions that are performed when migrating a config to a specific version. + */ +public class VersionMigrator { + + /** + * Creates a new builder for a VersionMigrator. + * + * @param version The version number of the config that this migrator migrates to. + * @return The builder instance. + */ + public static Builder builder(double version) { + return new Builder(version); + } + + private final double version; + private final List actions; + + protected VersionMigrator(double version, List actions) { + this.version = version; + this.actions = actions; + } + + /** + * Performs all the migrator actions. + * + * @param config The target settings instance to migrate. + */ + public void migrate(ConfigurationSection config) { + actions.forEach(action -> action.migrate(config)); + } + + /** + * Gets the version number of the config that this migrator migrates to. + * + * @return The version number. + */ + public double getVersion() { + return version; + } + + /** + * A builder for a VersionMigrator. + */ + public static class Builder { + private final double version; + private final List actions = new ArrayList<>(); + + /** + * Creates a new builder for a VersionMigrator. + * + * @param version The version number of the config that this migrator migrates to. + */ + public Builder(double version) { + this.version = version; + } + + /** + * Adds a migrator action to the list of actions. + * + * @param action The action to add. + * @return The builder instance. + */ + public Builder addAction(MigratorAction action) { + actions.add(action); + return this; + } + + /** + * Builds the VersionMigrator. + * + * @return The built VersionMigrator. + */ + public VersionMigrator build() { + return new VersionMigrator(version, actions); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/migration/package-info.java b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/package-info.java new file mode 100644 index 000000000..73deddec5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/migration/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.configuration.migration; diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/CommentedNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/CommentedNode.java new file mode 100644 index 000000000..21b3d6f0d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/CommentedNode.java @@ -0,0 +1,13 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import org.jetbrains.annotations.NotNull; + +public interface CommentedNode extends Node { + + /** + * Gets the comment of the node. + * + * @return The comment of the node. + */ + @NotNull String[] getComments(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigHeaderNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigHeaderNode.java new file mode 100644 index 000000000..51cf48657 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigHeaderNode.java @@ -0,0 +1,87 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.Strings; +import org.jetbrains.annotations.NotNull; + +/** + * A node that represents a header without any value. + */ +public class ConfigHeaderNode implements CommentedNode { + + /** + * Creates a new builder for a {@link ConfigHeaderNode}. + * + * @param path The path of the node. + * @return The new builder. + */ + public static @NotNull Builder> builder(String path) { + return new Builder<>(path); + } + + private final @NotNull String path; + private final @NotNull String[] comments; + + protected ConfigHeaderNode(@NotNull String path, @NotNull String[] comments) { + this.path = path; + this.comments = comments; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getPath() { + return path; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String[] getComments() { + return comments; + } + + public static class Builder> { + + protected final @NotNull String path; + protected final @NotNull List comments; + + public Builder(@NotNull String path) { + this.path = path; + this.comments = new ArrayList<>(); + } + + /** + * Adds a comment line to the node. + * + * @param comment The comment to add. + * @return This builder. + */ + public @NotNull B comment(@NotNull String comment) { + if (!Strings.isNullOrEmpty(comment) && !comment.startsWith("#")) { + comment = "# " + comment; + } + comments.add(comment); + return self(); + } + + /** + * Builds the node. + * + * @return The built node. + */ + public @NotNull ConfigHeaderNode build() { + return new ConfigHeaderNode(path, comments.toArray(new String[0])); + } + + + protected @NotNull B self() { + //noinspection unchecked + return (B) this; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNode.java new file mode 100644 index 000000000..21f631df9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNode.java @@ -0,0 +1,300 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.Collection; +import java.util.Collections; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.functions.DefaultSerializerProvider; +import org.mvplugins.multiverse.core.configuration.functions.DefaultStringParserProvider; +import org.mvplugins.multiverse.core.configuration.functions.DefaultSuggesterProvider; +import org.mvplugins.multiverse.core.configuration.functions.NodeSerializer; +import org.mvplugins.multiverse.core.configuration.functions.NodeStringParser; +import org.mvplugins.multiverse.core.configuration.functions.NodeSuggester; + +/** + * A node that contains a value. + * + * @param The type of the value. + */ +public class ConfigNode extends ConfigHeaderNode implements ValueNode { + + /** + * Creates a new builder for a {@link ConfigNode}. + * + * @param path The path of the node. + * @param type The type of the value. + * @param The type of the value. + * @return The new builder. + */ + public static @NotNull ConfigNode.Builder> builder( + @NotNull String path, + @NotNull Class type) { + return new ConfigNode.Builder<>(path, type); + } + + protected final @Nullable String name; + protected final @NotNull Class type; + protected final @Nullable Supplier defaultValue; + protected @Nullable NodeSuggester suggester; + protected @Nullable NodeStringParser stringParser; + protected @Nullable NodeSerializer serializer; + protected @Nullable Function> validator; + protected @Nullable BiConsumer onSetValue; + + protected ConfigNode( + @NotNull String path, + @NotNull String[] comments, + @Nullable String name, + @NotNull Class type, + @Nullable Supplier defaultValue, + @Nullable NodeSuggester suggester, + @Nullable NodeStringParser stringParser, + @Nullable NodeSerializer serializer, + @Nullable Function> validator, + @Nullable BiConsumer onSetValue) { + super(path, comments); + this.name = name; + this.type = type; + this.defaultValue = defaultValue; + this.suggester = (suggester != null) + ? suggester + : DefaultSuggesterProvider.getDefaultSuggester(type); + this.stringParser = (stringParser != null) + ? stringParser + : DefaultStringParserProvider.getDefaultStringParser(type); + this.serializer = (serializer != null) + ? serializer + : DefaultSerializerProvider.getDefaultSerializer(type); + this.validator = validator; + this.onSetValue = onSetValue; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getName() { + return Option.of(name); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Class getType() { + return type; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable T getDefaultValue() { + if (defaultValue != null) { + return defaultValue.get(); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggest(@Nullable String input) { + if (suggester != null) { + return suggester.suggest(input); + } + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Try parseFromString(@Nullable String input) { + if (stringParser != null) { + return stringParser.parse(input, type); + } + return Try.failure(new UnsupportedOperationException("No string parser for type " + type.getName())); + } + + /** + * {@inheritDoc} + */ + public @Nullable NodeSerializer getSerializer() { + return serializer; + } + + /** + * {@inheritDoc} + */ + @Override + public Try validate(@Nullable T value) { + if (validator != null) { + return validator.apply(value); + } + return Try.success(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void onSetValue(@Nullable T oldValue, @Nullable T newValue) { + if (onSetValue != null) { + onSetValue.accept(oldValue, newValue); + } + } + + /** + * Builder for {@link ConfigNode}. + * + * @param The type of the value. + * @param The type of the builder. + */ + public static class Builder> extends ConfigHeaderNode.Builder { + + protected @Nullable String name; + protected @NotNull final Class type; + protected @Nullable Supplier defaultValue; + protected @Nullable NodeSuggester suggester; + protected @Nullable NodeStringParser stringParser; + protected @Nullable NodeSerializer serializer; + protected @Nullable Function> validator; + protected @Nullable BiConsumer onSetValue; + + /** + * Creates a new builder. + * + * @param path The path of the node. + * @param type The type of the value. + */ + protected Builder(@NotNull String path, @NotNull Class type) { + super(path); + this.name = path; + this.type = type; + } + + /** + * Gets the path of this node. + * + * @return The path of this node. + */ + public @NotNull String path() { + return path; + } + + /** + * Sets the default value for this node. + * + * @param defaultValue The default value. + * @return This builder. + */ + public @NotNull B defaultValue(@NotNull T defaultValue) { + this.defaultValue = () -> defaultValue; + return self(); + } + + /** + * Sets the default value for this node. + * + * @param defaultValue The default value supplier. + * @return This builder. + */ + public @NotNull B defaultValue(@NotNull Supplier defaultValue) { + this.defaultValue = defaultValue; + return self(); + } + + /** + * Sets the name of this node. Used for identifying the node from user input. + * + * @param name The name of this node. + * @return This builder. + */ + public @NotNull B name(@Nullable String name) { + this.name = name; + return self(); + } + + /** + * Gets the name of this node. Used for identifying the node from user input. + * + * @return The name of this node, or {@code null} if the node has no name. + */ + public @Nullable String name() { + return name; + } + + /** + * Sets the suggester for this node. + * + * @param suggester The suggester for this node. + * @return This builder. + */ + public @NotNull B suggester(@NotNull NodeSuggester suggester) { + this.suggester = suggester; + return self(); + } + + /** + * Sets the string parser for this node. + * + * @param stringParser The string parser for this node. + * @return This builder. + */ + public @NotNull B stringParser(@NotNull NodeStringParser stringParser) { + this.stringParser = stringParser; + return self(); + } + + /** + * Sets the serializer for this node. + * + * @param serializer The serializer for this node. + * @return This builder. + */ + public @NotNull B serializer(@NotNull NodeSerializer serializer) { + this.serializer = serializer; + return self(); + } + + /** + * Sets the validator for this node. + * + * @param validator The validator for this node. + * @return This builder. + */ + public @NotNull B validator(@NotNull Function> validator) { + this.validator = validator; + return self(); + } + + /** + * Sets the action to be performed when the value is set. + * + * @param onSetValue The action to be performed. + * @return This builder. + */ + public @NotNull B onSetValue(@NotNull BiConsumer onSetValue) { + this.onSetValue = this.onSetValue == null ? onSetValue : this.onSetValue.andThen(onSetValue); + return self(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull ConfigNode build() { + return new ConfigNode<>(path, comments.toArray(new String[0]), + name, type, defaultValue, suggester, stringParser, serializer, validator, onSetValue); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNodeNotFoundException.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNodeNotFoundException.java new file mode 100644 index 000000000..9892fde9a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ConfigNodeNotFoundException.java @@ -0,0 +1,15 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.exceptions.MultiverseException; + +import static org.mvplugins.multiverse.core.locale.MVCorei18n.CONFIG_NODE_NOTFOUND; +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +public class ConfigNodeNotFoundException extends MultiverseException { + + public ConfigNodeNotFoundException(@Nullable String nodeName) { + super(CONFIG_NODE_NOTFOUND.bundle("Config node not found: {node}", replace("{node}").with(nodeName)), null); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListConfigNode.java new file mode 100644 index 000000000..b139f8bbd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListConfigNode.java @@ -0,0 +1,335 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import io.vavr.Value; +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.functions.*; + +/** + * A config node that contains a list of values. + * + * @param The type of the value. + */ +public class ListConfigNode extends ConfigNode> implements ListValueNode { + + /** + * Creates a new builder for a {@link ConfigNode}. + * + * @param path The path of the node. + * @param type The type of the value. + * @param The type of the value. + * @return The new builder. + */ + public static @NotNull > Builder listBuilder( + @NotNull String path, + @NotNull Class type) { + return new Builder<>(path, type); + } + + protected final Class itemType; + protected final NodeSuggester itemSuggester; + protected final NodeStringParser itemStringParser; + protected final NodeSerializer itemSerializer; + protected final Function> itemValidator; + protected final BiConsumer onSetItemValue; + + protected ListConfigNode( + @NotNull String path, + @NotNull String[] comments, + @Nullable String name, + @NotNull Class> type, + @Nullable Supplier> defaultValueSupplier, + @Nullable NodeSuggester suggester, + @Nullable NodeStringParser> stringParser, + @Nullable NodeSerializer> serializer, + @Nullable Function, Try> validator, + @Nullable BiConsumer, List> onSetValue, + @NotNull Class itemType, + @Nullable NodeSuggester itemSuggester, + @Nullable NodeStringParser itemStringParser, + @Nullable NodeSerializer itemSerializer, + @Nullable Function> itemValidator, + @Nullable BiConsumer onSetItemValue) { + super(path, comments, name, type, defaultValueSupplier, suggester, stringParser, serializer, + validator, onSetValue); + this.itemType = itemType; + this.itemSuggester = itemSuggester != null + ? itemSuggester + : DefaultSuggesterProvider.getDefaultSuggester(itemType); + this.itemStringParser = itemStringParser != null + ? itemStringParser + : DefaultStringParserProvider.getDefaultStringParser(itemType); + this.itemSerializer = itemSerializer != null + ? itemSerializer + : DefaultSerializerProvider.getDefaultSerializer(itemType); + this.itemValidator = itemValidator; + this.onSetItemValue = onSetItemValue; + + setDefaults(); + } + + private void setDefaults() { + if (this.itemSuggester != null && this.suggester == null) { + setDefaultSuggester(); + } + if (this.itemStringParser != null && this.stringParser == null) { + setDefaultStringParser(); + } + if (this.itemValidator != null && this.validator == null) { + setDefaultValidator(); + } + if (this.itemSerializer != null && this.serializer == null) { + setDefaultSerialiser(); + } + if (this.onSetItemValue != null && this.onSetValue == null) { + setDefaultOnSetValue(); + } + } + + private void setDefaultSuggester() { + this.suggester = input -> { + int lastIndexOf = input == null ? -1 : input.lastIndexOf(','); + if (lastIndexOf == -1) { + return itemSuggester.suggest(input); + } + + String lastInput = input.substring(lastIndexOf + 1); + String inputBeforeLast = input.substring(0, lastIndexOf + 1); + Set inputs = Set.of(inputBeforeLast.split(",")); + return itemSuggester.suggest(lastInput).stream() + .filter(item -> !inputs.contains(item)) + .map(item -> inputBeforeLast + item) + .toList(); + }; + } + + private void setDefaultStringParser() { + this.stringParser = (input, type) -> { + if (input == null) { + return Try.failure(new IllegalArgumentException("Input cannot be null")); + } + return Try.sequence(Arrays.stream(input.split(",")) + .map(inputItem -> itemStringParser.parse(inputItem, itemType)) + .toList()).map(Value::toJavaList); + }; + } + + private void setDefaultValidator() { + this.validator = value -> { + if (value != null) { + return Try.sequence(value.stream().map(itemValidator).toList()).map(v -> null); + } + return Try.success(null); + }; + } + + private void setDefaultSerialiser() { + this.serializer = new NodeSerializer<>() { + @Override + public List deserialize(Object object, Class> type) { + if (object instanceof List list) { + //noinspection unchecked + return list.stream() + .map(item -> itemSerializer != null ? itemSerializer.deserialize(item, itemType) : item) + .toList(); + } + //todo: Maybe assume object is the first element of the list + return new ArrayList<>(); + } + + @Override + public Object serialize(List object, Class> type) { + if (object == null) { + return new ArrayList<>(); + } + return object.stream() + .map(item -> itemSerializer != null ? itemSerializer.serialize(item, itemType) : item) + .toList(); + } + }; + } + + private void setDefaultOnSetValue() { + this.onSetValue = (oldValue, newValue) -> { + if (oldValue != null) { + oldValue.stream() + .filter(value -> !newValue.contains(value)) + .forEach(item -> onSetItemValue.accept(item, null)); + } + newValue.forEach(item -> onSetItemValue.accept(null, item)); + }; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Class getItemType() { + return itemType; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestItem(@Nullable String input) { + if (itemSuggester != null) { + return itemSuggester.suggest(input); + } + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Try parseItemFromString(@Nullable String input) { + if (itemStringParser != null) { + return itemStringParser.parse(input, itemType); + } + return Try.failure(new UnsupportedOperationException("No item string parser for type " + itemType)); + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable NodeSerializer getItemSerializer() { + return itemSerializer; + } + + /** + * {@inheritDoc} + */ + @Override + public Try validateItem(@Nullable I value) { + if (itemValidator != null) { + return itemValidator.apply(value); + } + return Try.success(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void onSetItemValue(@Nullable I oldValue, @Nullable I newValue) { + if (onSetItemValue != null) { + onSetItemValue.accept(oldValue, newValue); + } + } + + public static class Builder> extends ConfigNode.Builder, B> { + + protected final @NotNull Class itemType; + protected @Nullable NodeSuggester itemSuggester; + protected @Nullable NodeStringParser itemStringParser; + protected @Nullable NodeSerializer itemSerializer; + protected @Nullable Function> itemValidator; + protected @Nullable BiConsumer onSetItemValue; + + /** + * Creates a new builder. + * + * @param path The path of the node. + * @param itemType The type of the item value in list. + */ + protected Builder(@NotNull String path, @NotNull Class itemType) { + //noinspection unchecked + super(path, (Class>) (Object) List.class); + this.itemType = itemType; + this.defaultValue = () -> (List) new ArrayList<>(); + } + + /** + * Sets the suggester for an individual item in the list. + * + * @param itemSuggester The suggester. + * @return This builder. + */ + public @NotNull B itemSuggester(@NotNull NodeSuggester itemSuggester) { + this.itemSuggester = itemSuggester; + return self(); + } + + /** + * Sets the string parser for an individual item in the list. + * + * @param itemStringParser The string parser. + * @return This builder. + */ + public @NotNull B itemStringParser(@NotNull NodeStringParser itemStringParser) { + this.itemStringParser = itemStringParser; + return self(); + } + + /** + * Sets the serializer for an individual item in the list. + * + * @param serializer The serializer. + * @return This builder. + */ + public @NotNull B itemSerializer(@NotNull NodeSerializer serializer) { + this.itemSerializer = serializer; + return self(); + } + + /** + * Sets the validator for an individual item in the list. + * + * @param itemValidator The validator. + * @return This builder. + */ + public @NotNull B itemValidator(@NotNull Function> itemValidator) { + this.itemValidator = itemValidator; + return self(); + } + + /** + * Sets the onSetValue for an individual item in the list. + * + * @param onSetItemValue The onSetValue. + * @return This builder. + */ + public @NotNull B onSetItemValue(@Nullable BiConsumer onSetItemValue) { + this.onSetItemValue = onSetItemValue; + return self(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull ListConfigNode build() { + return new ListConfigNode<>( + path, + comments.toArray(new String[0]), + name, + type, + defaultValue, + suggester, + stringParser, + serializer, + validator, + onSetValue, + itemType, + itemSuggester, + itemStringParser, + itemSerializer, + itemValidator, + onSetItemValue); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListValueNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListValueNode.java new file mode 100644 index 000000000..a16b9bce1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ListValueNode.java @@ -0,0 +1,64 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.Collection; +import java.util.List; + +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.functions.NodeSerializer; + +/** + * A node that holds a list of values of a specific type. + * + * @param The type of list item. + */ +public interface ListValueNode extends ValueNode> { + + /** + * Gets the class type of list item. + * + * @return The class type of list item. + */ + @NotNull Class getItemType(); + + /** + * Suggests possible string values for this node. + * + * @param input The input string. + * @return A collection of possible string values. + */ + @NotNull Collection suggestItem(@Nullable String input); + + /** + * Parses the given string into a value of type {@link I}. Used for property set by user input. + * + * @param input The string to parse. + * @return The parsed value, or given exception if parsing failed. + */ + @NotNull Try parseItemFromString(@Nullable String input); + + /** + * Gets the serializer for this node. + * + * @return The serializer for this node. + */ + @Nullable NodeSerializer getItemSerializer(); + + /** + * Validates the value of this node. + * + * @param value The value to validate. + * @return True if the value is valid, false otherwise. + */ + Try validateItem(@Nullable I value); + + /** + * Called when the value of this node is set. + * + * @param oldValue The old value. + * @param newValue The new value. + */ + void onSetItemValue(@Nullable I oldValue, @Nullable I newValue); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/Node.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/Node.java new file mode 100644 index 000000000..bd1c23441 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/Node.java @@ -0,0 +1,13 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import org.jetbrains.annotations.NotNull; + +public interface Node { + + /** + * Gets the YAML path of the node. + * + * @return The YAML path of the node. + */ + @NotNull String getPath(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/NodeGroup.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/NodeGroup.java new file mode 100644 index 000000000..a95e5789c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/NodeGroup.java @@ -0,0 +1,156 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode; +import io.vavr.control.Option; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A collection of {@link CommentedNode}s, with mappings to nodes by name. + */ +public class NodeGroup implements Collection { + private final Collection nodes; + private final Map nodesMap; + + /** + * Creates a new empty node group. + */ + public NodeGroup() { + this.nodes = new ArrayList<>(); + this.nodesMap = new HashMap<>(); + } + + /** + * Creates a new node group with the given nodes. + * + * @param nodes The nodes to make up the group. + */ + public NodeGroup(@NotNull Collection nodes) { + this.nodes = nodes; + this.nodesMap = new HashMap<>(nodes.size()); + nodes.forEach(this::addNodeIndex); + } + + private void addNodeIndex(@NotNull Node node) { + if (node instanceof ValueNode) { + ((ValueNode) node).getName().peek(name -> nodesMap.put(name, node)); + } + } + + private void removeNodeIndex(@NotNull Node node) { + if (node instanceof ValueNode) { + ((ValueNode) node).getName().peek(nodesMap::remove); + } + } + + /** + * Gets the names of all nodes in this group. + * + * @return The names of all nodes in this group. + */ + public @NotNull Collection getNames() { + return nodesMap.keySet(); + } + + /** + * Gets the node with the given name. + * + * @param name The name of the node to get. + * @return The node with the given name, or {@link Option.None} if no node with the given name exists. + */ + public @NotNull Option findNode(@Nullable String name) { + return Option.of(nodesMap.get(name)); + } + + /** + * Gets the node with the given name. + * + * @param name The name of node to get. + * @param type The type of node to get. + * @param The type of node. + * @return The node with the given name, or {@link Option.None} if no node with the given name exists. + */ + public @NotNull Option findNode(@Nullable String name, @NotNull Class type) { + return Option.of(nodesMap.get(name)).map(node -> type.isAssignableFrom(node.getClass()) ? (T) node : null); + } + + @Override + public int size() { + return nodes.size(); + } + + @Override + public boolean isEmpty() { + return nodes.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return nodes.contains(o); + } + + @NotNull + @Override + public Iterator iterator() { + return nodes.iterator(); + } + + @Override + public Object @NotNull [] toArray() { + return nodes.toArray(); + } + + @Override + public T @NotNull [] toArray(T @NotNull [] ts) { + return nodes.toArray(ts); + } + + @Override + public boolean add(Node node) { + if (nodes.add(node)) { + addNodeIndex(node); + return true; + } + return false; + } + + @Override + public boolean remove(Object o) { + if (nodes.remove(o) && o instanceof CommentedNode) { + removeNodeIndex((Node) o); + return true; + } + return false; + } + + @Override + public boolean containsAll(@NotNull Collection collection) { + return nodes.containsAll(collection); + } + + @Override + public boolean addAll(@NotNull Collection collection) { + return nodes.addAll(collection); + } + + @Override + public boolean removeAll(@NotNull Collection collection) { + return nodes.removeAll(collection); + } + + @Override + public boolean retainAll(@NotNull Collection collection) { + return nodes.retainAll(collection); + } + + @Override + public void clear() { + nodes.clear(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/ValueNode.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ValueNode.java new file mode 100644 index 000000000..6a7115da4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/ValueNode.java @@ -0,0 +1,74 @@ +package org.mvplugins.multiverse.core.configuration.node; + +import java.util.Collection; + +import io.vavr.control.Option; +import io.vavr.control.Try; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.functions.NodeSerializer; + +public interface ValueNode extends Node { + + /** + * Gets the name of this node. Used for identifying the node from user input. + * + * @return An {@link Option} containing the name of this node, or {@link Option.None} if the node has no name. + */ + @NotNull Option getName(); + + /** + * Gets the class type {@link T} of the node value. + * + * @return The class type of the node value. + */ + @NotNull Class getType(); + + /** + * Gets the default value with type {@link T} of the node. + * + * @return The default value of the node. + */ + @Nullable T getDefaultValue(); + + /** + * Suggests possible string values for this node. Generated based on the current user input. + * + * @param input The current partial user input + * @return A collection of possible string values. + */ + @NotNull Collection suggest(@Nullable String input); + + /** + * Parses the given string into a value of type {@link T}. Used for property set by user input. + * + * @param input The string to parse. + * @return The parsed value, or given exception if parsing failed. + */ + @NotNull Try parseFromString(@Nullable String input); + + /** + * Gets the serializer for this node. + * + * @return The serializer for this node. + */ + @Nullable NodeSerializer getSerializer(); + + /** + * Validates the value of this node. + * + * @param value The value to validate. + * @return An empty {@link Try} if the value is valid, or a {@link Try} containing an exception if the value is + * invalid. + */ + Try validate(@Nullable T value); + + /** + * Called when the value of this node is set. + * + * @param oldValue The old value. + * @param newValue The new value. + */ + void onSetValue(@Nullable T oldValue, @Nullable T newValue); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/configuration/node/package-info.java b/src/main/java/org/mvplugins/multiverse/core/configuration/node/package-info.java new file mode 100644 index 000000000..3da947474 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/configuration/node/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.configuration.node; diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/Destination.java b/src/main/java/org/mvplugins/multiverse/core/destination/Destination.java new file mode 100644 index 000000000..af2feebae --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/Destination.java @@ -0,0 +1,48 @@ +package org.mvplugins.multiverse.core.destination; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Contract; + +/** + * A destination is a location that can be teleported to. + * + * @param The type of the destination + * @param The type of the destination instance + */ +@Contract +public interface Destination, T extends DestinationInstance> { + /** + * Returns the identifier or prefix that is required for this destination. + * + *

Portals have a prefix of "p" for example and OpenWarp (third party plugin) uses "ow". This is derived from a + * hash and cannot have duplicate values. Read that as your plugin cannot use 'p' because it's already used. + * Please check the wiki when adding a custom destination!

+ * + * @return The identifier or prefix that is required for this destination. + */ + @NotNull String getIdentifier(); + + /** + * Returns the destination instance for the given destination parameters. + * + * @param destinationParams The destination parameters. ex: p:MyPortal:nw + * @return The destination instance, or null if the destination parameters are invalid. + */ + @Nullable T getDestinationInstance(@Nullable String destinationParams); + + /** + * Returns a list of possible destinations for the given destination parameters. + * + * @param commandSender The command sender + * @param destinationParams The destination parameters. ex: p:MyPortal:nw + * @return A list of possible destinations + */ + @NotNull + Collection suggestDestinations( + @NotNull CommandSender commandSender, @Nullable String destinationParams); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java new file mode 100644 index 000000000..5f1f6ffa0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationInstance.java @@ -0,0 +1,96 @@ +package org.mvplugins.multiverse.core.destination; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +/** + * Instance of a specific {@link Destination}. + * + * @param The type of the instance. + * @param The type of the destination. + */ +public abstract class DestinationInstance, T extends Destination> { + + protected final T destination; + + protected DestinationInstance(@NotNull T destination) { + this.destination = destination; + } + + /** + * Gets the destination that created this instance. + * + * @return The destination. + */ + public @NotNull T getDestination() { + return this.destination; + } + + /** + * Gets the {@link Destination#getIdentifier()} for this instance. + * + * @return The identifier. + */ + public @NotNull String getIdentifier() { + return this.destination.getIdentifier(); + } + + /** + * Gets the exact location to teleport an entity to. + * + * @param teleportee The entity to teleport. + * @return The location to teleport to. + */ + public abstract @NotNull Option getLocation(@NotNull Entity teleportee); + + /** + * Gets the velocity to apply to an entity after teleporting. + * + * @param teleportee The entity to teleport. + * @return A vector representing the speed/direction the player should travel when arriving at the destination. + */ + public abstract @NotNull Option getVelocity(@NotNull Entity teleportee); + + /** + * Should the Multiverse SafeTeleporter be used? + * + *

If not, MV will blindly take people to the location specified.

+ * + * @return True if the SafeTeleporter will be used, false if not. + */ + public abstract boolean checkTeleportSafety(); + + /** + * Gets the permission suffix to check for when teleporting to this destination. + * This is used for finer per world/player permissions, such as "multiverse.teleport.self.worldname". + * + *

For example, if the destination is "w:world", the permission suffix is "world".

+ * + * @return The permission suffix. + */ + public abstract @NotNull Option getFinerPermissionSuffix(); + + /** + * Serialises the destination instance to a savable string. + * + *

This is used when plugins save destinations to configuration, + * and when the destination is displayed to the user.

+ * + * @return The serialised destination instance. + */ + @NotNull + protected abstract String serialise(); + + /** + * String representation of the destination instance that can be deserialised back into the destination instance. + * + * @return The string representation of the destination instance. + */ + @Override + public String toString() { + return this.destination.getIdentifier() + ":" + this.serialise(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/DestinationSuggestionPacket.java b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationSuggestionPacket.java new file mode 100644 index 000000000..20c4a08a7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationSuggestionPacket.java @@ -0,0 +1,10 @@ +package org.mvplugins.multiverse.core.destination; + +/** + * Data of a possible destination for tab completion and permission checking + * + * @param destinationString The destination string + * @param finerPermissionSuffix The finer permission suffix + */ +public record DestinationSuggestionPacket(String destinationString, String finerPermissionSuffix) { +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/DestinationsProvider.java b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationsProvider.java new file mode 100644 index 000000000..73007dcc2 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/DestinationsProvider.java @@ -0,0 +1,87 @@ +package org.mvplugins.multiverse.core.destination; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.permissions.CorePermissions; + +/** + * Provides destinations for teleportation. + */ +@Service +public final class DestinationsProvider { + private static final String SEPARATOR = ":"; + + private final Map> destinationMap; + private final CorePermissions corePermissions; + + @Inject + DestinationsProvider(@NotNull CorePermissions corePermissions) { + this.corePermissions = corePermissions; + this.destinationMap = new HashMap<>(); + } + + /** + * Adds a destination to the provider. + * + * @param destination The destination. + */ + public void registerDestination(@NotNull Destination destination) { + this.destinationMap.put(destination.getIdentifier(), destination); + this.corePermissions.addDestinationPermissions(destination); + } + + /** + * Converts a destination string to a destination object. + * + * @param destinationString The destination string. + * @return The destination object, or null if invalid format. + */ + public @NotNull Option> parseDestination(@NotNull String destinationString) { + String[] items = destinationString.split(SEPARATOR, 2); + + String idString = items[0]; + String destinationParams; + Destination destination; + + if (items.length < 2) { + // Assume world destination + destination = this.getDestinationById("w"); + destinationParams = items[0]; + } else { + destination = this.getDestinationById(idString); + destinationParams = items[1]; + } + + if (destination == null) { + return Option.none(); + } + + return Option.of(destination.getDestinationInstance(destinationParams)); + } + + /** + * Gets a destination by its identifier. + * + * @param identifier The identifier. + * @return The destination, or null if not found. + */ + public @Nullable Destination getDestinationById(@Nullable String identifier) { + return this.destinationMap.get(identifier); + } + + /** + * Gets all registered destinations. + * + * @return A collection of destinations. + */ + public @NotNull Collection> getDestinations() { + return this.destinationMap.values(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestination.java new file mode 100644 index 000000000..40b19c3e6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestination.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.destination.core; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.anchor.AnchorManager; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; + +/** + * {@link Destination} implementation for anchors. + */ +@Service +public class AnchorDestination implements Destination { + + private final AnchorManager anchorManager; + + @Inject + AnchorDestination(AnchorManager anchorManager) { + this.anchorManager = anchorManager; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "a"; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable AnchorDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + if (destinationParams == null) { + return null; + } + Location anchorLocation = this.anchorManager.getAnchorLocation(destinationParams); + if (anchorLocation == null) { + return null; + } + return new AnchorDestinationInstance(this, destinationParams, anchorLocation); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + return this.anchorManager.getAnchors(sender instanceof Player ? (Player)sender : null) + .stream() + .map(anchorName -> new DestinationSuggestionPacket(anchorName, anchorName)) + .toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java new file mode 100644 index 000000000..40a33c91d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/AnchorDestinationInstance.java @@ -0,0 +1,73 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Destination instance implementation for the {@link AnchorDestination}. + */ +public class AnchorDestinationInstance extends DestinationInstance { + private final String anchorName; + private final Location anchorLocation; + + /** + * Constructor. + * + * @param anchorName The name of the anchor. + * @param anchorLocation The location of the anchor. + */ + AnchorDestinationInstance( + @NotNull AnchorDestination destination, + @NotNull String anchorName, + @NotNull Location anchorLocation + ) { + super(destination); + this.anchorName = anchorName; + this.anchorLocation = anchorLocation; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + return Option.of(anchorLocation); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(anchorName); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + return anchorName; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java new file mode 100644 index 000000000..b05250157 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java @@ -0,0 +1,63 @@ +package org.mvplugins.multiverse.core.destination.core; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import co.aikar.commands.BukkitCommandIssuer; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; +import org.mvplugins.multiverse.core.utils.PlayerFinder; + +/** + * {@link Destination} implementation for beds. + */ +@Service +public class BedDestination implements Destination { + static final String OWN_BED_STRING = "playerbed"; + + BedDestination() { + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "b"; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable BedDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + Player player = PlayerFinder.get(destinationParams); + if (player == null && !destinationParams.equals(OWN_BED_STRING)) { + return null; + } + return new BedDestinationInstance(this, player); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + List collect = Bukkit.getOnlinePlayers().stream() + .map(player -> new DestinationSuggestionPacket(player.getName(), player.getName())) + .collect(Collectors.toList()); + if (sender instanceof Player) { + collect.add(new DestinationSuggestionPacket(OWN_BED_STRING, OWN_BED_STRING)); + } + return collect; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java new file mode 100644 index 000000000..3c3bb104d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestinationInstance.java @@ -0,0 +1,74 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Destination instance implementation for the {@link BedDestination}. + */ +public class BedDestinationInstance extends DestinationInstance { + private final Player player; + + /** + * Constructor. + * + * @param player The player whose bed to use. + */ + BedDestinationInstance(@NotNull BedDestination destination, @Nullable Player player) { + super(destination); + this.player = player; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + if (player != null) { + return Option.of(player.getBedSpawnLocation()); + } + if (teleportee instanceof Player) { + return Option.of(((Player) teleportee).getBedSpawnLocation()); + } + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(player != null ? player.getName() : BedDestination.OWN_BED_STRING); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + return player != null ? player.getName() : BedDestination.OWN_BED_STRING; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestination.java new file mode 100644 index 000000000..e710fef4a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestination.java @@ -0,0 +1,94 @@ +package org.mvplugins.multiverse.core.destination.core; + +import co.aikar.commands.BukkitCommandIssuer; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +import java.util.Collection; +import java.util.List; + +/** + * {@link Destination} implementation for cannons. + */ +@Service +public class CannonDestination implements Destination { + + private final WorldManager worldManager; + + @Inject + CannonDestination(WorldManager worldManager) { + this.worldManager = worldManager; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "ca"; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable CannonDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + String[] params = destinationParams.split(":"); + if (params.length != 5) { + return null; + } + + String worldName = params[0]; + String coordinates = params[1]; + String pitch = params[2]; + String yaw = params[3]; + String speed = params[4]; + + String[] coordinatesParams = coordinates.split(","); + if (coordinatesParams.length != 3) { + return null; + } + + World world = this.worldManager.getLoadedWorld(worldName).map(LoadedMultiverseWorld::getBukkitWorld).getOrNull().getOrNull(); + if (world == null) { + return null; + } + + Location location; + double dSpeed; + try { + location = new Location( + world, + Double.parseDouble(coordinatesParams[0]), + Double.parseDouble(coordinatesParams[1]), + Double.parseDouble(coordinatesParams[2]), + Float.parseFloat(yaw), + Float.parseFloat(pitch) + ); + dSpeed = Double.parseDouble(speed); + } catch (NumberFormatException e) { + return null; + } + + return new CannonDestinationInstance(this, location, dSpeed); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + return List.of(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestinationInstance.java new file mode 100644 index 000000000..35e7e6499 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/CannonDestinationInstance.java @@ -0,0 +1,79 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Destination instance implementation for the {@link CannonDestination}. + */ +public class CannonDestinationInstance extends DestinationInstance { + private final Location location; + private final double speed; + + /** + * Constructor. + * + * @param location The location to teleport to. + * @param speed The speed to fire the player at. + */ + CannonDestinationInstance(@NotNull CannonDestination destination, @NotNull Location location, double speed) { + super(destination); + this.location = location; + this.speed = speed; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + return Option.of(location); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + double pitchRadians = Math.toRadians(location.getPitch()); + double yawRadians = Math.toRadians(location.getYaw()); + double x = Math.sin(yawRadians) * speed * -1; + double y = Math.sin(pitchRadians) * speed * -1; + double z = Math.cos(yawRadians) * speed; + // Account for the angle they were pointed, and take away velocity + x = Math.cos(pitchRadians) * x; + z = Math.cos(pitchRadians) * z; + return Option.of(new Vector(x, y, z)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(location.getWorld()).map(World::getName); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + return location.getWorld().getName() + ":" + location.getX() + "," + location.getY() + + "," + location.getZ() + ":" + location.getPitch() + ":" + location.getYaw() + ":" + this.speed; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java new file mode 100644 index 000000000..0b76b63ed --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestination.java @@ -0,0 +1,126 @@ +package org.mvplugins.multiverse.core.destination.core; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryChecker; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider; + +/** + * {@link Destination} implementation for exact locations. + */ +@Service +public class ExactDestination implements Destination { + + private final MVCoreConfig config; + private final WorldManager worldManager; + private final WorldEntryCheckerProvider worldEntryCheckerProvider; + + @Inject + public ExactDestination(MVCoreConfig config, WorldManager worldManager, WorldEntryCheckerProvider worldEntryCheckerProvider) { + this.config = config; + this.worldManager = worldManager; + this.worldEntryCheckerProvider = worldEntryCheckerProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "e"; + } + + /** + * Make a new {@link ExactDestinationInstance} from a {@link Location}. + * + * @param location The target location + * @return A new {@link ExactDestinationInstance} + */ + public @NotNull ExactDestinationInstance fromLocation(@NotNull Location location) { + return new ExactDestinationInstance(this, location); + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable ExactDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + String[] items = destinationParams.split(":"); + if (items.length < 2) { + return null; + } + + String worldName = items[0]; + String coordinates = items[1]; + String[] coordinatesParams = coordinates.split(","); + if (coordinatesParams.length != 3) { + return null; + } + + World world = getLoadedMultiverseWorld(worldName).flatMap(LoadedMultiverseWorld::getBukkitWorld).getOrNull(); + if (world == null) { + return null; + } + + Location location; + try { + location = new Location( + world, + Double.parseDouble(coordinatesParams[0]), + Double.parseDouble(coordinatesParams[1]), + Double.parseDouble(coordinatesParams[2]) + ); + } catch (NumberFormatException e) { + return null; + } + + if (items.length == 4) { + String pitch = items[2]; + String yaw = items[3]; + try { + location.setPitch(Float.parseFloat(pitch)); + location.setYaw(Float.parseFloat(yaw)); + } catch (NumberFormatException e) { + return null; + } + } + + return new ExactDestinationInstance(this, location); + } + + private Option getLoadedMultiverseWorld(String worldName) { + return config.getResolveAliasName() + ? worldManager.getLoadedWorldByNameOrAlias(worldName) + : worldManager.getLoadedWorld(worldName); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + return worldManager.getLoadedWorlds().stream() + .filter(world -> worldEntryCheckerProvider.forSender(sender) + .canAccessWorld(world) + .isSuccess()) + .map(world -> + new DestinationSuggestionPacket(world.getName() + ":", world.getName())) + .toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java new file mode 100644 index 000000000..9c14f1aa4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/ExactDestinationInstance.java @@ -0,0 +1,71 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Destination instance implementation for the {@link ExactDestination}. + */ +public class ExactDestinationInstance extends DestinationInstance { + private final Location location; + + /** + * Constructor. + * + * @param location The location to teleport to. + */ + ExactDestinationInstance(@NotNull ExactDestination destination, @NotNull Location location) { + super(destination); + this.location = location; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + if (location.getWorld() == null) { + return Option.none(); + } + return Option.of(location); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(location.getWorld()).map(World::getName); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + return location.getWorld().getName() + ":" + location.getX() + "," + location.getY() + + "," + location.getZ() + ":" + location.getPitch() + ":" + location.getYaw(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestination.java new file mode 100644 index 000000000..f7987e725 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestination.java @@ -0,0 +1,58 @@ +package org.mvplugins.multiverse.core.destination.core; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; +import org.mvplugins.multiverse.core.utils.PlayerFinder; + +/** + * {@link Destination} implementation for players.s + */ +@Service +public class PlayerDestination implements Destination { + /** + * Creates a new instance of the PlayerDestination. + */ + PlayerDestination() { + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "pl"; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable PlayerDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + Player player = PlayerFinder.get(destinationParams); + if (player == null) { + return null; + } + return new PlayerDestinationInstance(this, player); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + return Bukkit.getOnlinePlayers().stream() + .map(p -> new DestinationSuggestionPacket(p.getName(), p.getName())) + .toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java new file mode 100644 index 000000000..01819cd44 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestinationInstance.java @@ -0,0 +1,67 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Destination instance implementation for the {@link PlayerDestination}. + */ +public class PlayerDestinationInstance extends DestinationInstance { + private final Player player; + + /** + * Constructor. + * + * @param player The player whose location to go to. + */ + PlayerDestinationInstance(@NotNull PlayerDestination destination, @NotNull Player player) { + super(destination); + this.player = player; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + return Option.of(player.getLocation()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(player.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + return player.getName(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestination.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestination.java new file mode 100644 index 000000000..7b288cf9e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestination.java @@ -0,0 +1,93 @@ +package org.mvplugins.multiverse.core.destination.core; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import jakarta.inject.Inject; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider; + +/** + * {@link Destination} implementation for exact locations. + */ +@Service +public class WorldDestination implements Destination { + + private final MVCoreConfig config; + private final WorldManager worldManager; + private final LocationManipulation locationManipulation; + private final WorldEntryCheckerProvider worldEntryCheckerProvider; + + @Inject + WorldDestination( + @NotNull MVCoreConfig config, + @NotNull WorldManager worldManager, + @NotNull LocationManipulation locationManipulation, + @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider) { + this.config = config; + this.worldManager = worldManager; + this.locationManipulation = locationManipulation; + this.worldEntryCheckerProvider = worldEntryCheckerProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getIdentifier() { + return "w"; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable WorldDestinationInstance getDestinationInstance(@Nullable String destinationParams) { + String[] items = destinationParams.split(":"); + if (items.length > 3) { + return null; + } + + String worldName = items[0]; + MultiverseWorld world = getLoadedMultiverseWorld(worldName); + if (world == null) { + return null; + } + + String direction = (items.length == 2) ? items[1] : null; + float yaw = direction != null ? this.locationManipulation.getYaw(direction) : -1; + + return new WorldDestinationInstance(this, world, direction, yaw); + } + + @Nullable + private MultiverseWorld getLoadedMultiverseWorld(String worldName) { + return config.getResolveAliasName() + ? worldManager.getLoadedWorldByNameOrAlias(worldName).getOrNull() + : worldManager.getLoadedWorld(worldName).getOrNull(); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestDestinations( + @NotNull CommandSender sender, @Nullable String destinationParams) { + return worldManager.getLoadedWorlds().stream() + .filter(world -> worldEntryCheckerProvider.forSender(sender) + .canAccessWorld(world) + .isSuccess()) + .map(world -> new DestinationSuggestionPacket(world.getName(), world.getName())) + .toList(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java new file mode 100644 index 000000000..7e9dfe252 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/WorldDestinationInstance.java @@ -0,0 +1,88 @@ +package org.mvplugins.multiverse.core.destination.core; + +import io.vavr.control.Option; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +/** + * Destination instance implementation for the {@link WorldDestination}. + */ +public class WorldDestinationInstance extends DestinationInstance { + private final MultiverseWorld world; + private final String direction; + private final float yaw; + + /** + * Constructor. + * + * @param destination The destination. + * @param world The world to teleport to. + * @param direction The direction to face. + * @param yaw The yaw to face. + */ + WorldDestinationInstance( + @NotNull WorldDestination destination, + @NotNull MultiverseWorld world, + @Nullable String direction, + float yaw + ) { + super(destination); + this.world = world; + this.direction = direction; + this.yaw = yaw; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLocation(@NotNull Entity teleportee) { + Location worldLoc = world.getSpawnLocation(); + if (this.yaw >= 0) { + // Only modify the yaw if its set. + worldLoc.setYaw(this.yaw); + } + return Option.of(worldLoc); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getVelocity(@NotNull Entity teleportee) { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean checkTeleportSafety() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getFinerPermissionSuffix() { + return Option.of(world.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String serialise() { + if (this.direction != null) { + return this.world.getName() + ":" + this.direction; + } + return this.world.getName(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/destination/core/package-info.java b/src/main/java/org/mvplugins/multiverse/core/destination/core/package-info.java new file mode 100644 index 000000000..8e2ae65a2 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/destination/core/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains the in-built implementations of the Destination interface. + */ +package org.mvplugins.multiverse.core.destination.core; diff --git a/src/main/java/com/onarandombox/MultiverseCore/destination/package-info.java b/src/main/java/org/mvplugins/multiverse/core/destination/package-info.java similarity index 51% rename from src/main/java/com/onarandombox/MultiverseCore/destination/package-info.java rename to src/main/java/org/mvplugins/multiverse/core/destination/package-info.java index 9c052b523..a3edd22ce 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/destination/package-info.java +++ b/src/main/java/org/mvplugins/multiverse/core/destination/package-info.java @@ -1,4 +1,4 @@ /** * This package contains all destination-types. */ -package com.onarandombox.MultiverseCore.destination; +package org.mvplugins.multiverse.core.destination; diff --git a/src/main/java/org/mvplugins/multiverse/core/display/ContentDisplay.java b/src/main/java/org/mvplugins/multiverse/core/display/ContentDisplay.java new file mode 100644 index 000000000..eef8a1345 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/ContentDisplay.java @@ -0,0 +1,70 @@ +package org.mvplugins.multiverse.core.display; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import co.aikar.commands.BukkitCommandIssuer; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.display.handlers.DefaultSendHandler; +import org.mvplugins.multiverse.core.display.handlers.SendHandler; +import org.mvplugins.multiverse.core.display.parsers.ContentProvider; + +/** + * 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 contentParsers = new ArrayList<>(); + private SendHandler sendHandler = DefaultSendHandler.getInstance(); + + ContentDisplay() { + } + + /** + * Adds content to be displayed. + * + * @param parser The content parser to add. + * @return Same {@link ContentDisplay} for method chaining. + */ + @NotNull + public ContentDisplay addContent(@NotNull ContentProvider 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 issuer The target command sender to show the display to. + */ + public void send(@NotNull BukkitCommandIssuer issuer) { + Objects.requireNonNull(sendHandler, "No send handler set for content display"); + List parsedContent = new ArrayList<>(); + contentParsers.forEach(parser -> parsedContent.addAll(parser.parse(issuer))); + sendHandler.send(issuer, parsedContent); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/filters/ContentFilter.java b/src/main/java/org/mvplugins/multiverse/core/display/filters/ContentFilter.java new file mode 100644 index 000000000..9f34e3c84 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/filters/ContentFilter.java @@ -0,0 +1,28 @@ +package org.mvplugins.multiverse.core.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(); + + /** + * Gets the string representation of this filter. + * + * @return The string representation of this filter. + */ + String toString(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/filters/DefaultContentFilter.java b/src/main/java/org/mvplugins/multiverse/core/display/filters/DefaultContentFilter.java new file mode 100644 index 000000000..45eedd4fd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/filters/DefaultContentFilter.java @@ -0,0 +1,45 @@ +package org.mvplugins.multiverse.core.display.filters; + +/** + * Default implementation of {@link ContentFilter} that doesn't filter anything. + */ +public final class DefaultContentFilter implements ContentFilter { + + private static DefaultContentFilter instance; + + /** + * Gets the singleton instance of this class. + * + * @return The singleton instance of this class. + */ + public static DefaultContentFilter get() { + 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; + } + + @Override + public String toString() { + return "N/A"; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/filters/RegexContentFilter.java b/src/main/java/org/mvplugins/multiverse/core/display/filters/RegexContentFilter.java new file mode 100644 index 000000000..0bfdc0bcd --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/filters/RegexContentFilter.java @@ -0,0 +1,119 @@ +package org.mvplugins.multiverse.core.display.filters; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +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; + +/** + * 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(cleanedFilter); + } + + private final String regexString; + private Pattern regexPattern; + + 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)).toLowerCase(); + try { + return regexPattern.matcher(text).find(); + } catch (PatternSyntaxException ignored) { + Logging.warning("Error parsing regex '%s' for input '%s'", regexString, text); + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean needToFilter() { + return hasValidRegex(); + } + + /** + * Check if the regex is valid. + * + * @return True if the regex is valid. + */ + public boolean hasValidRegex() { + return regexPattern != null; + } + + /** + * Get the regex string. + * + * @return The regex string. + */ + public String getRegexString() { + return regexString; + } + + /** + * Get the compiled regex pattern. + * + * @return The compiled regex pattern. + */ + public Pattern getRegexPattern() { + return regexPattern; + } + + @Override + public String toString() { + return regexString; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/filters/package-info.java b/src/main/java/org/mvplugins/multiverse/core/display/filters/package-info.java new file mode 100644 index 000000000..7b81e16c5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/filters/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the filters that can be used to choose specific data to display. + */ +package org.mvplugins.multiverse.core.display.filters; diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/BaseSendHandler.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/BaseSendHandler.java new file mode 100644 index 000000000..8767594bf --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/BaseSendHandler.java @@ -0,0 +1,133 @@ +package org.mvplugins.multiverse.core.display.handlers; + +import java.util.List; +import java.util.stream.Collectors; + +import co.aikar.commands.BukkitCommandIssuer; +import com.google.common.base.Strings; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.display.filters.ContentFilter; +import org.mvplugins.multiverse.core.display.filters.DefaultContentFilter; + +/** + * Base implementation of {@link SendHandler} with some common parameters. + * + * @param The subclass that inherited this baseclass. + */ +public abstract class BaseSendHandler> implements SendHandler { + + /** + * Header to be displayed. + */ + protected String header = ""; + + /** + * Filter to keep only contents that matches the filter. + */ + protected ContentFilter filter = DefaultContentFilter.get(); + + /** + * Fallback message to be displayed when there is no content to display. + */ + protected String noContentMessage = String.format("%sThere is no content to display.", ChatColor.RED); + + /** + * {@inheritDoc} + */ + @Override + public void send(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + sendHeader(issuer); + List filteredContent = filterContent(content); + if (filteredContent.isEmpty() && !Strings.isNullOrEmpty(noContentMessage)) { + issuer.sendMessage(noContentMessage); + return; + } + sendContent(issuer, filteredContent); + } + + /** + * Sends the header if header is present. + * + * @param issuer The target which the header will be displayed to. + */ + protected void sendHeader(BukkitCommandIssuer issuer) { + if (!Strings.isNullOrEmpty(header)) { + issuer.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 filterContent(@NotNull List content) { + if (filter.needToFilter()) { + return content.stream().filter(filter::checkMatch).collect(Collectors.toList()); + } + return content; + } + + /** + * Display the contents. + * + * @param issuer The target which the content will be displayed to. + * @param content The content to display. + */ + protected abstract void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List 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 getT(); + } + + /** + * 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 getT(); + } + + /** + * Sets the message to be displayed when there is no content to display. + * + * @param message The message to display. Null to disable. + * @return Same {@link T} for method chaining. + */ + public T noContentMessage(@Nullable String message) { + this.noContentMessage = message; + return getT(); + } + + @SuppressWarnings("unchecked") + private @NotNull T getT() { + return (T) this; + } + + public String getHeader() { + return header; + } + + public ContentFilter getFilter() { + return filter; + } + + public String getNoContentMessage() { + return noContentMessage; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/DefaultSendHandler.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/DefaultSendHandler.java new file mode 100644 index 000000000..541165994 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/DefaultSendHandler.java @@ -0,0 +1,37 @@ +package org.mvplugins.multiverse.core.display.handlers; + +import java.util.List; + +import co.aikar.commands.BukkitCommandIssuer; +import org.jetbrains.annotations.NotNull; + +/** + * Most basic implementation of {@link SendHandler} that just sends content with no formatting. + */ +public final class DefaultSendHandler implements SendHandler { + + private static DefaultSendHandler instance; + + /** + * Gets the singleton instance of this class. + * + * @return The singleton instance of this class. + */ + public static DefaultSendHandler getInstance() { + if (instance == null) { + instance = new DefaultSendHandler(); + } + return instance; + } + + private DefaultSendHandler() { + } + + /** + * {@inheritDoc} + */ + @Override + public void send(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + content.forEach(issuer::sendMessage); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/InlineSendHandler.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/InlineSendHandler.java new file mode 100644 index 000000000..b6c576aaf --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/InlineSendHandler.java @@ -0,0 +1,67 @@ +package org.mvplugins.multiverse.core.display.handlers; + +import java.util.List; + +import co.aikar.commands.BukkitCommandIssuer; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; + +/** + * Display the contents in a single line. + */ +public class InlineSendHandler extends BaseSendHandler { + + /** + * 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 + ", "; + private String prefix = null; + + InlineSendHandler() { + } + + /** + * {@inheritDoc} + */ + @Override + public void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + if (filter.needToFilter()) { + issuer.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter)); + } + String message = String.join(delimiter, content); + if (prefix != null) { + message = prefix + message; + } + issuer.sendMessage(message); + } + + /** + * 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 InlineSendHandler withPrefix(String prefix) { + this.prefix = prefix; + return this; + } + + public String getDelimiter() { + return delimiter; + } + + public String getPrefix() { + return prefix; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/PagedSendHandler.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/PagedSendHandler.java new file mode 100644 index 000000000..f8875f4da --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/PagedSendHandler.java @@ -0,0 +1,164 @@ +package org.mvplugins.multiverse.core.display.handlers; + +import java.util.List; + +import co.aikar.commands.BukkitCommandIssuer; +import org.bukkit.ChatColor; +import org.bukkit.command.ConsoleCommandSender; +import org.jetbrains.annotations.NotNull; + +/** + * Display content as a list with optional pagination. + */ +public class PagedSendHandler extends BaseSendHandler { + + /** + * 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; // SUPPRESS CHECKSTYLE: MagicNumberCheck + private int targetPage = 1; + + PagedSendHandler() { + } + + /** + * {@inheritDoc} + */ + @Override + public void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + if (!paginate || (issuer.getIssuer() instanceof ConsoleCommandSender && !paginateInConsole)) { + sendNormal(issuer, content); + return; + } + sendPaged(issuer, content); + } + + /** + * Send content list without pagination. + * + * @param issuer The target which the content will be displayed to. + * @param content The content to display. + */ + private void sendNormal(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + if (filter.needToFilter()) { + issuer.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter)); + } + content.forEach(issuer::sendMessage); + } + + /** + * Send content list with pagination. + * + * @param issuer The target which the content will be displayed to. + * @param content The content to display. + */ + private void sendPaged(@NotNull BukkitCommandIssuer issuer, @NotNull List content) { + int totalPages = (content.size() + linesPerPage - 1) / linesPerPage; // Basically just divide round up + if (targetPage < 1 || targetPage > totalPages) { + issuer.sendMessage(String.format("%sInvalid page number. Please enter a page number between 1 and %s", ChatColor.RED, totalPages)); + return; + } + + if (filter.needToFilter()) { + issuer.sendMessage(String.format("%s[Page %s of %s] [Filter '%s']", ChatColor.GRAY, targetPage, totalPages, filter)); + } else { + issuer.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 pageContent = content.subList(startIndex, endIndex); + if (padEnd) { + for (int i = 0; i < (pageEndIndex - endIndex); i++) { + pageContent.add(""); + } + } + pageContent.forEach(issuer::sendMessage); + } + + /** + * 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 if {@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; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/SendHandler.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/SendHandler.java new file mode 100644 index 000000000..7a0719eae --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/SendHandler.java @@ -0,0 +1,20 @@ +package org.mvplugins.multiverse.core.display.handlers; + +import java.util.List; + +import co.aikar.commands.BukkitCommandIssuer; +import org.jetbrains.annotations.NotNull; + +/** + * Handles the sending of all content to the command sender. + */ +@FunctionalInterface +public interface SendHandler { + /** + * Sends all the content to the given command sender. + * + * @param issuer The target which the content will be displayed to. + * @param content The content to display. + */ + void send(@NotNull BukkitCommandIssuer issuer, @NotNull List content); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/handlers/package-info.java b/src/main/java/org/mvplugins/multiverse/core/display/handlers/package-info.java new file mode 100644 index 000000000..1211944d4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/handlers/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the handlers for the display system. + */ +package org.mvplugins.multiverse.core.display.handlers; diff --git a/src/main/java/org/mvplugins/multiverse/core/display/package-info.java b/src/main/java/org/mvplugins/multiverse/core/display/package-info.java new file mode 100644 index 000000000..2fa2c6ea5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the classes that are used to display nicely formatted information to the user. + */ +package org.mvplugins.multiverse.core.display; diff --git a/src/main/java/org/mvplugins/multiverse/core/display/parsers/ContentProvider.java b/src/main/java/org/mvplugins/multiverse/core/display/parsers/ContentProvider.java new file mode 100644 index 000000000..3287fb46b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/parsers/ContentProvider.java @@ -0,0 +1,20 @@ +package org.mvplugins.multiverse.core.display.parsers; + +import java.util.Collection; + +import co.aikar.commands.BukkitCommandIssuer; +import org.jetbrains.annotations.NotNull; + +/** + * Parse objects into string or list of strings. + */ +@FunctionalInterface +public interface ContentProvider { + /** + * Parse the object to string(s) and add it to the content. + * + * @param issuer The target which the content will be displayed to. + * @return The parsed content list. + */ + Collection parse(@NotNull BukkitCommandIssuer issuer); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/parsers/ListContentProvider.java b/src/main/java/org/mvplugins/multiverse/core/display/parsers/ListContentProvider.java new file mode 100644 index 000000000..46971fad7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/parsers/ListContentProvider.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.display.parsers; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import co.aikar.commands.BukkitCommandIssuer; +import org.jetbrains.annotations.NotNull; + +/** + * Simple parser for list object. + * + * @param List element type. + */ +public class ListContentProvider implements ContentProvider { + + /** + * New list content parser for the given list. + * + * @param list The list object to parse. + * @param List element type. + * @return New {@link MapContentProvider} instance. + */ + public static ListContentProvider forContent(List list) { + return new ListContentProvider<>(list); + } + + private final List list; + + private String format = null; + + ListContentProvider(List list) { + this.list = list; + } + + /** + * {@inheritDoc} + */ + @Override + public Collection parse(@NotNull BukkitCommandIssuer issuer) { + if (format == null) { + return list.stream().map(Object::toString).collect(Collectors.toList()); + } + return list.stream().map(element -> String.format(format, element)).collect(Collectors.toList()); + } + + /** + * 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 ListContentProvider} for method chaining. + */ + public ListContentProvider withFormat(String format) { + this.format = format; + return this; + } + + public List getList() { + return list; + } + + public String getFormat() { + return format; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/parsers/MapContentProvider.java b/src/main/java/org/mvplugins/multiverse/core/display/parsers/MapContentProvider.java new file mode 100644 index 000000000..28739cd05 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/parsers/MapContentProvider.java @@ -0,0 +1,122 @@ +package org.mvplugins.multiverse.core.display.parsers; + +import java.util.Collection; +import java.util.Map; + +import co.aikar.commands.BukkitCommandIssuer; +import com.google.common.base.Strings; +import org.bukkit.ChatColor; +import org.jetbrains.annotations.NotNull; + +/** + * Simple parser for map object. + * + * @param Key type. + * @param Value type. + */ +public class MapContentProvider implements ContentProvider { + + /** + * New map content parser for the given map. + * + * @param map The map object to parse. + * @param Key type. + * @param Value type. + * @return New {@link MapContentProvider} instance. + */ + public static MapContentProvider forContent(Map map) { + return new MapContentProvider<>(map); + } + + private final Map map; + + private String format = "%s%s%s%s%s"; + private ChatColor keyColor = ChatColor.WHITE; + private ChatColor valueColor = ChatColor.WHITE; + private String separator = ": "; + + MapContentProvider(Map map) { + this.map = map; + } + + /** + * {@inheritDoc} + */ + @Override + public Collection parse(@NotNull BukkitCommandIssuer issuer) { + return map.entrySet().stream() + .map(e -> String.format(format, keyColor, e.getKey(), separator, valueColor, formatValue(e.getValue()))) + .toList(); + } + + private String formatValue(V value) { + if (value instanceof String stringValue) { + return Strings.isNullOrEmpty(stringValue) ? "&7&onull" : stringValue; + } + return value == null ? "&7&onull" : String.valueOf(value); + } + + /** + * 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 MapContentProvider} for method chaining. + */ + public MapContentProvider withFormat(String format) { + this.format = format; + return this; + } + + /** + * Sets the color for the key text. + * + * @param keyColor The color to use. + * @return Same {@link MapContentProvider} for method chaining. + */ + public MapContentProvider withKeyColor(ChatColor keyColor) { + this.keyColor = keyColor; + return this; + } + + /** + * Sets the color for the value text. + * + * @param valueColor The color to use. + * @return Same {@link MapContentProvider} for method chaining. + */ + public MapContentProvider 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 MapContentProvider} for method chaining. + */ + public MapContentProvider withSeparator(String separator) { + this.separator = separator; + return this; + } + + public Map getMap() { + return map; + } + + public String getFormat() { + return format; + } + + public ChatColor getKeyColor() { + return keyColor; + } + + public ChatColor getValueColor() { + return valueColor; + } + + public String getSeparator() { + return separator; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/display/parsers/package-info.java b/src/main/java/org/mvplugins/multiverse/core/display/parsers/package-info.java new file mode 100644 index 000000000..995c4371c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/display/parsers/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the parsers for the display system. + */ +package org.mvplugins.multiverse.core.display.parsers; diff --git a/src/main/java/org/mvplugins/multiverse/core/economy/ItemEconomy.java b/src/main/java/org/mvplugins/multiverse/core/economy/ItemEconomy.java new file mode 100644 index 000000000..1c44bb3b6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/economy/ItemEconomy.java @@ -0,0 +1,90 @@ +package org.mvplugins.multiverse.core.economy; + +import java.util.HashMap; + +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jvnet.hk2.annotations.Service; + +@Service +final class ItemEconomy { + + private static final String ECONOMY_NAME = "Simple Item Economy"; + + @Inject + private ItemEconomy() { + // Can't instantiate + } + + String getFormattedPrice(double amount, Material currency) { + if (MVEconomist.isItemCurrency(currency)) { + return amount + " " + currency.toString(); + } else { + return ""; + } + } + + String getName() { + return ECONOMY_NAME; + } + + boolean hasEnough(Player player, double amount, Material currency) { + if (currency != null) { + return player.getInventory().contains(currency, (int) amount); + } else { + return true; + } + } + + void deposit(Player player, double amount, Material currency) { + if (MVEconomist.isItemCurrency(currency)) { + giveItem(player, amount, currency); + } + } + + void withdraw(Player player, double amount, Material currency) { + if (MVEconomist.isItemCurrency(currency)) { + takeItem(player, amount, currency); + } + } + + void giveItem(Player player, double amount, Material type) { + ItemStack item = new ItemStack(type, (int) amount); + player.getInventory().addItem(item); + showReceipt(player, (amount * -1), type); + } + + void takeItem(Player player, double amount, Material type) { + int removed = 0; + HashMap items = (HashMap) player.getInventory().all(type); + for (int i : items.keySet()) { + if (removed >= amount) { + break; + } + int diff = (int) (amount - removed); + int amt = player.getInventory().getItem(i).getAmount(); + if (amt - diff > 0) { + player.getInventory().getItem(i).setAmount(amt - diff); + break; + } else { + removed += amt; + player.getInventory().clear(i); + } + } + showReceipt(player, amount, type); + } + + void showReceipt(Player player, double price, Material item) { + if (price > 0D) { + player.sendMessage(String.format("%s%s%s %s", + ChatColor.WHITE, "You have been charged", ChatColor.GREEN, getFormattedPrice(price, item))); + } else if (price < 0D) { + player.sendMessage(String.format("%s%s%s %s", + ChatColor.DARK_GREEN, getFormattedPrice((price * -1), item), + ChatColor.WHITE, "has been added to your inventory.")); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/MVEconomist.java b/src/main/java/org/mvplugins/multiverse/core/economy/MVEconomist.java similarity index 67% rename from src/main/java/com/onarandombox/MultiverseCore/utils/MVEconomist.java rename to src/main/java/org/mvplugins/multiverse/core/economy/MVEconomist.java index 04353649a..6cb8e6c38 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/MVEconomist.java +++ b/src/main/java/org/mvplugins/multiverse/core/economy/MVEconomist.java @@ -1,24 +1,30 @@ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core.economy; -import org.bukkit.ChatColor; +import jakarta.inject.Inject; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; -import java.util.HashMap; +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.world.MultiverseWorld; /** * Multiverse's Friendly Economist. This is used to deal with external economies and also item costs for stuff in MV. */ -public class MVEconomist { +@Service +public final class MVEconomist { + public static final String VAULT_ECONOMY_CODE = "@vault-economy"; + public static final Material VAULT_ECONOMY_MATERIAL = Material.AIR; private final VaultHandler vaultHandler; + private final ItemEconomy itemEconomy; - public MVEconomist(Plugin plugin) { + @Inject + public MVEconomist(MultiverseCore plugin, ItemEconomy itemEconomy) { vaultHandler = new VaultHandler(plugin); + this.itemEconomy = itemEconomy; } private boolean isUsingVault(Material currency) { @@ -45,7 +51,7 @@ public String formatPrice(double amount, @Nullable Material currency) { if (isUsingVault(currency)) { return getVaultHandler().getEconomy().format(amount); } else { - return ItemEconomy.getFormattedPrice(amount, currency); + return itemEconomy.getFormattedPrice(amount, currency); } } @@ -58,7 +64,7 @@ public String getEconomyName() { if (getVaultHandler().hasEconomy()) { return getVaultHandler().getEconomy().getName(); } else { - return ItemEconomy.getName(); + return itemEconomy.getName(); } } @@ -76,7 +82,7 @@ public boolean isPlayerWealthyEnough(Player player, double amount, Material curr } else if (isUsingVault(currency)) { return getVaultHandler().getEconomy().has(player, amount); } else { - return ItemEconomy.hasEnough(player, amount, currency); + return itemEconomy.hasEnough(player, amount, currency); } } @@ -91,6 +97,35 @@ public String getNSFMessage(Material currency, String message) { return "Sorry, you don't have enough " + (isItemCurrency(currency) ? "items" : "funds") + ". " + message; } + /** + * Pays for a given amount of currency either from the player's economy account or inventory if the currency. + * + * @param player the player to deposit currency into. + * @param world the world to take entry fee from. + */ + public void payEntryFee(Player player, MultiverseWorld world) { + payEntryFee(player, world.getPrice(), world.getCurrency()); + } + + /** + * Pays for a given amount of currency either from the player's economy account or inventory if the currency. + * + * @param player the player to take currency from. + * @param price the amount to take. + * @param currency the type of currency. + */ + public void payEntryFee(Player player, double price, Material currency) { + if (price == 0D) { + return; + } + + if (price < 0) { + this.deposit(player, -price, currency); + } else { + this.withdraw(player, price, currency); + } + } + /** * Deposits a given amount of currency either into the player's economy account or inventory if the currency * is not null. @@ -103,7 +138,7 @@ public void deposit(Player player, double amount, @Nullable Material currency) { if (isUsingVault(currency)) { getVaultHandler().getEconomy().depositPlayer(player, amount); } else { - ItemEconomy.deposit(player, amount, currency); + itemEconomy.deposit(player, amount, currency); } } @@ -119,7 +154,7 @@ public void withdraw(Player player, double amount, @Nullable Material currency) if (isUsingVault(currency)) { getVaultHandler().getEconomy().withdrawPlayer(player, amount); } else { - ItemEconomy.withdraw(player, amount, currency); + itemEconomy.withdraw(player, amount, currency); } } @@ -191,11 +226,8 @@ public void setBalance(Player player, World world, double amount) throws Illegal * This method is public for backwards compatibility. * * @return the old VaultHandler. - * @deprecated just use the other methods in this class for economy stuff. */ - // TODO make private - @Deprecated - public VaultHandler getVaultHandler() { + private VaultHandler getVaultHandler() { return vaultHandler; } @@ -206,80 +238,7 @@ public VaultHandler getVaultHandler() { * @return true if currency string matches a valid material. */ public static boolean isItemCurrency(Material currency) { - return currency != null; + return currency != VAULT_ECONOMY_MATERIAL; } - private static class ItemEconomy { - - private static final String ECONOMY_NAME = "Simple Item Economy"; - - private static String getFormattedPrice(double amount, Material currency) { - if (isItemCurrency(currency)) { - return amount + " " + currency.toString(); - } else { - return ""; - } - } - - private static String getName() { - return ECONOMY_NAME; - } - - private static boolean hasEnough(Player player, double amount, Material currency) { - if (currency != null) { - return player.getInventory().contains(currency, (int) amount); - } else { - return true; - } - } - - private static void deposit(Player player, double amount, Material currency) { - if (isItemCurrency(currency)) { - giveItem(player, amount, currency); - } - } - - private static void withdraw(Player player, double amount, Material currency) { - if (isItemCurrency(currency)) { - takeItem(player, amount, currency); - } - } - - private static void giveItem(Player player, double amount, Material type) { - ItemStack item = new ItemStack(type, (int) amount); - player.getInventory().addItem(item); - showReceipt(player, (amount * -1), type); - } - - private static void takeItem(Player player, double amount, Material type) { - int removed = 0; - HashMap items = (HashMap) player.getInventory().all(type); - for (int i : items.keySet()) { - if (removed >= amount) { - break; - } - int diff = (int) (amount - removed); - int amt = player.getInventory().getItem(i).getAmount(); - if (amt - diff > 0) { - player.getInventory().getItem(i).setAmount(amt - diff); - break; - } else { - removed += amt; - player.getInventory().clear(i); - } - } - showReceipt(player, amount, type); - } - - private static void showReceipt(Player player, double price, Material item) { - if (price > 0D) { - player.sendMessage(String.format("%s%s%s %s", - ChatColor.WHITE, "You have been charged", ChatColor.GREEN, getFormattedPrice(price, item))); - } else if (price < 0D) { - player.sendMessage(String.format("%s%s%s %s", - ChatColor.DARK_GREEN, getFormattedPrice((price * -1), item), - ChatColor.WHITE, "has been added to your inventory.")); - } - } - } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/VaultHandler.java b/src/main/java/org/mvplugins/multiverse/core/economy/VaultHandler.java similarity index 86% rename from src/main/java/com/onarandombox/MultiverseCore/utils/VaultHandler.java rename to src/main/java/org/mvplugins/multiverse/core/economy/VaultHandler.java index 4e4e0c1bf..52913e05c 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/VaultHandler.java +++ b/src/main/java/org/mvplugins/multiverse/core/economy/VaultHandler.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core.economy; import com.dumptruckman.minecraft.util.Logging; import net.milkbowl.vault.economy.Economy; @@ -13,19 +13,20 @@ /** * A class we use to interface with Vault when it is present. */ -public class VaultHandler implements Listener { +final class VaultHandler implements Listener { private Economy economy; - public VaultHandler(final Plugin plugin) { + VaultHandler(final Plugin plugin) { Bukkit.getPluginManager().registerEvents(new VaultListener(), plugin); setupVaultEconomy(); } private boolean setupVaultEconomy() { if (Bukkit.getPluginManager().getPlugin("Vault") != null) { - final RegisteredServiceProvider economyProvider = - Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); + final RegisteredServiceProvider economyProvider = Bukkit + .getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); if (economyProvider != null) { Logging.fine("Vault economy enabled."); economy = economyProvider.getProvider(); @@ -38,13 +39,31 @@ private boolean setupVaultEconomy() { economy = null; } - return (economy != null); + return economy != null; + } + + /** + * Checks whether Vault is in use and has an economy system enabled. + * + * @return true if vault is in use and has an economy system enabled. + */ + public boolean hasEconomy() { + return economy != null; + } + + /** + * Returns the Vault economy system if Vault is present and has an economy system enabled. + * + * @return The vault economy system or null if not configured. + */ + public Economy getEconomy() { + return economy; } /** * Listens for Vault plugin events. */ - private class VaultListener implements Listener { + private final class VaultListener implements Listener { @EventHandler private void vaultEnabled(PluginEnableEvent event) { if (event.getPlugin() != null && event.getPlugin().getName().equals("Vault")) { @@ -60,22 +79,4 @@ private void vaultDisabled(PluginDisableEvent event) { } } } - - /** - * Checks whether Vault is in use and has an economy system enabled. - * - * @return true if vault is in use and has an economy system enabled. - */ - public boolean hasEconomy() { - return economy != null; - } - - /** - * Returns the Vault economy system if Vault is present and has an economy system enabled. - * - * @return The vault economy system or null if not configured. - */ - public Economy getEconomy() { - return economy; - } } diff --git a/src/main/java/org/mvplugins/multiverse/core/economy/package-info.java b/src/main/java/org/mvplugins/multiverse/core/economy/package-info.java new file mode 100644 index 000000000..67577271d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/economy/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains the economy classes to handle external economy plugins. + */ +package org.mvplugins.multiverse.core.economy; diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVConfigReloadEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVConfigReloadEvent.java similarity index 90% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVConfigReloadEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVConfigReloadEvent.java index bae3541b1..d0ae05bff 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVConfigReloadEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVConfigReloadEvent.java @@ -5,18 +5,18 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.event; +package org.mvplugins.multiverse.core.event; + +import java.util.List; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import java.util.List; - /** * Called when the Multiverse-config should be reloaded. */ -public class MVConfigReloadEvent extends Event { - private List configsLoaded; +public final class MVConfigReloadEvent extends Event { + private final List configsLoaded; public MVConfigReloadEvent(List configsLoaded) { this.configsLoaded = configsLoaded; @@ -34,6 +34,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -42,6 +43,7 @@ public static HandlerList getHandlerList() { /** * Adds a config to this event. + * * @param config The config to add. */ public void addConfig(String config) { @@ -50,6 +52,7 @@ public void addConfig(String config) { /** * Gets all loaded configs. + * * @return A list of all loaded configs. */ public List getAllConfigsLoaded() { diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVDebugModeEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVDebugModeEvent.java similarity index 88% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVDebugModeEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVDebugModeEvent.java index f3c3408b7..2985a2e5c 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVDebugModeEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVDebugModeEvent.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.event; +package org.mvplugins.multiverse.core.event; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -6,7 +6,7 @@ /** * Called when Core's debug level is changed. */ -public class MVDebugModeEvent extends Event { +public final class MVDebugModeEvent extends Event { private static final HandlerList HANDLERS = new HandlerList(); @@ -26,6 +26,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVDumpsDebugInfoEvent.java similarity index 75% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVDumpsDebugInfoEvent.java index 9b1bc6326..8197a3cac 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVDumpsDebugInfoEvent.java @@ -1,8 +1,4 @@ -package com.onarandombox.MultiverseCore.event; - -import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; +package org.mvplugins.multiverse.core.event; import java.io.BufferedReader; import java.io.File; @@ -13,17 +9,21 @@ import java.util.HashMap; import java.util.Map; +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + /** * Called when somebody requests version information about Multiverse. */ -public class MVVersionEvent extends Event { +public final class MVDumpsDebugInfoEvent extends Event { - private final StringBuilder versionInfoBuilder; - private final Map detailedVersionInfo; + private final StringBuilder debugInfoBuilder; + private final Map detailedDebugInfo; - public MVVersionEvent() { - versionInfoBuilder = new StringBuilder(); - detailedVersionInfo = new HashMap<>(); + public MVDumpsDebugInfoEvent() { + debugInfoBuilder = new StringBuilder(); + detailedDebugInfo = new HashMap<>(); } private static final HandlerList HANDLERS = new HandlerList(); @@ -38,6 +38,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -46,33 +47,35 @@ public static HandlerList getHandlerList() { /** * Gets the version-info currently saved in this event. + * * @return The version-info. */ - public String getVersionInfo() { - return this.versionInfoBuilder.toString(); + public String getDebugInfo() { + return this.debugInfoBuilder.toString(); } /** * Gets the key/value pair of the detailed version info. - * + *

* This information is used for advanced paste services that would prefer * to get the information as several files. Examples include config.yml or * portals.yml. Note that the map returned is immutable. - * + *

* The keys are filenames, the values are the contents of the files. * * @return The immutable key value mapping of files and the contents of those files. */ - public Map getDetailedVersionInfo() { - return Collections.unmodifiableMap(this.detailedVersionInfo); + public Map getDetailedDebugInfo() { + return Collections.unmodifiableMap(this.detailedDebugInfo); } /** * Appends more version-info to the version-info currently saved in this event. + * * @param moreVersionInfo The version-info to add. Should end with '\n'. */ - public void appendVersionInfo(String moreVersionInfo) { - this.versionInfoBuilder.append(moreVersionInfo); + public void appendDebugInfo(String moreVersionInfo) { + this.debugInfoBuilder.append(moreVersionInfo); } private String readFile(final String filename) { @@ -101,19 +104,21 @@ private String readFile(final String filename) { /** * Adds a file to to the detailed version-info currently saved in this event. + * * @param fileName The name of the file. * @param contents The contents of the file. */ - public void putDetailedVersionInfo(String fileName, String contents) { - this.detailedVersionInfo.put(fileName, contents); + public void putDetailedDebugInfo(String fileName, String contents) { + this.detailedDebugInfo.put(fileName, contents); } /** * Adds a file to to the detailed version-info currently saved in this event. + * * @param filename The name of the file. * @param file The file. */ - public void putDetailedVersionInfo(String filename, File file) { - this.putDetailedVersionInfo(filename, readFile(file.getAbsolutePath())); + public void putDetailedDebugInfo(String filename, File file) { + this.putDetailedDebugInfo(filename, readFile(file.getAbsolutePath())); } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVPlayerTouchedPortalEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVPlayerTouchedPortalEvent.java similarity index 91% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVPlayerTouchedPortalEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVPlayerTouchedPortalEvent.java index 56ec89e3d..1cf265641 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVPlayerTouchedPortalEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVPlayerTouchedPortalEvent.java @@ -5,7 +5,7 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.event; +package org.mvplugins.multiverse.core.event; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -16,7 +16,7 @@ /** * This event is thrown when a portal is touched. */ -public class MVPlayerTouchedPortalEvent extends Event implements Cancellable { +public final class MVPlayerTouchedPortalEvent extends Event implements Cancellable { private Player p; private Location l; private boolean isCancelled; @@ -39,6 +39,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -47,6 +48,7 @@ public static HandlerList getHandlerList() { /** * Gets the {@link Location} of the portal-block that was touched. + * * @return The {@link Location} of the portal-block that was touched. */ public Location getBlockTouched() { @@ -55,6 +57,7 @@ public Location getBlockTouched() { /** * Gets the {@link Player} that's touching the portal. + * * @return The {@link Player} that's touching the portal. */ public Player getPlayer() { @@ -81,11 +84,17 @@ public void setCanUseThisPortal(boolean canUse) { this.canUse = canUse; } + /** + * {@inheritDoc} + */ @Override public boolean isCancelled() { return this.isCancelled; } + /** + * {@inheritDoc} + */ @Override public void setCancelled(boolean b) { this.isCancelled = b; diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVRespawnEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVRespawnEvent.java similarity index 62% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVRespawnEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVRespawnEvent.java index 0f71cdf87..1121afebd 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVRespawnEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVRespawnEvent.java @@ -5,26 +5,24 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.event; +package org.mvplugins.multiverse.core.event; import org.bukkit.Location; import org.bukkit.entity.Player; -import org.bukkit.event.Event; +import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; /** * Called when a player is respawning. */ -public class MVRespawnEvent extends Event { - private Player player; +public final class MVRespawnEvent extends PlayerEvent implements Cancellable { private Location location; - private String respawnMethod; + private boolean cancelled = false; - - public MVRespawnEvent(Location spawningAt, Player p, String respawnMethod) { - this.player = p; + public MVRespawnEvent(Location spawningAt, Player player) { + super(player); this.location = spawningAt; - this.respawnMethod = respawnMethod; } private static final HandlerList HANDLERS = new HandlerList(); @@ -39,6 +37,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -46,34 +45,36 @@ public static HandlerList getHandlerList() { } /** - * Gets the {@link Player} that's respawning. - * @return The {@link Player} that's respawning. + * Gets the player's respawn-{@link Location}. + * + * @return The player's respawn-{@link Location}. */ - public Player getPlayer() { - return this.player; + public Location getRespawnLocation() { + return this.location; } /** - * Gets the respawn-method. - * @return The respawn-method. + * Sets the player's respawn-{@link Location}. + * + * @param location The new respawn-{@link Location}. */ - public String getRespawnMethod() { - return this.respawnMethod; + public void setRespawnLocation(Location location) { + this.location = location; } /** - * Gets the player's respawn-{@link Location}. - * @return The player's respawn-{@link Location}. + * {@inheritDoc} */ - public Location getPlayersRespawnLocation() { - return this.location; + @Override + public boolean isCancelled() { + return cancelled; } /** - * Sets the player's respawn-{@link Location}. - * @param l The new respawn-{@link Location}. + * {@inheritDoc} */ - public void setRespawnLocation(Location l) { - this.location = l; + @Override + public void setCancelled(boolean cancel) { + this.cancelled = cancel; } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVTeleportEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVTeleportDestinationEvent.java similarity index 69% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVTeleportEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVTeleportDestinationEvent.java index fac53728b..c3654f0ad 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVTeleportEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVTeleportDestinationEvent.java @@ -5,33 +5,31 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.event; - -import com.onarandombox.MultiverseCore.api.MVDestination; -import com.onarandombox.MultiverseCore.api.SafeTTeleporter; +package org.mvplugins.multiverse.core.event; import org.bukkit.Location; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; + /** - * Event that gets called when a player use the /mvtp command. + * Event that gets called when a player teleports to a {@link DestinationInstance} with {@link AsyncSafetyTeleporter}. */ -public class MVTeleportEvent extends Event implements Cancellable { - private Player teleportee; - private CommandSender teleporter; - private MVDestination dest; - private boolean useSafeTeleport; +public final class MVTeleportDestinationEvent extends Event implements Cancellable { + private final Entity teleportee; + private final CommandSender teleporter; + private final DestinationInstance dest; private boolean isCancelled; - public MVTeleportEvent(MVDestination dest, Player teleportee, CommandSender teleporter, boolean safeTeleport) { + public MVTeleportDestinationEvent(DestinationInstance dest, Entity teleportee, CommandSender teleporter) { this.teleportee = teleportee; this.teleporter = teleporter; this.dest = dest; - this.useSafeTeleport = safeTeleport; } private static final HandlerList HANDLERS = new HandlerList(); @@ -46,6 +44,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -57,7 +56,7 @@ public static HandlerList getHandlerList() { * * @return The player who will be teleported by this event. */ - public Player getTeleportee() { + public Entity getTeleportee() { return this.teleportee; } @@ -84,18 +83,10 @@ public CommandSender getTeleporter() { * * @return The destination the player will spawn at. */ - public MVDestination getDestination() { + public DestinationInstance getDestination() { return this.dest; } - /** - * Looks if this {@link MVTeleportEvent} is using the {@link SafeTTeleporter}. - * @return True if this {@link MVTeleportEvent} is using the {@link SafeTTeleporter}. - */ - public boolean isUsingSafeTTeleporter() { - return useSafeTeleport; - } - @Override public boolean isCancelled() { return this.isCancelled; diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVWorldDeleteEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVWorldDeleteEvent.java similarity index 56% rename from src/main/java/com/onarandombox/MultiverseCore/event/MVWorldDeleteEvent.java rename to src/main/java/org/mvplugins/multiverse/core/event/MVWorldDeleteEvent.java index 9615e4741..5fd899ee2 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVWorldDeleteEvent.java +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVWorldDeleteEvent.java @@ -1,26 +1,22 @@ -package com.onarandombox.MultiverseCore.event; +package org.mvplugins.multiverse.core.event; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; - -import com.onarandombox.MultiverseCore.api.MultiverseWorld; import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + /** * Called when a world is about to be deleted by Multiverse. */ -public class MVWorldDeleteEvent extends Event implements Cancellable { +public final class MVWorldDeleteEvent extends Event implements Cancellable { private boolean cancelled = false; - private final MultiverseWorld world; - private final boolean removeFromConfig; - - public MVWorldDeleteEvent(MultiverseWorld world, boolean removeFromConfig) { - if (world == null) - throw new IllegalArgumentException("world can't be null!"); + private final LoadedMultiverseWorld world; + public MVWorldDeleteEvent(@NotNull LoadedMultiverseWorld world) { this.world = world; - this.removeFromConfig = removeFromConfig; } private static final HandlerList HANDLERS = new HandlerList(); @@ -35,6 +31,7 @@ public HandlerList getHandlers() { /** * Gets the handler list. This is required by the event system. + * * @return A list of HANDLERS. */ public static HandlerList getHandlerList() { @@ -60,19 +57,9 @@ public void setCancelled(boolean cancel) { /** * Gets the world that's about to be deleted. * - * @return That {@link MultiverseWorld}. + * @return That {@link LoadedMultiverseWorld}. */ - public MultiverseWorld getWorld() { + public LoadedMultiverseWorld getWorld() { return world; } - - /** - * Is the world about to be removed from the config? - * - * @return True if yes, false if no. - */ - public boolean removeWorldFromConfig() { - return removeFromConfig; - } - } diff --git a/src/main/java/org/mvplugins/multiverse/core/event/MVWorldPropertyChangeEvent.java b/src/main/java/org/mvplugins/multiverse/core/event/MVWorldPropertyChangeEvent.java new file mode 100644 index 000000000..11c180d68 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/event/MVWorldPropertyChangeEvent.java @@ -0,0 +1,92 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.event; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import org.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +/** + * This event is fired after the property is changed. + *

+ * To get the name of the property that was changed, use {@link #getName()}. + * To get the old value, use {@link #getOldValue()}. + * To get the new value, use {@link #getNewValue()}. + * + * @param The type of the property that was set. + */ +public final class MVWorldPropertyChangeEvent extends Event { + private final MultiverseWorld world; + private final String name; + private final T oldValue; + private final T newValue; + + public MVWorldPropertyChangeEvent(@NotNull MultiverseWorld world, @NotNull String name, T oldValue, T value) { + this.world = world; + this.name = name; + this.oldValue = oldValue; + this.newValue = value; + } + + private static final HandlerList HANDLERS = new HandlerList(); + + /** + * {@inheritDoc} + */ + @Override + public HandlerList getHandlers() { + return HANDLERS; + } + + /** + * Gets the handler list. This is required by the event system. + * + * @return A list of handlers. + */ + public static HandlerList getHandlerList() { + return HANDLERS; + } + + /** + * Gets the changed world property's name. + * + * @return The changed world property's name. + */ + public @NotNull String getName() { + return this.name; + } + + /** + * Gets the old value. + * + * @return The old value. + */ + public T getOldValue() { + return oldValue; + } + + /** + * Gets the new value. + * + * @return The new value. + */ + public T getNewValue() { + return this.newValue; + } + + /** + * Get the world targeted because of this change. + * + * @return A valid MultiverseWorld. + */ + public @NotNull MultiverseWorld getWorld() { + return this.world; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/event/package-info.java b/src/main/java/org/mvplugins/multiverse/core/event/package-info.java new file mode 100644 index 000000000..1d46f1576 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/event/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all multiverse core events. + */ +package org.mvplugins.multiverse.core.event; diff --git a/src/main/java/org/mvplugins/multiverse/core/exceptions/MultiverseException.java b/src/main/java/org/mvplugins/multiverse/core/exceptions/MultiverseException.java new file mode 100644 index 000000000..51e3df65f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/exceptions/MultiverseException.java @@ -0,0 +1,77 @@ +package org.mvplugins.multiverse.core.exceptions; + +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.locale.message.Message; + +/** + * A base exception for Multiverse. + *
+ * {@link #getMVMessage()} provides access to a {@link Message} which can be used to provide a localized message. + */ +public class MultiverseException extends Exception { + + private final transient @Nullable Message message; + + /** + * Creates a new exception with the given message. + *
+ * If the message is not null, this exception will also contain a {@link Message} which can be accessed via + * {@link #getMVMessage()}. This message will just be the given message wrapped in a {@link Message}. + * + * @param message The message for the exception + */ + public MultiverseException(@Nullable String message) { + super(message); + this.message = message != null ? Message.of(message) : null; + } + + /** + * Creates a new exception with the given message. + *
+ * If the message is not null, this exception will also contain a String message which can be accessed via + * {@link #getMessage()}. This message will just be the given message formatted without locale support. + * + * @param message The message for the exception + */ + public MultiverseException(@Nullable Message message) { + super(message != null ? message.formatted() : null); + this.message = message; + } + + /** + * Creates a new exception with the given message and cause. + *
+ * If the message is not null, this exception will also contain a {@link Message} which can be accessed via + * {@link #getMVMessage()}. This message will just be the given message wrapped in a {@link Message}. + * + * @param message The message for the exception + * @param cause The cause of the exception + */ + public MultiverseException(@Nullable String message, @Nullable Throwable cause) { + this(message != null ? Message.of(message) : null, cause); + } + + /** + * Creates a new exception with the given message and cause. + *
+ * If the message is not null, this exception will also contain a String message which can be accessed via + * {@link #getMessage()}. This message will just be the given message formatted without locale support. + * + * @param message The message for the exception + * @param cause The cause of the exception + */ + public MultiverseException(@Nullable Message message, @Nullable Throwable cause) { + super(message != null ? message.formatted() : null, cause); + this.message = message; + } + + /** + * Gets the {@link Message} for this exception. + * + * @return The message, or null if none was provided + */ + public final @Nullable Message getMVMessage() { + return message; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/exceptions/package-info.java b/src/main/java/org/mvplugins/multiverse/core/exceptions/package-info.java new file mode 100644 index 000000000..ee7ef1c0f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/exceptions/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all exceptions. + */ +package org.mvplugins.multiverse.core.exceptions; diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocator.java b/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocator.java new file mode 100644 index 000000000..191b2e4ee --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocator.java @@ -0,0 +1,151 @@ +package org.mvplugins.multiverse.core.inject; + +import io.vavr.control.Try; +import org.bukkit.plugin.Plugin; +import org.glassfish.hk2.api.DynamicConfigurationService; +import org.glassfish.hk2.api.MultiException; +import org.glassfish.hk2.api.ServiceHandle; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.utilities.ClasspathDescriptorFileFinder; +import org.glassfish.hk2.utilities.ServiceLocatorUtilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.inject.binder.PluginBinder; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.stream.Collectors; + +public final class PluginServiceLocator { + + private final PluginBinder pluginBinder; + private final ServiceLocator serviceLocator; + + PluginServiceLocator(@NotNull PluginBinder pluginBinder, @NotNull ServiceLocator serviceLocator) { + this.pluginBinder = pluginBinder; + this.serviceLocator = serviceLocator; + } + + /** + * Starts and inject the plugin classes. + * + * @return The plugin service locator + */ + public @NotNull Try enable() { + return bindPlugin() + .flatMap(ignore -> populateServices()) + .flatMap(ignore -> injectPlugin()) + .mapTry(ignore -> this); + } + + /** + * Shut down the service locator + */ + public void disable() { + serviceLocator.preDestroy(pluginBinder.getPlugin()); + } + + @NotNull + private Try bindPlugin() { + return Try.of(() -> { + ServiceLocatorUtilities.bind(serviceLocator, pluginBinder); + return null; + }); + } + + @NotNull + private Try populateServices() { + Plugin plugin = pluginBinder.getPlugin(); + return Try.of(() -> serviceLocator.getService(DynamicConfigurationService.class)) + .mapTry(dynamicConfigurationService -> { + dynamicConfigurationService.getPopulator().populate(new ClasspathDescriptorFileFinder( + plugin.getClass().getClassLoader(), + plugin.getName()) + ); + return null; + }); + } + + @NotNull + private Try injectPlugin() { + return Try.of(() -> { + serviceLocator.inject(pluginBinder.getPlugin()); + serviceLocator.postConstruct(pluginBinder.getPlugin()); + return null; + }); + } + + /** + * Gets the best service from this plugin that implements the given contract or has the given implementation. + * Service will be instantiated if it is not already instantiated. + * + * @param contractOrImpl The contract or concrete implementation to get the best instance of + * @param qualifiers The set of qualifiers that must match this service definition + * @param The type of the contract to get + * @return An instance of the contract or impl if it is a service, null otherwise + * @throws MultiException if there was an error during service lookup + */ + public @Nullable T getService(@NotNull Class contractOrImpl, Annotation... qualifiers) throws MultiException { + return serviceLocator.getService(contractOrImpl, qualifiers); + } + + /** + * Gets the best active service from this plugin that implements the given contract or has the given implementation. + * + * @param contractOrImpl The contract or concrete implementation to get the best instance of + * @param qualifiers The set of qualifiers that must match this service definition + * @param The type of the contract to get + * @return An instance of the contract or impl if it is a service and is already instantiated, null otherwise + * @throws MultiException if there was an error during service lookup + */ + public @Nullable T getActiveService(@NotNull Class contractOrImpl, Annotation... qualifiers) throws MultiException { + var handle = serviceLocator.getServiceHandle(contractOrImpl, qualifiers); + if (handle != null && handle.isActive()) { + return handle.getService(); + } + return null; + } + + /** + * Gets all services from this plugin that implement the given contract or have the given implementation and have + * the provided qualifiers. Services will be instantiated if it is not already instantiated. + * + * @param contractOrImpl The contract or concrete implementation to get the best instance of + * @param qualifiers The set of qualifiers that must match this service definition + * @param The type of the contract to get + * @return A list of services implementing this contract or concrete implementation. May not return null, but may + * return an empty list. + * @throws MultiException if there was an error during service lookup + */ + public @NotNull List getAllServices( + @NotNull Class contractOrImpl, + Annotation... qualifiers) throws MultiException { + return serviceLocator.getAllServices(contractOrImpl, qualifiers); + } + + /** + * Gets all services from this plugin that implement the given contract or have the given implementation and have + * the provided qualifiers. + * + * @param contractOrImpl The contract or concrete implementation to get the best instance of + * @param qualifiers The set of qualifiers that must match this service definition + * @param The type of the contract to get + * @return A list of services already instantiated implementing this contract or concrete implementation. + * May not return null, but may return an empty list. + * @throws MultiException if there was an error during service lookup + */ + public @NotNull List getAllActiveServices( + @NotNull Class contractOrImpl, + Annotation... qualifiers) throws MultiException { + var handles = serviceLocator.getAllServiceHandles(contractOrImpl, qualifiers); + return handles.stream() + .filter(ServiceHandle::isActive) + .map(ServiceHandle::getService) + .collect(Collectors.toList()); + } + + @NotNull + ServiceLocator getServiceLocator() { + return serviceLocator; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocatorFactory.java b/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocatorFactory.java new file mode 100644 index 000000000..8cc8a7fc6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/PluginServiceLocatorFactory.java @@ -0,0 +1,104 @@ +package org.mvplugins.multiverse.core.inject; + +import io.vavr.control.Try; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.glassfish.hk2.api.DynamicConfigurationService; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.api.ServiceLocatorFactory; +import org.glassfish.hk2.internal.ServiceLocatorFactoryImpl; +import org.glassfish.hk2.utilities.ServiceLocatorUtilities; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.inject.binder.PluginBinder; + +import java.util.Objects; + +public final class PluginServiceLocatorFactory { + + private static PluginServiceLocatorFactory instance = null; + + public static PluginServiceLocatorFactory get() { + if (instance == null) { + instance = new PluginServiceLocatorFactory(); + instance.init().getOrElseThrow(exception -> new IllegalStateException(exception)); + } + return instance; + } + + private final ServiceLocatorFactory serviceLocatorFactory; + private ServiceLocator baseServiceLocator; + + private PluginServiceLocatorFactory() { + this.serviceLocatorFactory = new ServiceLocatorFactoryImpl(); + } + + @NotNull + private Try init() { + return createSystemServiceLocator() + .flatMap(this::createServerServiceLocator) + .mapTry(locator -> { + baseServiceLocator = locator; + return null; + }); + } + + /** + * Stops injection of all Multiverse plugins. + */ + public void shutdown() { + baseServiceLocator.shutdown(); + baseServiceLocator = null; + instance = null; + } + + @NotNull + private Try createSystemServiceLocator() { + return Try.of(() -> serviceLocatorFactory.create("system")) + .mapTry(systemServiceLocator -> { + systemServiceLocator.getService(DynamicConfigurationService.class) + .getPopulator() + .populate(); + return systemServiceLocator; + }); + } + + @NotNull + private Try createServerServiceLocator(@NotNull ServiceLocator systemServiceLocator) { + return Try.of(() -> serviceLocatorFactory.create("server", systemServiceLocator)) + .mapTry(locator -> { + ServiceLocatorUtilities.bind(locator, new ServerBinder()); + return locator; + }); + } + + public Try registerPlugin(@NotNull PluginBinder pluginBinder) { + Objects.requireNonNull(baseServiceLocator, "PluginServiceLocatorFactory has not been initialized."); + return createPluginServiceLocator(pluginBinder, baseServiceLocator); + } + + public Try registerPlugin( + @NotNull PluginBinder pluginBinder, + @NotNull PluginServiceLocator parentServiceLocator) { + return createPluginServiceLocator(pluginBinder, parentServiceLocator.getServiceLocator()); + } + + private Try createPluginServiceLocator( + @NotNull PluginBinder pluginBinder, + @Nullable ServiceLocator parentServiceLocator) { + return Try.of(() -> new PluginServiceLocator( + pluginBinder, + serviceLocatorFactory.create(pluginBinder.getPlugin().getName(), parentServiceLocator))); + } + + private static final class ServerBinder extends AbstractBinder { + @Override + protected void configure() { + bind(Bukkit.getServer()).to(Server.class); + bind(Bukkit.getPluginManager()).to(PluginManager.class); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/binder/JavaPluginBinder.java b/src/main/java/org/mvplugins/multiverse/core/inject/binder/JavaPluginBinder.java new file mode 100644 index 000000000..f9fda45b9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/binder/JavaPluginBinder.java @@ -0,0 +1,22 @@ +package org.mvplugins.multiverse.core.inject.binder; + +import org.bukkit.plugin.java.JavaPlugin; +import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder; +import org.jetbrains.annotations.NotNull; + +/** + * A base class for java plugin binders. Binds the plugin to the {@link JavaPlugin} interface. + * + * @param The type of the plugin. + */ +public abstract class JavaPluginBinder extends PluginBinder { + + protected JavaPluginBinder(@NotNull T plugin) { + super(plugin); + } + + @Override + protected ScopedBindingBuilder bindPluginClass(ScopedBindingBuilder bindingBuilder) { + return bindingBuilder.to(JavaPlugin.class); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/binder/PluginBinder.java b/src/main/java/org/mvplugins/multiverse/core/inject/binder/PluginBinder.java new file mode 100644 index 000000000..5eee3c133 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/binder/PluginBinder.java @@ -0,0 +1,42 @@ +package org.mvplugins.multiverse.core.inject.binder; + +import java.util.logging.Logger; + +import org.bukkit.plugin.Plugin; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.hk2.utilities.binding.ScopedBindingBuilder; +import org.jetbrains.annotations.NotNull; + +/** + * The base class for all plugin binders. Initiates the binding of the plugin instance and initially binds it to the + * {@link Plugin} interface. + * + * @param The type of the plugin. + */ +public abstract class PluginBinder extends AbstractBinder { + + private final T plugin; + + protected PluginBinder(@NotNull T plugin) { + this.plugin = plugin; + } + + @NotNull + public T getPlugin() { + return plugin; + } + + @Override + protected final void configure() { + var bindingBuilder = bindPlugin(getPlugin()); + bindingBuilder.to(Plugin.class); + bindPluginClass(bindingBuilder); + bind(plugin.getLogger()).to(Logger.class); + } + + private ScopedBindingBuilder bindPlugin(T plugin) { + return bind(plugin); + } + + protected abstract ScopedBindingBuilder bindPluginClass(ScopedBindingBuilder bindingBuilder); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/binder/package-info.java b/src/main/java/org/mvplugins/multiverse/core/inject/binder/package-info.java new file mode 100644 index 000000000..27733df4b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/binder/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.inject.binder; diff --git a/src/main/java/org/mvplugins/multiverse/core/inject/package-info.java b/src/main/java/org/mvplugins/multiverse/core/inject/package-info.java new file mode 100644 index 000000000..6e9d29d72 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/inject/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.inject; diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/CoreListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/CoreListener.java new file mode 100644 index 000000000..7b91d30aa --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/CoreListener.java @@ -0,0 +1,9 @@ +package org.mvplugins.multiverse.core.listeners; + +import org.bukkit.event.Listener; +import org.jvnet.hk2.annotations.Contract; + +@Contract +public sealed interface CoreListener extends Listener permits MVChatListener, MVEntityListener, MVLocalesListener, + MVPlayerListener, MVPortalListener, MVWeatherListener, MVWorldListener { +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java new file mode 100644 index 000000000..c0099a56f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java @@ -0,0 +1,64 @@ +package org.mvplugins.multiverse.core.listeners; + +import jakarta.inject.Inject; +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.world.WorldManager; + +/** + * Multiverse's Listener for players. + */ +@Service +final class MVChatListener implements CoreListener { + private final MVCoreConfig config; + private final WorldManager worldManager; + private final MVPlayerListener playerListener; + + @Inject + MVChatListener( + MVCoreConfig config, + WorldManager worldManager, + MVPlayerListener playerListener) { + this.config = config; + this.worldManager = worldManager; + this.playerListener = playerListener; + } + + /** + * This handles a {@link AsyncPlayerChatEvent}. + * + * @param event The {@link AsyncPlayerChatEvent}. + */ + @EventHandler + public void asyncPlayerChat(AsyncPlayerChatEvent event) { + if (event.isCancelled()) { + return; + } + + // Check whether the Server is set to prefix the chat with the World name. + // If not we do nothing, if so we need to check if the World has an Alias. + if (!config.isEnablePrefixChat()) { + return; + } + + String world = playerListener.getPlayerWorld().get(event.getPlayer().getName()); + if (world == null) { + world = event.getPlayer().getWorld().getName(); + playerListener.getPlayerWorld().put(event.getPlayer().getName(), world); + } + + String prefix = this.worldManager.getLoadedWorld(world) + .map(mvworld -> mvworld.isHidden() ? "" : mvworld.getAlias()) + .getOrElse(""); + String chat = event.getFormat(); + + String prefixChatFormat = config.getPrefixChatFormat(); + prefixChatFormat = prefixChatFormat.replace("%world%", prefix).replace("%chat%", chat); + prefixChatFormat = ChatColor.translateAlternateColorCodes('&', prefixChatFormat); + + event.setFormat(prefixChatFormat); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVEntityListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVEntityListener.java new file mode 100644 index 000000000..0bb7d9faa --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVEntityListener.java @@ -0,0 +1,109 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.listeners; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +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.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.helpers.WorldPurger; + +/** + * Multiverse's Entity {@link Listener}. + */ +@Service +final class MVEntityListener implements CoreListener { + private final WorldManager worldManager; + private final WorldPurger worldPurger; + + @Inject + MVEntityListener( + @NotNull WorldManager worldManager, + @NotNull WorldPurger worldPurger) { + this.worldManager = worldManager; + this.worldPurger = worldPurger; + } + + /** + * This method is called when an entity's food level goes higher or lower. + * + * @param event The Event that was fired. + */ + @EventHandler + public void foodLevelChange(FoodLevelChangeEvent event) { + if (event.isCancelled()) { + return; + } + if (!(event.getEntity() instanceof Player player)) { + return; + } + + worldManager.getLoadedWorld(player.getWorld()) + .peek(world -> { + if (!world.getHunger() && event.getFoodLevel() < player.getFoodLevel()) { + event.setCancelled(true); + } + }); + } + + /** + * This method is called when an entity's health goes up or down. + * + * @param event The Event that was fired. + */ + @EventHandler + public void entityRegainHealth(EntityRegainHealthEvent event) { + if (event.isCancelled() || event.getRegainReason() != RegainReason.REGEN) { + return; + } + + worldManager.getLoadedWorld(event.getEntity().getWorld()) + .peek(world -> { + if (!world.getAutoHeal()) { + event.setCancelled(true); + } + }); + } + + /** + * Handle Animal/Monster Spawn settings, seems like a more concrete method than using CraftBukkit. + * + * @param event The event. + */ + @EventHandler + public void creatureSpawn(CreatureSpawnEvent event) { + if (event.isCancelled()) { + return; + } + + // Check to see if the Creature is spawned by a plugin, we don't want to prevent this behaviour. + if (event.getSpawnReason() == SpawnReason.CUSTOM + || event.getSpawnReason() == SpawnReason.SPAWNER_EGG + || event.getSpawnReason() == SpawnReason.BREEDING) { + return; + } + + worldManager.getLoadedWorld(event.getEntity().getWorld()) + .peek(world -> { + if (this.worldPurger.shouldWeKillThisCreature(world, event.getEntity())) { + Logging.finer("Cancelling Creature Spawn Event for: " + event.getEntity()); + event.setCancelled(true); + } + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVLocalesListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVLocalesListener.java new file mode 100644 index 000000000..7e4091e11 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVLocalesListener.java @@ -0,0 +1,43 @@ +package org.mvplugins.multiverse.core.listeners; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLocaleChangeEvent; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; + +import java.util.Locale; + +@Service +final class MVLocalesListener implements CoreListener { + + private final MVCommandManager commandManager; + + @Inject + MVLocalesListener(@NotNull MVCommandManager commandManager) { + this.commandManager = commandManager; + } + + @EventHandler + void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + Logging.finer(player.getName() + " joined with locale " + player.getLocale()); + commandManager.setPlayerLocale(player, serializeLocale(player.getLocale())); + } + + @EventHandler + void onLocaleChange(PlayerLocaleChangeEvent event) { + Player player = event.getPlayer(); + Logging.finer(player.getName() + " changed locale from " + player.getLocale() + " to " + event.getLocale()); + commandManager.setPlayerLocale(player, serializeLocale(event.getLocale())); + } + + private Locale serializeLocale(String locale) { + String[] split = locale.split("_"); + return split.length > 1 ? new Locale(split[0], split[1]) : new Locale(split[0]); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java new file mode 100644 index 000000000..6cece4c76 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java @@ -0,0 +1,380 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.listeners; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.plugin.Plugin; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.event.MVRespawnEvent; +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.BlockSafety; +import org.mvplugins.multiverse.core.teleportation.TeleportQueue; +import org.mvplugins.multiverse.core.utils.result.ResultChain; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.entrycheck.EntryFeeResult; +import org.mvplugins.multiverse.core.world.entrycheck.WorldEntryCheckerProvider; +import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; + +/** + * Multiverse's Listener for players. + */ +@Service +final class MVPlayerListener implements CoreListener { + private final Plugin plugin; + private final MVCoreConfig config; + private final Provider worldManagerProvider; + private final BlockSafety blockSafety; + private final AsyncSafetyTeleporter safetyTeleporter; + private final Server server; + private final TeleportQueue teleportQueue; + private final MVEconomist economist; + private final WorldEntryCheckerProvider worldEntryCheckerProvider; + private final Provider commandManagerProvider; + private final DestinationsProvider destinationsProvider; + private final EnforcementHandler enforcementHandler; + + private final Map playerWorld = new ConcurrentHashMap(); + + @Inject + MVPlayerListener( + MultiverseCore plugin, + MVCoreConfig config, + Provider worldManagerProvider, + BlockSafety blockSafety, + AsyncSafetyTeleporter safetyTeleporter, + Server server, + TeleportQueue teleportQueue, + MVEconomist economist, + WorldEntryCheckerProvider worldEntryCheckerProvider, + Provider commandManagerProvider, + DestinationsProvider destinationsProvider, + EnforcementHandler enforcementHandler) { + this.plugin = plugin; + this.config = config; + this.worldManagerProvider = worldManagerProvider; + this.blockSafety = blockSafety; + this.safetyTeleporter = safetyTeleporter; + this.server = server; + this.teleportQueue = teleportQueue; + this.economist = economist; + this.worldEntryCheckerProvider = worldEntryCheckerProvider; + this.commandManagerProvider = commandManagerProvider; + this.destinationsProvider = destinationsProvider; + this.enforcementHandler = enforcementHandler; + } + + private WorldManager getWorldManager() { + return worldManagerProvider.get(); + } + + private MVCommandManager getCommandManager() { + return commandManagerProvider.get(); + } + + /** + * Gets the map of player and the world name they are in. + * + * @return the playerWorld-map + */ + public Map getPlayerWorld() { + return playerWorld; + } + + /** + * This method is called when a player respawns. + * + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.LOW) + public void playerRespawn(PlayerRespawnEvent event) { + Player player = event.getPlayer(); + getWorldManager().getLoadedWorld(player.getWorld()) + .onEmpty(() -> Logging.fine("Player '%s' is in a world that is not managed by Multiverse.", player.getName())) + .filter(mvWorld -> { + if (mvWorld.getBedRespawn() && event.isBedSpawn()) { + Logging.fine("Spawning %s at their bed.", player.getName()); + return false; + } + if (mvWorld.getAnchorRespawn() && event.isAnchorSpawn()) { + Logging.fine("Spawning %s at their anchor.", player.getName()); + return false; + } + if (!config.getDefaultRespawnWithinSameWorld() && mvWorld.getRespawnWorldName().isEmpty()) { + Logging.fine("Not overriding respawn location for player '%s' as " + + "default-respawn-within-same-world is disabled and no respawn-world is set.", player.getName()); + return false; + } + return true; + }) + .flatMap(mvWorld -> getMostAccurateRespawnLocation(player, mvWorld, event.getRespawnLocation())) + .peek(newRespawnLocation -> { + MVRespawnEvent respawnEvent = new MVRespawnEvent(newRespawnLocation, event.getPlayer()); + this.server.getPluginManager().callEvent(respawnEvent); + if (respawnEvent.isCancelled()) { + Logging.fine("Player '%s' cancelled their respawn event.", player.getName()); + return; + } + Logging.fine("Overriding respawn location for player '%s' to '%s'.", player.getName(), respawnEvent.getRespawnLocation()); + event.setRespawnLocation(respawnEvent.getRespawnLocation()); + }); + } + + private Option getMostAccurateRespawnLocation(Player player, MultiverseWorld mvWorld, Location defaultRespawnLocation) { + return Option.of(mvWorld.getRespawnWorldName().isEmpty() + ? player.getWorld() + : server.getWorld(mvWorld.getRespawnWorldName())) + .onEmpty(() -> Logging.warning("World '%s' has respawn-world property of '%s' that does not exist!", + player.getWorld().getName(), mvWorld.getRespawnWorldName())) + .flatMap(newRespawnWorld -> { + if (!config.getEnforceRespawnAtWorldSpawn() && newRespawnWorld.equals(defaultRespawnLocation.getWorld())) { + Logging.fine("Respawn location is within same world as respawn-world, not overriding."); + return Option.none(); + } + return getWorldManager() + .getLoadedWorld(newRespawnWorld) + .map(newMVRespawnWorld -> (Location) newMVRespawnWorld.getSpawnLocation()) + .orElse(() -> Option.of(newRespawnWorld.getSpawnLocation())); + }); + } + + @EventHandler + void playerSpawnLocation(PlayerSpawnLocationEvent event) { + Player player = event.getPlayer(); + MultiverseWorld world = getWorldManager().getLoadedWorld(player.getWorld()).getOrNull(); + if (world == null) { + Logging.finer("Player joined in a world that is not managed by Multiverse."); + return; + } + if (!player.hasPlayedBefore()) { + handleFirstSpawn(event); + } else { + handleJoinLocation(event); + } + this.handleGameModeAndFlight(player, event.getSpawnLocation().getWorld()); + } + + private void handleFirstSpawn(PlayerSpawnLocationEvent event) { + if (!config.getFirstSpawnOverride()) { + Logging.finer("FirstSpawnOverride is disabled"); + // User has disabled the feature in config + return; + } + Logging.fine("Moving NEW player to(firstspawnoverride): %s", config.getFirstSpawnLocation()); + destinationsProvider.parseDestination(config.getFirstSpawnLocation()) + .flatMap(destination -> destination.getLocation(event.getPlayer())) + .peek(event::setSpawnLocation) + .onEmpty(() -> Logging.warning("The destination in FirstSpawnLocation in config is invalid")); + } + + private void handleJoinLocation(PlayerSpawnLocationEvent event) { + if (!config.getEnableJoinDestination()) { + Logging.finer("JoinDestination is disabled"); + // User has disabled the feature in config + return; + } + if (config.getJoinDestination().isBlank()) { + Logging.warning("Joindestination is enabled but no destination has been specified in config!"); + return; + } + Logging.finer("JoinDestination is " + config.getJoinDestination()); + destinationsProvider.parseDestination(config.getJoinDestination()) + .flatMap(destination -> destination.getLocation(event.getPlayer())) + .peek(event::setSpawnLocation) + .onEmpty(() -> Logging.warning("The destination in JoinDestination in config is invalid")); + } + + /** + * This method is called when a player changes worlds. + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.MONITOR) + public void playerChangedWorld(PlayerChangedWorldEvent event) { + // Permissions now determine whether or not to handle a gamemode. + this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld()); + playerWorld.put(event.getPlayer().getName(), event.getPlayer().getWorld().getName()); + } + + /** + * This method is called when a player teleports anywhere. + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.HIGHEST) + public void playerTeleport(PlayerTeleportEvent event) { + Logging.finer("Got teleport event for player '" + + event.getPlayer().getName() + "' with cause '" + event.getCause() + "'"); + if (event.isCancelled()) { + return; + } + Player teleportee = event.getPlayer(); + CommandSender teleporter = null; + Option teleporterName = teleportQueue.popFromQueue(teleportee.getName()); + if (teleporterName.isDefined()) { + if (teleporterName.equals("CONSOLE")) { + Logging.finer("We know the teleporter is the console! Magical!"); + teleporter = this.server.getConsoleSender(); + } else { + teleporter = this.server.getPlayerExact(teleporterName.get()); + } + if (teleporter != null) { + Logging.finer("Inferred sender '" + teleporter + "' from name '" + + teleporterName + "', fetched from name '" + teleportee.getName() + "'"); + } + } + + if (teleporter == null) { + if (config.getTeleportIntercept()) { + teleporter = teleportee; + } else { + Logging.finer("Teleport for %s was not initiated by multiverse and " + + "teleport intercept is disabled. Ignoring...", teleportee.getName()); + return; + } + } + + MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld().getName()).getOrNull(); + LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld().getName()).getOrNull(); + if (toWorld == null) { + Logging.fine("Player '" + teleportee.getName() + "' is teleporting to world '" + + event.getTo().getWorld().getName() + "' which is not managed by Multiverse-Core. No further " + + "actions will be taken by Multiverse-Core."); + return; + } + if (event.getFrom().getWorld().equals(event.getTo().getWorld())) { + // The player is Teleporting to the same world. + Logging.finer("Player '" + teleportee.getName() + "' is teleporting to the same world."); + this.stateSuccess(teleportee.getName(), toWorld.getName()); + return; + } + + CommandSender finalTeleporter = teleporter; + ResultChain entryResult = worldEntryCheckerProvider.forSender(finalTeleporter).canEnterWorld(fromWorld, toWorld) + .onSuccessReason(EntryFeeResult.Success.class, reason -> { + if (reason == EntryFeeResult.Success.ENOUGH_MONEY) { + economist.payEntryFee((Player) finalTeleporter, toWorld); + // Send payment receipt + } + }) + .onFailure(results -> { + event.setCancelled(true); + getCommandManager().getCommandIssuer(finalTeleporter).sendError(results.getLastResultMessage()); + }); + + Logging.fine("Teleport result: %s", entryResult); + } + + private void stateSuccess(String playerName, String worldName) { + Logging.fine("MV-Core is allowing Player '" + playerName + + "' to go to '" + worldName + "'."); + } + + /** + * This method is called to adjust the portal location to the actual portal location (and not + * right outside of it. + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.LOWEST) + public void playerPortalCheck(PlayerPortalEvent event) { + if (event.isCancelled() || event.getFrom() == null) { + return; + } + + // REMEMBER! getTo MAY be NULL HERE!!! + // If the player was actually outside of the portal, adjust the from location + if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() != Material.NETHER_PORTAL) { + Location newloc = blockSafety.findPortalBlockNextTo(event.getFrom()); + if (newloc != null) { + event.setFrom(newloc); + } + } + // Wait for the adjust, then return! + if (event.getTo() == null) { + return; + } + } + + /** + * This method is called when a player actually portals via a vanilla style portal. + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.HIGH) + public void playerPortal(PlayerPortalEvent event) { + if (event.isCancelled() || (event.getFrom() == null)) { + return; + } + // The adjust should have happened much earlier. + if (event.getTo() == null) { + return; + } + if (config.isUsingCustomPortalSearch()) { + event.setSearchRadius(config.getCustomPortalSearchRadius()); + } + + MultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld().getName()).getOrNull(); + LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld().getName()).getOrNull(); + if (event.getFrom().getWorld().equals(event.getTo().getWorld())) { + // The player is Portaling to the same world. + Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world."); + return; + } + + ResultChain entryResult = worldEntryCheckerProvider.forSender(event.getPlayer()).canEnterWorld(fromWorld, toWorld) + .onFailure(results -> { + event.setCancelled(true); + getCommandManager().getCommandIssuer(event.getPlayer()).sendError(results.getLastResultMessage()); + }); + + Logging.fine("Teleport result: %s", entryResult); + } + + /** + * Handles the gamemode for the specified {@link Player}. + * + * @param player The {@link Player}. + * @param world The {@link World} the player is supposed to be in. + */ + private void handleGameModeAndFlight(final Player player, World world) { + // We perform this task one tick later to MAKE SURE that the player actually reaches the + // destination world, otherwise we'd be changing the player mode if they havent moved anywhere. + this.server.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> { + if (!player.isOnline() || !player.getWorld().equals(world)) { + return; + } + Logging.finer("Handling gamemode and flight for player %s in world '%s'", player.getName(), world.getName()); + enforcementHandler.handleFlightEnforcement(player); + enforcementHandler.handleGameModeEnforcement(player); + }, 1L); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVPortalListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPortalListener.java new file mode 100644 index 000000000..c016c0f03 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVPortalListener.java @@ -0,0 +1,135 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2012. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.listeners; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.Material; +import org.bukkit.PortalType; +import org.bukkit.block.BlockState; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.Action; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.world.PortalCreateEvent; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.world.WorldManager; + +import static org.bukkit.PortalType.CUSTOM; + +/** + * A custom listener for portal related events. + */ +@Service +final class MVPortalListener implements CoreListener { + + private final MVCoreConfig config; + private final WorldManager worldManager; + + @Inject + MVPortalListener(@NotNull MVCoreConfig config, @NotNull WorldManager worldManager) { + this.config = config; + this.worldManager = worldManager; + } + + /** + * This is called when a portal is formed. + * + * @param event The event where a portal was created or formed due to a world link + */ + @EventHandler(ignoreCancelled = true) + public void portalCreate(PortalCreateEvent event) { + Logging.fine("Attempting to create portal at '%s' with reason: %s", + event.getWorld().getName(), event.getReason()); + + this.worldManager.getLoadedWorld(event.getWorld()).peek(world -> { + PortalType targetType = getPortalType(event); + if (targetType == PortalType.CUSTOM) { + return; + } + if (!world.getPortalForm().isPortalAllowed(targetType)) { + Logging.fine("Cancelling creation of %s portal because portalForm disallows.", targetType); + event.setCancelled(true); + } + }).onEmpty(() -> { + Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PortalCreateEvent.", + event.getWorld().getName()); + }); + } + + private PortalType getPortalType(PortalCreateEvent event) { + return switch (event.getReason()) { + case FIRE -> { + // Ensure portal by flint and steel actually creates nether + for (BlockState block : event.getBlocks()) { + if (block.getType() == Material.NETHER_PORTAL) { + yield PortalType.NETHER; + } + } + yield CUSTOM; + } + case NETHER_PAIR -> { + yield PortalType.NETHER; + } + case END_PLATFORM -> { + yield PortalType.ENDER; + } + default -> { + Logging.fine("Portal created is not NETHER or ENDER type. Ignoring..."); + yield CUSTOM; + } + }; + } + + /** + * This method will prevent ender portals from being created in worlds where they are not allowed due to portalForm. + * + * @param event The player interact event. + */ + @EventHandler(ignoreCancelled = true) + public void playerInteract(PlayerInteractEvent event) { + if (isCreateEndPortalInteraction(event)) { + return; + } + + this.worldManager.getLoadedWorld(event.getPlayer().getWorld()).peek(world -> { + if (!world.getPortalForm().isPortalAllowed(PortalType.ENDER)) { + Logging.fine("Cancelling creation of ENDER portal because portalForm disallows."); + event.setCancelled(true); + } + }).onEmpty(() -> { + Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PlayerInteractEvent.", + event.getPlayer().getWorld().getName()); + }); + } + + private boolean isCreateEndPortalInteraction(PlayerInteractEvent event) { + return event.getAction() != Action.RIGHT_CLICK_BLOCK + || event.getClickedBlock() == null + || event.getClickedBlock().getType() != Material.END_PORTAL_FRAME + || event.getItem() == null + || event.getItem().getType() != Material.ENDER_EYE; + } + + /** + * 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 (config.isUsingCustomPortalSearch()) { + event.setSearchRadius(config.getCustomPortalSearchRadius()); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVWeatherListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVWeatherListener.java new file mode 100644 index 000000000..fd43b01b9 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVWeatherListener.java @@ -0,0 +1,68 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.listeners; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.event.EventHandler; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.world.WorldManager; + +/** + * Multiverse's Weather Listener. + */ +@Service +final class MVWeatherListener implements CoreListener { + + private final WorldManager worldManager; + + @Inject + MVWeatherListener(WorldManager worldManager) { + this.worldManager = worldManager; + } + + /** + * This method is called when the weather changes. + * + * @param event The Event that was fired. + */ + @EventHandler + public void weatherChange(WeatherChangeEvent event) { + if (event.isCancelled() || !event.toWeatherState()) { + return; + } + worldManager.getLoadedWorld(event.getWorld()) + .peek(world -> { + if (!world.getAllowWeather()) { + Logging.fine("Cancelling weather for %s as getAllowWeather is false", world.getName()); + event.setCancelled(true); + } + }); + } + + /** + * This method is called when a big storm is going to start. + * + * @param event The Event that was fired. + */ + @EventHandler + public void thunderChange(ThunderChangeEvent event) { + if (event.isCancelled() || !event.toThunderState()) { + return; + } + worldManager.getLoadedWorld(event.getWorld()) + .peek(world -> { + if (!world.getAllowWeather()) { + Logging.fine("Cancelling thunder for %s as getAllowWeather is false", world.getName()); + event.setCancelled(true); + } + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/MVWorldListener.java b/src/main/java/org/mvplugins/multiverse/core/listeners/MVWorldListener.java new file mode 100644 index 000000000..dbfae8d44 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/MVWorldListener.java @@ -0,0 +1,71 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.listeners; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions; +import org.mvplugins.multiverse.core.world.reasons.LoadFailureReason; +import org.mvplugins.multiverse.core.world.reasons.UnloadFailureReason; + +/** + * Multiverse's World Listener. + */ +@Service +final class MVWorldListener implements CoreListener { + + private final WorldManager worldManager; + + @Inject + MVWorldListener(WorldManager worldManager) { + this.worldManager = worldManager; + } + + /** + * This method is called when Bukkit fires off a WorldUnloadEvent. + * + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.MONITOR) + public void worldUnload(WorldUnloadEvent event) { + if (event.isCancelled()) { + return; + } + worldManager.getLoadedWorld(event.getWorld().getName()) + .peek(world -> worldManager.unloadWorld(UnloadWorldOptions.world(world)).onFailure(failure -> { + if (failure.getFailureReason() != UnloadFailureReason.WORLD_ALREADY_UNLOADING) { + Logging.severe("Failed to unload world: " + failure); + } + })); + } + + /** + * This method is called when Bukkit fires off a WorldLoadEvent. + * + * @param event The Event that was fired. + */ + @EventHandler(priority = EventPriority.MONITOR) + public void worldLoad(WorldLoadEvent event) { + worldManager.getUnloadedWorld(event.getWorld().getName()) + .peek(world -> { + Logging.fine("Loading world: " + world.getName()); + worldManager.loadWorld(world).onFailure(failure -> { + if (failure.getFailureReason() != LoadFailureReason.WORLD_ALREADY_LOADING) { + Logging.severe("Failed to load world: " + failure); + } + }); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/listeners/package-info.java b/src/main/java/org/mvplugins/multiverse/core/listeners/package-info.java new file mode 100644 index 000000000..d3e5e8993 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/listeners/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all listeners. + */ +package org.mvplugins.multiverse.core.listeners; diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/FileResClassLoader.java b/src/main/java/org/mvplugins/multiverse/core/locale/FileResClassLoader.java new file mode 100644 index 000000000..bff9e9e8c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/FileResClassLoader.java @@ -0,0 +1,59 @@ +package org.mvplugins.multiverse.core.locale; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.bukkit.plugin.Plugin; + +/** + * A class loader that loads resources from the plugin's locales folder. + */ +final class FileResClassLoader extends ClassLoader { + + private final transient File targetFolder; + + /** + * Creates a new FileResClassLoader. + * + * @param plugin The plugin to load resources from. + * @param subFolder The subfolder to load resources from. + */ + FileResClassLoader(final Plugin plugin, final String subFolder) { + super(plugin.getClass().getClassLoader()); + this.targetFolder = new File(plugin.getDataFolder(), subFolder); + } + + /** + * {@inheritDoc} + */ + @Override + public URL getResource(final String string) { + final File file = new File(targetFolder, string); + if (file.exists()) { + try { + return file.toURI().toURL(); + } catch (final MalformedURLException ignored) { + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public InputStream getResourceAsStream(final String string) { + final File file = new File(targetFolder, string); + if (file.exists()) { + try { + return new FileInputStream(file); + } catch (final FileNotFoundException ignored) { + } + } + return null; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/MVCorei18n.java b/src/main/java/org/mvplugins/multiverse/core/locale/MVCorei18n.java new file mode 100644 index 000000000..823046159 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/MVCorei18n.java @@ -0,0 +1,223 @@ +package org.mvplugins.multiverse.core.locale; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; + +/** + * All the translation keys for the core plugin + */ +public enum MVCorei18n implements MessageKeyProvider { + // BEGIN CHECKSTYLE-SUPPRESSION: Javadoc + + // config status + CONFIG_SAVE_FAILED, + CONFIG_NODE_NOTFOUND, + + // check command + CHECK_HASPERMISSION, + CHECK_NOPERMISSION, + CHECK_LOCATION, + + // clone command + CLONE_CLONING, + CLONE_SUCCESS, + + // Coordinates command + COORDINATES_INFO_TITLE, + COORDINATES_INFO_WORLD, + COORDINATES_INFO_ALIAS, + COORDINATES_INFO_WORLDSCALE, + COORDINATES_INFO_COORDINATES, + COORDINATES_INFO_DIRECTION, + + // create command + CREATE_PROPERTIES, + CREATE_PROPERTIES_ENVIRONMENT, + CREATE_PROPERTIES_SEED, + CREATE_PROPERTIES_WORLDTYPE, + CREATE_PROPERTIES_ADJUSTSPAWN, + CREATE_PROPERTIES_BIOME, + CREATE_PROPERTIES_GENERATOR, + CREATE_PROPERTIES_STRUCTURES, + CREATE_LOADING, + CREATE_SUCCESS, + + // delete command + DELETE_DELETING, + DELETE_PROMPT, + DELETE_SUCCESS, + + // Dumps command + DUMPS_DESCRIPTION, + DUMPS_URL_LIST, + + // gamerule set command + GAMERULE_SET_FAILED, + GAMERULE_SET_SUCCESS_SINGLE, + GAMERULE_SET_SUCCESS_MULTIPLE, + + // gamerule reset command + GAMERULE_RESET_FAILED, + GAMERULE_RESET_SUCCESS_SINGLE, + GAMERULE_RESET_SUCCESS_MULTIPLE, + + // gamerule list command + GAMERULE_LIST_DESCRIPTION, + GAMERULE_LIST_DESCRIPTION_PAGE, + GAMERULE_LIST_DESCRIPTION_WORLD, + GAMERULE_LIST_TITLE, + + // Generators command + GENERATORS_DESCRIPTION, + GENERATORS_DESCRIPTION_FLAGS, + GENERATORS_EMPTY, + + // import command + IMPORT_IMPORTING, + IMPORT_SUCCESS, + + // load command + LOAD_LOADING, + LOAD_SUCCESS, + + // regen command + REGEN_REGENERATING, + REGEN_PROMPT, + REGEN_SUCCESS, + + // reload command + RELOAD_RELOADING, + RELOAD_SUCCESS, + + // remove command + REMOVE_SUCCESS, + + // root MV command + ROOT_TITLE, + ROOT_HELP, + + // spawn tp command + SPAWN_DESCRIPTION, + SPAWN_PLAYER_DESCRIPTION, + SPAWN_SUCCESS, + SPAWN_FAILED, + SPAWN_CONSOLENAME, + SPAWN_YOU, + + // teleport command + TELEPORT_TOOMANYPLAYERS, + TELEPORT_SUCCESS, + TELEPORT_FAILED, + + // unload command + UNLOAD_UNLOADING, + UNLOAD_SUCCESS, + + // who command + WHO_DESCRIPTION, + WHO_ALL_DESCRIPTION, + WHO_WORLD_DESCRIPTION, + WHO_FLAGS_DESCRIPTION, + WHO_EMPTY, + + // version command + VERSION_MV, + VERSION_AUTHORS, + VERSION_SECRETCODE, + + // debug command + DEBUG_INFO_OFF, + DEBUG_INFO_ON, + + // commands error + COMMANDS_ERROR_PLAYERSONLY, + COMMANDS_ERROR_MULTIVERSEWORLDONLY, + + // entry check + ENTRYCHECK_BLACKLISTED, + ENTRYCHECK_NOTENOUGHMONEY, + ENTRYCHECK_CANNOTPAYENTRYFEE, + ENTRYCHECK_EXCEEDPLAYERLIMIT, + ENTRYCHECK_NOWORLDACCESS, + + // teleport failure reasons + TELEPORTFAILUREREASON_NULL_DESTINATION, + TELEPORTFAILUREREASON_NULL_LOCATION, + TELEPORTFAILUREREASON_UNSAFE_LOCATION, + TELEPORTFAILUREREASON_TELEPORT_FAILED, + TELEPORTFAILUREREASON_TELEPORT_FAILED_EXCEPTION, + TELEPORTFAILUREREASON_EVENT_CANCELLED, + + // world manager result + CLONEWORLD_INVALIDWORLDNAME, + CLONEWORLD_WORLDEXISTFOLDER, + CLONEWORLD_WORLDEXISTUNLOADED, + CLONEWORLD_WORLDEXISTLOADED, + CLONEWORLD_COPYFAILED, + + CREATEWORLD_INVALIDWORLDNAME, + CREATEWORLD_WORLDEXISTFOLDER, + CREATEWORLD_WORLDEXISTUNLOADED, + CREATEWORLD_WORLDEXISTLOADED, + CREATEWORLD_BUKKITCREATIONFAILED, + + DELETEWORLD_WORLDNONEXISTENT, + DELETEWORLD_LOADFAILED, + DELETEWORLD_WORLDFOLDERNOTFOUND, + DELETEWORLD_FAILEDTODELETEFOLDER, + + IMPORTWORLD_INVALIDWORLDNAME, + IMPORTWORLD_WORLDFOLDERINVALID, + IMPORTWORLD_WORLDEXISTUNLOADED, + IMPORTWORLD_WORLDEXISTLOADED, + IMPORTWORLD_BUKKITCREATIONFAILED, + + LOADWORLD_WORLDALREADYLOADING, + LOADWORLD_WORLDNONEXISTENT, + LOADWORLD_WORLDEXISTFOLDER, + LOADWORLD_WORLDEXISTLOADED, + LOADWORLD_BUKKITCREATIONFAILED, + + REMOVEWORLD_WORLDNONEXISTENT, + + UNLOADWORLD_WORLDALREADYUNLOADING, + UNLOADWORLD_WORLDNONEXISTENT, + UNLOADWORLD_WORLDUNLOADED, + UNLOADWORLD_BUKKITUNLOADFAILED, + + // generic + GENERIC_SUCCESS, + GENERIC_FAILURE, + GENERIC_ERROR, + GENERIC_NULL, + GENERIC_YOU, + ; + + // END CHECKSTYLE-SUPPRESSION: Javadoc + + private final MessageKey key = MessageKey.of("mv-core." + this.name().replace('_', '.').toLowerCase()); + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return this.key; + } + + /** + * Creates a message with non-localized message fallback and replacements + * @param nonLocalizedMessage The non-localized message + * @param replacements The replacements + * + * @return A new localizable Message + */ + @NotNull + public Message bundle(@NotNull String nonLocalizedMessage, @NotNull MessageReplacement... replacements) { + return Message.of(this, nonLocalizedMessage, replacements); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/PluginLocales.java b/src/main/java/org/mvplugins/multiverse/core/locale/PluginLocales.java new file mode 100644 index 000000000..447bb5cc2 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/PluginLocales.java @@ -0,0 +1,45 @@ +package org.mvplugins.multiverse.core.locale; + +import co.aikar.commands.BukkitLocales; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.commandtools.MVCommandManager; + +/** + * Locale manager with additional methods for loading locales from plugin's locales folder. + */ +public final class PluginLocales extends BukkitLocales { + + private static final String DEFAULT_LOCALE_FOLDER_PATH = "locales"; + + /** + * Creates a new instance of {@link PluginLocales}. + * + * @param manager The command manager. + */ + public PluginLocales(MVCommandManager manager) { + super(manager); + } + + /** + * Adds a {@link FileResClassLoader} to the list of class loaders to load locales data from. + * + * @param plugin The plugin. + * @return True if the class loader was added successfully. + */ + public boolean addFileResClassLoader(@NotNull Plugin plugin) { + return this.addBundleClassLoader(new FileResClassLoader(plugin, DEFAULT_LOCALE_FOLDER_PATH)); + } + + /** + * Adds a {@link FileResClassLoader} to the list of class loaders to load locales data from. + * + * @param plugin The plugin. + * @param localesFolderPath The path to the folder containing the locales. + * @return True if the class loader was added successfully. + */ + public boolean addFileResClassLoader(@NotNull Plugin plugin, @NotNull String localesFolderPath) { + return this.addBundleClassLoader(new FileResClassLoader(plugin, localesFolderPath)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/message/LocalizedMessage.java b/src/main/java/org/mvplugins/multiverse/core/locale/message/LocalizedMessage.java new file mode 100644 index 000000000..b1b6ee882 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/message/LocalizedMessage.java @@ -0,0 +1,60 @@ +package org.mvplugins.multiverse.core.locale.message; + +import java.util.Objects; + +import co.aikar.commands.ACFUtil; +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.Locales; +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class LocalizedMessage extends Message implements MessageKeyProvider { + + private final @NotNull MessageKeyProvider messageKeyProvider; + + LocalizedMessage( + @NotNull MessageKeyProvider messageKeyProvider, + @NotNull String message, + @NotNull MessageReplacement... replacements) { + super(message, replacements); + this.messageKeyProvider = messageKeyProvider; + } + + @Override + public MessageKey getMessageKey() { + return messageKeyProvider.getMessageKey(); + } + + @Override + public @NotNull String[] getReplacements(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) { + return toReplacementsArray(locales, commandIssuer, replacements); + } + + @Override + public @NotNull String formatted(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) { + Objects.requireNonNull(locales, "locales must not be null"); + + String[] parsedReplacements = getReplacements(locales, commandIssuer); + if (parsedReplacements.length == 0) { + return locales.getMessage(commandIssuer, getMessageKey()); + } + return ACFUtil.replaceStrings(locales.getMessage(commandIssuer, getMessageKey()), parsedReplacements); + } + + private static String[] toReplacementsArray( + @NotNull Locales locales, + @Nullable CommandIssuer commandIssuer, + @NotNull MessageReplacement... replacements) { + String[] replacementsArray = new String[replacements.length * 2]; + int i = 0; + for (MessageReplacement replacement : replacements) { + replacementsArray[i++] = replacement.getKey(); + replacementsArray[i++] = replacement.getReplacement().fold( + str -> str, + message -> message.formatted(locales, commandIssuer)); + } + return replacementsArray; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/message/Message.java b/src/main/java/org/mvplugins/multiverse/core/locale/message/Message.java new file mode 100644 index 000000000..ae70f8ef4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/message/Message.java @@ -0,0 +1,168 @@ +package org.mvplugins.multiverse.core.locale.message; + +import java.util.Objects; + +import co.aikar.commands.ACFUtil; +import co.aikar.commands.CommandIssuer; +import co.aikar.commands.Locales; +import co.aikar.locales.MessageKeyProvider; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A message that can be formatted with replacements and localized. + */ +public sealed class Message permits LocalizedMessage { + + /** + * Creates a basic non-localized Message with the given message and replacements. + * + * @param message The message + * @param replacements The replacements + * @return A new Message + */ + @Contract(value = "_, _ -> new", pure = true) + public static Message of(@NotNull String message, @NotNull MessageReplacement... replacements) { + Objects.requireNonNull(message, "message must not be null"); + for (MessageReplacement replacement : replacements) { + Objects.requireNonNull(replacement, "replacements must not contain null"); + } + + return new Message(message, replacements); + } + + /** + * Creates a localized Message with the given message key provider and replacements. + *
+ * This message will extend {@link MessageKeyProvider} and delegate to the given message key provider. + * + * @param messageKeyProvider The message key provider + * @param replacements The replacements + * @return A new localizable Message + */ + @Contract(value = "_, _ -> new", pure = true) + public static Message of(@NotNull MessageKeyProvider messageKeyProvider, @NotNull MessageReplacement... replacements) { + return of(messageKeyProvider, "{error_key: %s}".formatted(messageKeyProvider.getMessageKey().getKey()), replacements); + } + + /** + * Creates a localized Message with the given message key provider, non-localized message and replacements. + *
+ * The non-localized message is required for conditions where it is not practical to provide a localized message. + *
+ * This message will extend {@link MessageKeyProvider} and delegate to the given message key provider. + * + * @param messageKeyProvider The message key provider + * @param nonLocalizedMessage The non-localized message + * @param replacements The replacements + * @return A new localizable Message + */ + @Contract(value = "_, _, _ -> new", pure = true) + public static Message of( + @NotNull MessageKeyProvider messageKeyProvider, + @NotNull String nonLocalizedMessage, + @NotNull MessageReplacement... replacements) { + Objects.requireNonNull(messageKeyProvider, "messageKeyProvider must not be null"); + Objects.requireNonNull(nonLocalizedMessage, "message must not be null"); + for (MessageReplacement replacement : replacements) { + Objects.requireNonNull(replacement, "replacements must not contain null"); + } + + return new LocalizedMessage(messageKeyProvider, nonLocalizedMessage, replacements); + } + + private final @NotNull String message; + protected final @NotNull MessageReplacement[] replacements; + + protected Message(@NotNull String message, @NotNull MessageReplacement... replacements) { + this.message = message; + this.replacements = replacements; + } + + /** + * Gets the replacements for this message. + *
+ * This array is guaranteed to be of even length and suitable for use with + * {@link ACFUtil#replaceStrings(String, String...)}. + * + * @return The replacements + */ + public @NotNull String[] getReplacements() { + return toReplacementsArray(replacements); + } + + /** + * Gets the replacements for this message with localization support. + *
+ * This array is guaranteed to be of even length and suitable for use with + * {@link ACFUtil#replaceStrings(String, String...)}. + * + * @param locales The MultiverseCore locales provider + * @param commandIssuer The command issuer the message is for, or null for the console (default locale) + * @return The replacements + */ + public @NotNull String[] getReplacements(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) { + return getReplacements(); + } + + /** + * Gets the raw, non-localized, non-replaced message. + * + * @return The raw message + */ + public @NotNull String raw() { + return message; + } + + /** + * Gets the formatted message. + *
+ * This is the raw, non-localized message with replacements applied. + * + * @return The formatted message + */ + public @NotNull String formatted() { + String[] parsedReplacements = getReplacements(); + if (parsedReplacements.length == 0) { + return raw(); + } + return ACFUtil.replaceStrings(message, parsedReplacements); + } + + /** + * Gets the formatted message from localization data. + *
+ * This is the localized message with replacements applied. The message is localized using the default locale. + * + * @param locales The MultiverseCore locales provider + * @return The formatted, localized message + */ + public @NotNull String formatted(@NotNull Locales locales) { + return formatted(locales, null); + } + + /** + * Gets the formatted message from localization data. + *
+ * This is the localized message with replacements applied. The message is localized using the locale of the given + * command issuer, if not null. + * + * @param locales The MultiverseCore locales provider + * @param commandIssuer The command issuer the message is for, or null for the console (default locale) + * @return The formatted, localized message + */ + public @NotNull String formatted(@NotNull Locales locales, @Nullable CommandIssuer commandIssuer) { + return formatted(); + } + + private static String[] toReplacementsArray(@NotNull MessageReplacement... replacements) { + String[] replacementsArray = new String[replacements.length * 2]; + int i = 0; + for (MessageReplacement replacement : replacements) { + replacementsArray[i++] = replacement.getKey(); + replacementsArray[i++] = replacement.getReplacement().fold(s -> s, Message::formatted); + } + return replacementsArray; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/message/MessageReplacement.java b/src/main/java/org/mvplugins/multiverse/core/locale/message/MessageReplacement.java new file mode 100644 index 000000000..340b8f961 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/message/MessageReplacement.java @@ -0,0 +1,132 @@ +package org.mvplugins.multiverse.core.locale.message; + +import io.vavr.control.Either; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Captures string replacements for {@link Message}s. + */ +public final class MessageReplacement { + + /** + * Creates a replacement key for the given key string. + * + * @param key The string to replace + * @return A new replacement key which can be used to create a replacement + */ + @Contract(value = "_ -> new", pure = true) + public static MessageReplacement.Key replace(@NotNull String key) { + return new MessageReplacement.Key(key); + } + + private final @NotNull String key; + private final @NotNull Either replacement; + + private MessageReplacement(@NotNull String key, @NotNull Message replacement) { + this.key = key; + this.replacement = Either.right(replacement); + } + + private MessageReplacement(@NotNull String key, @Nullable Object replacement) { + this.key = key; + this.replacement = Either.left(String.valueOf(replacement)); + } + + /** + * Gets the string to be replaced. + * + * @return The key + */ + public @NotNull String getKey() { + return key; + } + + /** + * Gets the replacement value. + * + * @return The replacement + */ + public @NotNull Either getReplacement() { + return replacement; + } + + /** + * A replacement key that maps to a value it can be replaced with. + * + */ + public static final class Key { + + private final @NotNull String key2; + + private Key(@NotNull String key) { + this.key2 = key; + } + + /** + * Creates a replacement for this key. + * + * @param replacement The replacement message + * @return A new message replacement + */ + @Contract(value = "_ -> new", pure = true) + public MessageReplacement with(@NotNull Message replacement) { + return new MessageReplacement(key2, replacement); + } + + /** + * Creates a replacement for this key. + * + * @param replacement The replacement value, if null it will be replaced with a string equal to "null" + * @return A new message replacement + */ + @Contract(value = "_ -> new", pure = true) + public MessageReplacement with(@Nullable Object replacement) { + return new MessageReplacement(key2, replacement); + } + } + + /** + * Predefined replacement keys for common replacements. + */ + public enum Replace { + // BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable + COUNT(replace("{count}")), + DESTINATION(replace("{destination}")), + GAMERULE(replace("{gamerule}")), + PLAYER(replace("{player}")), + REASON(replace("{reason}")), + VALUE(replace("{value}")), + WORLD(replace("{world}")),; + // END CHECKSTYLE-SUPPRESSION: JavadocVariable + + private final Key replaceKey; + + Replace(Key replaceKey) { + this.replaceKey = replaceKey; + } + + /** + * Creates a replacement for this key. + * + * @param replacement The replacement message + * @return A new message replacement + */ + @Contract(value = "_ -> new", pure = true) + public MessageReplacement with(@NotNull Message replacement) { + return replaceKey.with(replacement); + } + + /** + * Creates a replacement for this key. + * + * @param replacement The replacement value, if null it will be replaced with a string equal to "null" + * @return A new message replacement + */ + @Contract(value = "_ -> new", pure = true) + public MessageReplacement with(@Nullable Object replacement) { + return replaceKey.with(replacement); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/message/package-info.java b/src/main/java/org/mvplugins/multiverse/core/locale/message/package-info.java new file mode 100644 index 000000000..fc1dc8c65 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/message/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.locale.message; diff --git a/src/main/java/org/mvplugins/multiverse/core/locale/package-info.java b/src/main/java/org/mvplugins/multiverse/core/locale/package-info.java new file mode 100644 index 000000000..bed59d4b8 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/locale/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.locale; \ No newline at end of file diff --git a/src/main/java/com/onarandombox/MultiverseCore/package-info.java b/src/main/java/org/mvplugins/multiverse/core/package-info.java similarity index 66% rename from src/main/java/com/onarandombox/MultiverseCore/package-info.java rename to src/main/java/org/mvplugins/multiverse/core/package-info.java index ed08147bd..d5e0f4215 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/package-info.java +++ b/src/main/java/org/mvplugins/multiverse/core/package-info.java @@ -1,4 +1,4 @@ /** * The "master"-package, containing everything related to Multiverse-Core. */ -package com.onarandombox.MultiverseCore; +package org.mvplugins.multiverse.core; diff --git a/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissions.java b/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissions.java new file mode 100644 index 000000000..74a5dc073 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissions.java @@ -0,0 +1,104 @@ +package org.mvplugins.multiverse.core.permissions; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +import static org.mvplugins.multiverse.core.permissions.PermissionUtils.concatPermission; + +@Service +public final class CorePermissions { + /** + * Permission to access a world. + */ + static final String WORLD_ACCESS = "multiverse.access"; + + /** + * Permission to bypass the entry fee of a world. + */ + static final String WORLD_EXEMPT = "multiverse.exempt"; + + /** + * Permission to bypass the gamemode of a world. + */ + static final String GAMEMODE_BYPASS = "mv.bypass.gamemode"; + + /** + * Permission to bypass the player limit of a world. + */ + static final String PLAYERLIMIT_BYPASS = "mv.bypass.playerlimit"; + + /** + * Permission to teleport to a destination. + */ + static final String TELEPORT = "multiverse.teleport"; + + /** + * Permission to teleport to spawn in a world. + */ + static final String SPAWN = "multiverse.core.spawn"; + + private final PluginManager pluginManager; + + @Inject + CorePermissions(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + public Try addWorldPermissions(@NotNull MultiverseWorld world) { + return Try.run(() -> { + pluginManager.addPermission(new Permission( + concatPermission(WORLD_ACCESS, world.getName()), PermissionDefault.OP)); + pluginManager.addPermission(new Permission( + concatPermission(WORLD_EXEMPT, world.getName()), PermissionDefault.OP)); + pluginManager.addPermission(new Permission( + concatPermission(GAMEMODE_BYPASS, world.getName()), PermissionDefault.FALSE)); + pluginManager.addPermission(new Permission( + concatPermission(PLAYERLIMIT_BYPASS, world.getName()), PermissionDefault.FALSE)); + pluginManager.addPermission(new Permission( + concatPermission(SPAWN, world.getName()), PermissionDefault.OP)); + pluginManager.addPermission(new Permission( + concatPermission(SPAWN, "self", world.getName()), PermissionDefault.OP)); + pluginManager.addPermission(new Permission( + concatPermission(SPAWN, "other", world.getName()), PermissionDefault.OP)); + Logging.fine("Successfully registered permissions for world %s", world.getName()); + }); + } + + public Try removeWorldPermissions(@NotNull MultiverseWorld world) { + return Try.run(() -> { + pluginManager.removePermission(concatPermission(WORLD_ACCESS, world.getName())); + pluginManager.removePermission(concatPermission(WORLD_EXEMPT, world.getName())); + pluginManager.removePermission(concatPermission(GAMEMODE_BYPASS, world.getName())); + pluginManager.removePermission(concatPermission(PLAYERLIMIT_BYPASS, world.getName())); + pluginManager.removePermission(concatPermission(SPAWN, "self", world.getName())); + pluginManager.removePermission(concatPermission(SPAWN, "other", world.getName())); + Logging.fine("Successfully removed permissions for world %s", world.getName()); + }); + } + + public Try addDestinationPermissions(@NotNull Destination destination) { + return Try.run(() -> { + pluginManager.addPermission(new Permission( + concatPermission(TELEPORT, "self", destination.getIdentifier()), PermissionDefault.OP)); + pluginManager.addPermission(new Permission( + concatPermission(TELEPORT, "other", destination.getIdentifier()), PermissionDefault.OP)); + Logging.fine("Successfully registered permissions for destination %s", destination.getIdentifier()); + }); + } + + public Try removeDestinationPermissions(@NotNull Destination destination) { + return Try.run(() -> { + pluginManager.removePermission(concatPermission(TELEPORT, "self", destination.getIdentifier())); + pluginManager.removePermission(concatPermission(TELEPORT, "other", destination.getIdentifier())); + Logging.fine("Successfully removed permissions for destination %s", destination.getIdentifier()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissionsChecker.java b/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissionsChecker.java new file mode 100644 index 000000000..28615c293 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/permissions/CorePermissionsChecker.java @@ -0,0 +1,166 @@ +package org.mvplugins.multiverse.core.permissions; + +import jakarta.inject.Inject; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.destination.Destination; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.world.MultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +import static org.mvplugins.multiverse.core.permissions.PermissionUtils.concatPermission; +import static org.mvplugins.multiverse.core.permissions.PermissionUtils.hasPermission; + +@Service +public final class CorePermissionsChecker { + + private final MVCoreConfig config; + private final DestinationsProvider destinationsProvider; + private final WorldManager worldManager; + + @Inject + CorePermissionsChecker( + @NotNull MVCoreConfig config, + @NotNull DestinationsProvider destinationsProvider, + @NotNull WorldManager worldManager) { + this.config = config; + this.destinationsProvider = destinationsProvider; + this.worldManager = worldManager; + } + + public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) { + return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName())); + } + + public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) { + return hasPermission(sender, concatPermission(CorePermissions.WORLD_EXEMPT, world.getName())); + } + + public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) { + return hasPermission(sender, concatPermission(CorePermissions.PLAYERLIMIT_BYPASS, world.getName())); + } + + public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) { + return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName())); + } + + /** + * Checks if the teleporter has permission to teleport the teleportee to the world's spawnpoint. + * + * @param teleporter The teleporter. + * @param teleportee The teleportee. + * @param world The world. + * @return True if the teleporter has permission, false otherwise. + */ + public boolean checkSpawnPermission( + @NotNull CommandSender teleporter, + @NotNull Entity teleportee, + @NotNull MultiverseWorld world) { + // TODO: Config whether to use finer permission + return hasPermission(teleporter, concatPermission( + CorePermissions.SPAWN, teleportee.equals(teleporter) ? "self" : "other", world.getName())); + } + + /** + * Check if the issuer has any base spawn permission of `multiverse.core.spawn.self` or `multiverse.core.spawn.other` + * + * @param sender The sender that ran the command + * @return True if the sender has any base spawn permission. + */ + public boolean hasMinimumSpawnPermission(@NotNull CommandSender sender) { + if (sender instanceof Player player) { + return hasPermission(sender, concatPermission(CorePermissions.SPAWN, "self", player.getWorld().getName())) + || hasPermission(sender, concatPermission(CorePermissions.SPAWN, "other", player.getWorld().getName())); + } + return hasSpawnOtherPermission(sender); + } + + public boolean hasSpawnOtherPermission(@NotNull CommandSender sender) { + return worldManager.getLoadedWorlds().stream() + .anyMatch(world -> hasPermission(sender, + concatPermission(CorePermissions.SPAWN, "other", world.getName()))); + } + + public boolean hasDestinationPermission( + @NotNull CommandSender teleporter, + @NotNull CommandSender teleportee, + @NotNull Destination destination) { + if (teleportee.equals(teleporter)) { + return hasPermission(teleporter, concatPermission(CorePermissions.TELEPORT, + "self", destination.getIdentifier())); + } + return hasPermission(teleporter, concatPermission(CorePermissions.TELEPORT, + "other", destination.getIdentifier())); + } + + public boolean hasFinerDestinationPermission( + @NotNull CommandSender teleporter, + @NotNull CommandSender teleportee, + @NotNull Destination destination, + @NotNull String finerPermissionSuffix) { + if (!config.getUseFinerTeleportPermissions() || finerPermissionSuffix.isEmpty()) { + return true; + } + if (teleportee.equals(teleporter)) { + return hasPermission(teleporter, concatPermission(CorePermissions.TELEPORT, + "self", destination.getIdentifier(), finerPermissionSuffix)); + } + return hasPermission(teleporter, concatPermission(CorePermissions.TELEPORT, + "other", destination.getIdentifier(), finerPermissionSuffix)); + } + + /** + * Checks if the teleporter has permission to teleport the teleportee to the destination. + * + * @param teleporter The teleporter. + * @param teleportee The teleportee. + * @param destination The destination. + * @return True if the teleporter has permission, false otherwise. + */ + public boolean checkTeleportPermissions( + @NotNull CommandSender teleporter, + @NotNull Entity teleportee, + @NotNull DestinationInstance destination) { + if (!hasDestinationPermission(teleporter, teleportee, destination.getDestination())) { + return false; + } + return hasFinerDestinationPermission( + teleporter, teleportee, destination.getDestination(), destination.getFinerPermissionSuffix().getOrElse("")); + } + + /** + * Checks if the issuer has permission to teleport to at least one destination. + * + * @param sender The sender to check. + * @return True if the issuer has permission, false otherwise. + */ + public boolean hasAnyTeleportPermission(CommandSender sender) { + for (Destination destination : destinationsProvider.getDestinations()) { + String permission = concatPermission(CorePermissions.TELEPORT, "self", destination.getIdentifier()); + if (hasPermission(sender, permission)) { + return true; + } + permission = concatPermission(CorePermissions.TELEPORT, "other", destination.getIdentifier()); + if (hasPermission(sender, permission)) { + return true; + } + } + return false; + } + + public boolean hasTeleportOtherPermission(CommandSender sender) { + for (Destination destination : destinationsProvider.getDestinations()) { + String permission = concatPermission(CorePermissions.TELEPORT, "other", destination.getIdentifier()); + if (hasPermission(sender, permission)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/permissions/PermissionUtils.java b/src/main/java/org/mvplugins/multiverse/core/permissions/PermissionUtils.java new file mode 100644 index 000000000..97a707cbb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/permissions/PermissionUtils.java @@ -0,0 +1,53 @@ +package org.mvplugins.multiverse.core.permissions; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; + +public final class PermissionUtils { + + private static boolean debugPermissions = false; + + private PermissionUtils() { + // Prevent instantiation + } + + public static boolean isDebugPermissions() { + return debugPermissions; + } + + public static void setDebugPermissions(boolean debugPermissions) { + PermissionUtils.debugPermissions = debugPermissions; + } + + /** + * Joins permissions with a dot. + * + * @param permission The permission + * @param child The string(s) to join + * @return The newly joined permission node. + */ + public static String concatPermission(String permission, String... child) { + return permission + "." + String.join(".", child); + } + + /** + * Check and log if the sender has the permission. + * + * @param sender The sender + * @param permission The permission + * @return True if the sender has the permission, else false. + */ + public static boolean hasPermission(CommandSender sender, String permission) { + if (sender.hasPermission(permission)) { + if (debugPermissions && !(sender instanceof ConsoleCommandSender)) { + Logging.finer("Checking sender [%s] has permission [%s] : YES", sender.getName(), permission); + } + return true; + } + if (debugPermissions && !(sender instanceof ConsoleCommandSender)) { + Logging.finer("Checking sender [%s] has permission [%s] : NO", sender.getName(), permission); + } + return false; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/permissions/package-info.java b/src/main/java/org/mvplugins/multiverse/core/permissions/package-info.java new file mode 100644 index 000000000..e4a35c26a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/permissions/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.permissions; diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java new file mode 100644 index 000000000..5d7beaf3a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporter.java @@ -0,0 +1,61 @@ +package org.mvplugins.multiverse.core.teleportation; + +import io.vavr.control.Either; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.destination.DestinationInstance; + +/** + * Teleports entities safely and asynchronously. Provider for the {@link AsyncSafetyTeleporter}. + */ +@Service +public final class AsyncSafetyTeleporter { + private final BlockSafety blockSafety; + private final TeleportQueue teleportQueue; + private final PluginManager pluginManager; + + @Inject + AsyncSafetyTeleporter( + @NotNull BlockSafety blockSafety, + @NotNull TeleportQueue teleportQueue, + @NotNull PluginManager pluginManager) { + this.blockSafety = blockSafety; + this.teleportQueue = teleportQueue; + this.pluginManager = pluginManager; + } + + /** + * Sets the location to teleport to. + * + * @param location The location + * @return A new {@link AsyncSafetyTeleporterAction} to be chained + */ + public AsyncSafetyTeleporterAction to(@Nullable Location location) { + return new AsyncSafetyTeleporterAction( + blockSafety, + teleportQueue, + pluginManager, + Either.left(location) + ); + } + + /** + * Sets the destination to teleport to. + * + * @param destination The destination + * @return A new {@link AsyncSafetyTeleporterAction} to be chained + */ + public AsyncSafetyTeleporterAction to(@Nullable DestinationInstance destination) { + return new AsyncSafetyTeleporterAction( + blockSafety, + teleportQueue, + pluginManager, + Either.right(destination) + ); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java new file mode 100644 index 000000000..c1c42d7be --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/AsyncSafetyTeleporterAction.java @@ -0,0 +1,171 @@ +package org.mvplugins.multiverse.core.teleportation; + +import co.aikar.commands.BukkitCommandIssuer; +import com.dumptruckman.minecraft.util.Logging; +import io.papermc.lib.PaperLib; +import io.vavr.control.Either; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.event.MVTeleportDestinationEvent; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.utils.result.AsyncAttempt; +import org.mvplugins.multiverse.core.utils.result.Attempt; + +import java.util.List; + +/** + * Teleports one or more entity safely to a location. + */ +public final class AsyncSafetyTeleporterAction { + + private final BlockSafety blockSafety; + private final TeleportQueue teleportQueue; + private final PluginManager pluginManager; + + private final @NotNull Either> locationOrDestination; + private boolean checkSafety; + private @Nullable CommandSender teleporter = null; + + AsyncSafetyTeleporterAction( + @NotNull BlockSafety blockSafety, + @NotNull TeleportQueue teleportQueue, + @NotNull PluginManager pluginManager, + @NotNull Either> locationOrDestination) { + this.blockSafety = blockSafety; + this.teleportQueue = teleportQueue; + this.pluginManager = pluginManager; + this.locationOrDestination = locationOrDestination; + this.checkSafety = locationOrDestination.fold( + location -> true, + destination -> destination != null && destination.checkTeleportSafety() + ); + } + + /** + * Sets whether to check for safe location before teleport. + * + * @param checkSafety Whether to check for safe location + * @return A new {@link AsyncSafetyTeleporterAction} to be chained + */ + public AsyncSafetyTeleporterAction checkSafety(boolean checkSafety) { + this.checkSafety = checkSafety; + return this; + } + + /** + * Sets the teleporter. + * + * @param issuer The issuer + * @return A new {@link AsyncSafetyTeleporterAction} to be chained + */ + public AsyncSafetyTeleporterAction by(@NotNull BukkitCommandIssuer issuer) { + return by(issuer.getIssuer()); + } + + /** + * Sets the teleporter. + * + * @param teleporter The teleporter + * @return A new {@link AsyncSafetyTeleporterAction} to be chained + */ + public AsyncSafetyTeleporterAction by(@NotNull CommandSender teleporter) { + this.teleporter = teleporter; + return this; + } + + /** + * Teleport multiple entities + * + * @param teleportees The entities to teleport + * @param + * @return A list of async futures that represent the teleportation result of each entity + */ + public Async>> teleport(@NotNull List teleportees) { + return AsyncAttempt.allOf(teleportees.stream().map(this::teleport).toList()); + } + + /** + * Teleports one entity + * + * @param teleportee The entity to teleport + * @return An async future that represents the teleportation result + */ + public AsyncAttempt teleport(@NotNull Entity teleportee) { + var localTeleporter = this.teleporter == null ? teleportee : this.teleporter; + return AsyncAttempt.fromAttempt(getLocation(teleportee).mapAttempt(this::doSafetyCheck)) + .onSuccess(() -> { + if (teleportee instanceof Player player) { + this.teleportQueue.addToQueue(localTeleporter, player); + } + }) + .mapAsyncAttempt(location -> doAsyncTeleport(teleportee, location)) + .thenRun(() -> { + if (teleportee instanceof Player player) { + this.teleportQueue.popFromQueue(player.getName()); + } + }); + } + + private Attempt getLocation(@NotNull Entity teleportee) { + return this.locationOrDestination.fold( + this::parseLocation, + destination -> parseDestination(teleportee, destination) + ); + } + + private Attempt parseLocation(@Nullable Location location) { + if (location == null) { + return Attempt.failure(TeleportFailureReason.NULL_LOCATION); + } + return Attempt.success(location); + } + + private Attempt parseDestination( + @NotNull Entity teleportee, @Nullable DestinationInstance destination) { + if (destination == null) { + return Attempt.failure(TeleportFailureReason.NULL_LOCATION); + } + MVTeleportDestinationEvent event = new MVTeleportDestinationEvent(destination, teleportee, teleporter); + this.pluginManager.callEvent(event); + if (event.isCancelled()) { + return Attempt.failure(TeleportFailureReason.EVENT_CANCELLED); + } + return destination.getLocation(teleportee) + .map(Attempt::success) + .getOrElse(Attempt.failure(TeleportFailureReason.NULL_LOCATION)); + } + + private Attempt doSafetyCheck(@NotNull Location location) { + if (!this.checkSafety) { + return Attempt.success(location); + } + Location safeLocation = blockSafety.findSafeSpawnLocation(location); + if (safeLocation == null) { + return Attempt.failure(TeleportFailureReason.UNSAFE_LOCATION); + } + return Attempt.success(safeLocation); + } + + private AsyncAttempt doAsyncTeleport( + @NotNull Entity teleportee, + @NotNull Location location) { + return AsyncAttempt.of(PaperLib.teleportAsync(teleportee, location), exception -> { + Logging.warning("Failed to teleport %s to %s: %s", + teleportee.getName(), location, exception.getMessage()); + return Attempt.failure(TeleportFailureReason.TELEPORT_FAILED_EXCEPTION); + }).mapAttempt(success -> { + if (success) { + Logging.finer("Teleported async %s to %s", teleportee.getName(), location); + return Attempt.success(null); + } + Logging.warning("Failed to async teleport %s to %s", teleportee.getName(), location); + return Attempt.failure(TeleportFailureReason.TELEPORT_FAILED); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/BlockSafety.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/BlockSafety.java new file mode 100644 index 000000000..8b15c18fb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/BlockSafety.java @@ -0,0 +1,388 @@ +package org.mvplugins.multiverse.core.teleportation; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Rail; +import org.bukkit.entity.Minecart; +import org.bukkit.entity.Vehicle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.config.MVCoreConfig; + +/** + * Used to check get or find block/location-related information. + */ +@Service +public final class BlockSafety { + + private final MVCoreConfig config; + private final LocationManipulation locationManipulation; + + @Inject + BlockSafety(@NotNull MVCoreConfig config, @NotNull LocationManipulation locationManipulation) { + this.config = config; + this.locationManipulation = locationManipulation; + } + + /** + * Function to check if a block is above air. + * + * @param location The location to check. + * @return True if the block at that {@link Location} is above air. + */ + public boolean isBlockAboveAir(Location location) { + return location.getBlock().getRelative(0, -1, 0).getType().isAir(); + } + + /** + * Checks if an entity would be on track at the specified {@link Location}. + * + * @param location The location to check. + * @return True if an entity would be on tracks at the specified {@link Location}. + */ + public boolean isEntityOnTrack(Location location) { + return location.getBlock().getBlockData() instanceof Rail; + } + + /** + * Gets the location of the highest spawnable block (i.e. y-axis) at the specified {@link Location}. + * + * @param location The location + * @return The location if found, null of all blocks are unsafe. + */ + public Location getTopBlock(Location location) { + Location check = location.clone(); + int maxHeight = Option.of(location.getWorld()).map(World::getMaxHeight).getOrElse(127); + check.setY(maxHeight); + while (check.getY() > 0) { + if (canSpawnAtLocationSafely(check)) { + return check; + } + check.setY(check.getY() - 1); + } + return null; + } + + /** + * Gets the location of the lowest spawnable block (i.e. y-axis) at the specified {@link Location}. + * + * @param location The location + * @return The location if found, null of all blocks are unsafe. + */ + public Location getBottomBlock(Location location) { + Location check = location.clone(); + int minHeight = Option.of(location.getWorld()).map(World::getMinHeight).getOrElse(0); + check.setY(minHeight); + while (check.getY() < 127) { // SUPPRESS CHECKSTYLE: MagicNumberCheck + if (canSpawnAtLocationSafely(check)) { + return check; + } + check.setY(check.getY() + 1); + } + return null; + } + + /** + * Checks if the specified {@link Minecart} can spawn safely. + * + * @param cart The {@link Minecart}. + * @return True if the minecart can spawn safely. + */ + public boolean canSpawnCartSafely(Minecart cart) { + if (isBlockAboveAir(cart.getLocation())) { + return true; + } + return isEntityOnTrack(locationManipulation.getNextBlock(cart)); + } + + /** + * Checks if the specified {@link Vehicle} can spawn safely. + * + * @param vehicle The {@link Vehicle}. + * @return True if the vehicle can spawn safely. + */ + public boolean canSpawnVehicleSafely(Vehicle vehicle) { + return isBlockAboveAir(vehicle.getLocation()); + } + + /** + * This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air + * etc. This also ensures there is enough space for a player to spawn! + * + * @param location The {@link Location} + * @return Whether the player can spawn safely at the given {@link Location} + */ + public boolean canSpawnAtLocationSafely(@NotNull Location location) { + return canSpawnAtBlockSafely(location.getBlock()); + } + + /** + * This function checks whether the block at the coordinates given is safe or not by checking for Lava/Fire/Air + * etc. This also ensures there is enough space for a player to spawn! + * + * @param block The {@link Block} + * @return Whether the player can spawn safely at the given {@link Location} + */ + public boolean canSpawnAtBlockSafely(@NotNull Block block) { + Logging.finest("Checking spawn safety for location: %s, %s, %s", block.getX(), block.getY(), block.getZ()); + if (isUnsafeSpawnBody(block)) { + // Player body will be stuck in solid + Logging.finest("Unsafe location for player's body."); + return false; + } + Block airBlockForHead = block.getRelative(0, 1, 0); + if (isUnsafeSpawnBody(airBlockForHead)) { + // Player's head will be stuck in solid + Logging.finest("Unsafe location for player's head."); + return false; + } + Block standingOnBlock = block.getRelative(0, -1, 0); + if (isUnsafeSpawnPlatform(standingOnBlock)) { + // Player will drop down + Logging.finest("Unsafe location due to invalid platform."); + return false; + } + Logging.finest("Location is safe."); + return true; + } + + /** + * Player's body must be in non-solid block that is non-harming. + * + * @param block The block + * @return True if the block is unsafe + */ + private boolean isUnsafeSpawnBody(@NotNull Block block) { + Material blockMaterial = block.getType(); + return blockMaterial.isSolid() || blockMaterial == Material.FIRE; + } + + /** + * Player must stand on solid ground, or water that is only 1 block deep to prevent drowning. + * + * @param block The block + * @return True if the block is unsafe + */ + private boolean isUnsafeSpawnPlatform(@NotNull Block block) { + return !block.getType().isSolid() || isDeepWater(block); + } + + /** + * Water that is 2 or more block deep + * + * @param block The block + * @return True if the block is unsafe + */ + private boolean isDeepWater(@NotNull Block block) { + if (block.getType() != Material.WATER) { + return false; + } + return block.getRelative(0, -1, 0).getType() == Material.WATER; + } + + /** + * Finds the closest possible safe location around the given location with the configured search radius. + * + * @param location The target location to find + * @return The safe location if found, otherwise null. + */ + public @Nullable Location findSafeSpawnLocation(@NotNull Location location) { + return findSafeSpawnLocation( + location, + config.getSafeLocationHorizontalSearchRadius(), + config.getSafeLocationVerticalSearchRadius()); + } + + /** + * Finds the closest possible safe location around the given location. + * + * @param location The target location to find + * @param horizontalRange The radius around x,z of given location to search. + * @param verticalRange The height of how far up and down to search. + * @return The safe location if found, otherwise null. + */ + public @Nullable Location findSafeSpawnLocation(@NotNull Location location, int horizontalRange, int verticalRange) { + Block safeBlock = findSafeSpawnBlock(location.getBlock(), horizontalRange, verticalRange); + if (safeBlock == null) { + return null; + } + return new Location( + location.getWorld(), + safeBlock.getX() + 0.5, + safeBlock.getY(), + safeBlock.getZ() + 0.5, + location.getYaw(), + location.getPitch()); + } + + /** + * Finds the closest possible location around the given block with the configured search radius. + * + * @param block The target block to find + * @return The safe block if found, otherwise null. + */ + public @Nullable Block findSafeSpawnBlock(@NotNull Block block) { + return findSafeSpawnBlock( + block, + config.getSafeLocationHorizontalSearchRadius(), + config.getSafeLocationVerticalSearchRadius()); + } + + /** + * Finds the closest possible location around the given block. + * + * @param block The target block to find + * @param horizontalRange The radius around x,z of given block to search. + * @param verticalRange The height of how far up and down to search. + * @return The safe block if found, otherwise null. + */ + public @Nullable Block findSafeSpawnBlock(@NotNull Block block, int horizontalRange, int verticalRange) { + Block searchResult = searchAroundXZ(block, horizontalRange); + if (searchResult != null) { + return searchResult; + } + int maxHeight = block.getWorld().getMaxHeight(); + int minHeight = block.getWorld().getMinHeight(); + for (int i = 1; i <= verticalRange; i++) { + if (block.getY() + i < maxHeight) { + searchResult = searchAroundXZ(block.getRelative(0, i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; + } + } + if (block.getY() - i >= minHeight) { + searchResult = searchAroundXZ(block.getRelative(0, -i, 0), horizontalRange); + if (searchResult != null) { + return searchResult; + } + } + } + return null; + } + + /** + * Search a square from n - radius to n + radius for both x and z + * + * @param block The block to be relative to + * @param radius The number of blocks +/- x and z to search + * @return The safe block, or null + */ + @Nullable + private Block searchAroundXZ(Block block, int radius) { + if (canSpawnAtBlockSafely(block)) { + return block; + } + for (int r = 1; r <= radius; r++) { + boolean radiusX = true; + boolean incrementOffset = false; + int offset = 0; + int noOfIterations = r * 2 + 1; + for (int i = 0; i < noOfIterations; i++) { + Block searchResult = radiusX + ? searchPlusMinusPermutation(block, r, offset) + : searchPlusMinusPermutation(block, offset, r); + if (searchResult != null) { + return searchResult; + } + if (incrementOffset) { + offset++; + } + radiusX = !radiusX; + incrementOffset = !incrementOffset; + } + } + return null; + } + + /** + * Search 4 relative blocks with the following offsets: (-x, -z) (-x, z) (x, -z) (x, z) + * + * @param block The block to be relative to + * @param x Amount to offset for the x axis + * @param z Amount to offset for the z axis + * @return The safe block, or null + */ + @Nullable + private Block searchPlusMinusPermutation(Block block, int x, int z) { + Block relative = block.getRelative(-x, 0, -z); + if (canSpawnAtBlockSafely(relative)) { + return relative; + } + if (z != 0) { + relative = block.getRelative(-x, 0, z); + if (canSpawnAtBlockSafely(relative)) { + return relative; + } + } + if (x != 0) { + relative = block.getRelative(x, 0, -z); + if (canSpawnAtBlockSafely(relative)) { + return relative; + } + if (z != 0) { + relative = block.getRelative(x, 0, z); + if (canSpawnAtBlockSafely(relative)) { + return relative; + } + } + } + return null; + } + + /** + * Finds a portal-block next to the specified {@link Location}. + * + * @param location The {@link Location} + * @return The next portal-block's {@link Location} if found, otherwise null. + */ + public @Nullable Location findPortalBlockNextTo(Location location) { + if (location.getWorld() == null) { + return null; + } + Block b = location.getWorld().getBlockAt(location); + Location foundLocation = null; + if (b.getType() == Material.NETHER_PORTAL) { + return location; + } + if (b.getRelative(BlockFace.NORTH).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.NORTH).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.SOUTH).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.SOUTH).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.EAST).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.EAST).getLocation(), foundLocation); + } + if (b.getRelative(BlockFace.WEST).getType() == Material.NETHER_PORTAL) { + foundLocation = getCloserBlock(location, b.getRelative(BlockFace.WEST).getLocation(), foundLocation); + } + return foundLocation; + } + + private Location getCloserBlock(Location source, Location blockA, Location blockB) { + // If B wasn't given, return a. + if (blockB == null) { + return blockA; + } + // Center our calculations + blockA.add(.5, 0, .5); + blockB.add(.5, 0, .5); + + // Retrieve the distance to the normalized blocks + double testA = source.distance(blockA); + double testB = source.distance(blockB); + + // Compare and return + if (testA <= testB) { + return blockA; + } + return blockB; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/LocationManipulation.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/LocationManipulation.java similarity index 85% rename from src/main/java/com/onarandombox/MultiverseCore/utils/LocationManipulation.java rename to src/main/java/org/mvplugins/multiverse/core/teleportation/LocationManipulation.java index b0bce924d..992666951 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/LocationManipulation.java +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/LocationManipulation.java @@ -5,7 +5,13 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core.teleportation; + +import java.text.DecimalFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -13,25 +19,17 @@ import org.bukkit.World; import org.bukkit.entity.Vehicle; import org.bukkit.util.Vector; - -import java.text.DecimalFormat; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import org.jvnet.hk2.annotations.Service; /** - * Utility class to manipulate locations. - * - * @deprecated Use instead: {@link com.onarandombox.MultiverseCore.api.LocationManipulation} and {@link SimpleLocationManipulation}. + * Used to manipulate locations. */ -@Deprecated -public class LocationManipulation { - private LocationManipulation() { } - - private static Map orientationInts = new HashMap(); +@Service +public final class LocationManipulation { + private static final Map ORIENTATION_INTS; static { + Map orientationInts = new HashMap(); // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck orientationInts.put("n", 180); orientationInts.put("ne", 225); @@ -42,8 +40,17 @@ private LocationManipulation() { } orientationInts.put("w", 90); orientationInts.put("nw", 135); + orientationInts.put("north", 180); + orientationInts.put("northeast", 225); + orientationInts.put("east", 270); + orientationInts.put("southeast", 315); + orientationInts.put("south", 0); + orientationInts.put("southwest", 45); + orientationInts.put("west", 90); + orientationInts.put("northwest", 135); + // "freeze" the map: - orientationInts = Collections.unmodifiableMap(orientationInts); + ORIENTATION_INTS = Collections.unmodifiableMap(orientationInts); // END CHECKSTYLE-SUPPRESSION: MagicNumberCheck } @@ -57,13 +64,10 @@ private LocationManipulation() { } * @param location The Location to save. * @return The location as a string in this format: WORLD:x,y,z:yaw:pitch */ - public static String locationToString(Location location) { + public String locationToString(Location location) { if (location == null) { return ""; } - // We set the locale to ENGLISH here so we always save with the format: - // world:1.2,5.4,3.6:1.8:21.3 - // Otherwise we blow up when parsing! return String.format(Locale.ENGLISH, "%s:%.2f,%.2f,%.2f:%.2f:%.2f", location.getWorld().getName(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); } @@ -74,7 +78,7 @@ public static String locationToString(Location location) { * @param l The location to round to the block location * @return A rounded location. */ - public static Location getBlockLocation(Location l) { + public Location getBlockLocation(Location l) { l.setX(l.getBlockX()); l.setY(l.getBlockY()); l.setZ(l.getBlockZ()); @@ -91,7 +95,7 @@ public static Location getBlockLocation(Location l) { * @param locationString The location represented as a string (WORLD:X,Y,Z:yaw:pitch) * @return A new location defined by the string or null if the string was invalid. */ - public static Location stringToLocation(String locationString) { + public Location stringToLocation(String locationString) { //format: //world:x,y,z:pitch:yaw if (locationString == null) { @@ -136,9 +140,9 @@ public static Location stringToLocation(String locationString) { * Returns a colored string with the coords. * * @param l The {@link Location} - * @return The {@link String} + * @return The {@link String}\ */ - public static String strCoords(Location l) { + public String strCoords(Location l) { String result = ""; DecimalFormat df = new DecimalFormat(); df.setMinimumFractionDigits(0); @@ -157,7 +161,7 @@ public static String strCoords(Location l) { * @param l The {@link Location} * @return The {@link String} */ - public static String strCoordsRaw(Location l) { + public String strCoordsRaw(Location l) { if (l == null) { return "null"; } @@ -179,7 +183,7 @@ public static String strCoordsRaw(Location l) { * @param location The {@link Location} * @return The NESW Direction */ - public static String getDirection(Location location) { + public String getDirection(Location location) { // BEGIN CHECKSTYLE-SUPPRESSION: MagicNumberCheck double r = (location.getYaw() % 360) + 180; // Remember, these numbers are every 45 degrees with a 22.5 offset, to detect boundaries. @@ -213,12 +217,12 @@ else if (r < 337.5) * @param orientation The cardinal direction * @return The yaw */ - public static float getYaw(String orientation) { + public float getYaw(String orientation) { if (orientation == null) { return 0; } - if (orientationInts.containsKey(orientation.toLowerCase())) { - return orientationInts.get(orientation.toLowerCase()); + if (ORIENTATION_INTS.containsKey(orientation.toLowerCase())) { + return ORIENTATION_INTS.get(orientation.toLowerCase()); } return 0; } @@ -229,7 +233,7 @@ public static float getYaw(String orientation) { * @param v The {@link Vector} * @return The speed */ - public static float getSpeed(Vector v) { + public float getSpeed(Vector v) { return (float) Math.sqrt(v.getX() * v.getX() + v.getZ() * v.getZ()); } @@ -243,7 +247,7 @@ public static float getSpeed(Vector v) { * @param direction The new direction * @return The translated {@link Vector} */ - public static Vector getTranslatedVector(Vector v, String direction) { + public Vector getTranslatedVector(Vector v, String direction) { if (direction == null) { return v; } @@ -275,7 +279,7 @@ public static Vector getTranslatedVector(Vector v, String direction) { * @param v The {@link Vehicle} * @return The {@link Location} */ - public static Location getNextBlock(Vehicle v) { + public Location getNextBlock(Vehicle v) { Vector vector = v.getVelocity(); Location location = v.getLocation(); int x = vector.getX() < 0 ? vector.getX() == 0 ? 0 : -1 : 1; diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportFailureReason.java new file mode 100644 index 000000000..9701bfde0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportFailureReason.java @@ -0,0 +1,58 @@ +package org.mvplugins.multiverse.core.teleportation; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; +import org.mvplugins.multiverse.core.event.MVTeleportDestinationEvent; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Reasons for a failed teleport. + */ +public enum TeleportFailureReason implements FailureReason { + /** + * The destination was null. + */ + NULL_DESTINATION(MVCorei18n.TELEPORTFAILUREREASON_NULL_DESTINATION), + + /** + * The location was null. + */ + NULL_LOCATION(MVCorei18n.TELEPORTFAILUREREASON_NULL_LOCATION), + + /** + * The location was unsafe. + */ + UNSAFE_LOCATION(MVCorei18n.TELEPORTFAILUREREASON_UNSAFE_LOCATION), + + /** + * The server teleport return false. + */ + TELEPORT_FAILED(MVCorei18n.TELEPORTFAILUREREASON_TELEPORT_FAILED), + + /** + * An exception was thrown. + */ + TELEPORT_FAILED_EXCEPTION(MVCorei18n.TELEPORTFAILUREREASON_TELEPORT_FAILED_EXCEPTION), + + /** + * The {@link MVTeleportDestinationEvent} was cancelled. + */ + EVENT_CANCELLED(MVCorei18n.TELEPORTFAILUREREASON_EVENT_CANCELLED), + ; + + private final MessageKeyProvider messageKey; + + TeleportFailureReason(MessageKeyProvider message) { + this.messageKey = message; + } + + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return messageKey.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportQueue.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportQueue.java new file mode 100644 index 000000000..be3f89f80 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/TeleportQueue.java @@ -0,0 +1,55 @@ +package org.mvplugins.multiverse.core.teleportation; + +import java.util.HashMap; +import java.util.Map; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jvnet.hk2.annotations.Service; + +@Service +public final class TeleportQueue { + + private final Map teleportQueueMap; + + TeleportQueue() { + teleportQueueMap = new HashMap<>(); + } + + /** + * This method is used to add a teleportation to the teleportQueue. + * + * @param teleporter The sender that initiated the teleportation. + * @param teleportee The player that will be teleported. + */ + public void addToQueue(CommandSender teleporter, Player teleportee) { + addToQueue(teleporter.getName(), teleportee.getName()); + } + + /** + * This method is used to add a teleportation to the teleportQueue. + * + * @param teleporter The name of the sender that initiated the teleportation. + * @param teleportee The name of the player that will be teleported. + */ + public void addToQueue(String teleporter, String teleportee) { + Logging.finest("Adding mapping '%s' => '%s' to teleport queue", teleporter, teleportee); + teleportQueueMap.put(teleportee, teleporter); + } + + /** + * This method is used to find out who is teleporting a player. + * @param playerName The teleported player (the teleportee). + * @return The player that teleported the other one (the teleporter). + */ + public Option popFromQueue(String playerName) { + if (teleportQueueMap.containsKey(playerName)) { + String teleportee = teleportQueueMap.get(playerName); + teleportQueueMap.remove(playerName); + return Option.of(teleportee); + } + return Option.none(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/teleportation/package-info.java b/src/main/java/org/mvplugins/multiverse/core/teleportation/package-info.java new file mode 100644 index 000000000..a778df7a4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/teleportation/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains all the teleportation and location safety related classes. + */ +package org.mvplugins.multiverse.core.teleportation; diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/FileUtils.java b/src/main/java/org/mvplugins/multiverse/core/utils/FileUtils.java new file mode 100644 index 000000000..3ac7a3c96 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/FileUtils.java @@ -0,0 +1,207 @@ +/****************************************************************************** + * Multiverse 2 Copyright (c) the Multiverse Team 2011. * + * Multiverse 2 is licensed under the BSD License. * + * For more information please check the README.md file included * + * with this project. * + ******************************************************************************/ + +package org.mvplugins.multiverse.core.utils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Stream; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.MultiverseCore; + +import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; + +/** + * File-utilities. + */ +@Service +public final class FileUtils { + + private final File serverFolder; + private final File bukkitYml; + private final File serverProperties; + + @Inject + protected FileUtils(@NotNull MultiverseCore plugin) { + this.serverFolder = new File(System.getProperty("user.dir")); + Logging.finer("Server folder: " + this.serverFolder); + this.bukkitYml = findFileFromServerDirectory("bukkit.yml"); + this.serverProperties = findFileFromServerDirectory("server.properties"); + } + + private @Nullable File findFileFromServerDirectory(String fileName) { + File[] files; + try { + files = this.serverFolder.listFiles((file, s) -> s.equalsIgnoreCase(fileName)); + } catch (Exception e) { + Logging.severe("Could not read from server directory. Unable to locate file: %s", fileName); + Logging.severe(e.getMessage()); + return null; + } + if (files != null && files.length == 1) { + return files[0]; + } + Logging.warning("Unable to locate file from server directory: %s", fileName); + return null; + } + + /** + * The root server folder where Multiverse-Core is installed. + * + * @return The root server folder + */ + public File getServerFolder() { + return this.serverFolder; + } + + /** + * The bukkit.yml file + * + * @return The bukkit.yml file if exist, else null. + */ + public @Nullable File getBukkitConfig() { + return this.bukkitYml; + } + + /** + * The server.properties file + * + * @return The server.properties file if exist, else null. + */ + public @Nullable File getServerProperties() { + return this.serverProperties; + } + + /** + * Deletes the given folder completely. + * + * @param file The folder to delete. + * @return A {@link Try} that will contain {@code null} if the folder was deleted successfully, or an exception if + * the folder could not be deleted. + */ + public Try deleteFolder(File file) { + return deleteFolder(file.toPath()); + } + + /** + * Deletes the given folder completely. + * + * @param path The folder to delete. + * @return A {@link Try} that will contain {@code null} if the folder was deleted successfully, or an exception if + * the folder could not be deleted. + */ + public Try deleteFolder(Path path) { + try (Stream files = Files.walk(path)) { + files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + return Try.success(null); + } catch (IOException e) { + Logging.severe("Failed to delete folder: " + path.toAbsolutePath()); + e.printStackTrace(); + return Try.failure(e); + } + } + + /** + * Copies all the content of the given folder to the given target folder. + * + * @param sourceDir The folder to copy. + * @param targetDir The target folder to copy to. + * @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if + * the folder could not be copied. + */ + public Try copyFolder(File sourceDir, File targetDir) { + return copyFolder(sourceDir.toPath(), targetDir.toPath(), Collections.emptyList()); + } + + /** + * Copies most of the content of the given folder to the given target folder, except the list of excluded files + * specified. + * + * @param sourceDir The folder to copy. + * @param targetDir The target folder to copy to. + * @param excludeFiles The list of files to exclude from copying. + * @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if + */ + public Try copyFolder(File sourceDir, File targetDir, List excludeFiles) { + return copyFolder(sourceDir.toPath(), targetDir.toPath(), excludeFiles); + } + + /** + * Copies all the content of the given folder to the given target folder. + * + * @param sourceDir The folder to copy. + * @param targetDir The target folder to copy to. + * @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if + * the folder could not be copied. + */ + public Try copyFolder(Path sourceDir, Path targetDir) { + return copyFolder(sourceDir, targetDir, Collections.emptyList()); + } + + /** + * Copies most of the content of the given folder to the given target folder, except the list of excluded files + * specified. + * + * @param sourceDir The folder to copy. + * @param targetDir The target folder to copy to. + * @param excludeFiles The list of files to exclude from copying. + * @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if + */ + public Try copyFolder(Path sourceDir, Path targetDir, List excludeFiles) { + return Try.run(() -> Files.walkFileTree(sourceDir, new CopyDirFileVisitor(sourceDir, targetDir, excludeFiles))) + .onFailure(e -> { + Logging.severe("Failed to copy folder: " + sourceDir.toAbsolutePath()); + e.printStackTrace(); + }); + } + + private static final class CopyDirFileVisitor extends SimpleFileVisitor { + + private final Path sourceDir; + private final Path targetDir; + private final List excludeFiles; + + private CopyDirFileVisitor(@NotNull Path sourceDir, @NotNull Path targetDir, @NotNull List excludeFiles) { + this.sourceDir = sourceDir; + this.targetDir = targetDir; + this.excludeFiles = excludeFiles; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path newDir = targetDir.resolve(sourceDir.relativize(dir)); + if (!Files.isDirectory(newDir)) { + Files.createDirectory(newDir); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + // Pass files that are set to ignore + if (excludeFiles.contains(file.getFileName().toString())) { + Logging.finest("Ignoring file: " + file.getFileName()); + return FileVisitResult.CONTINUE; + } + // Copy the files + Path targetFile = targetDir.resolve(sourceDir.relativize(file)); + Files.copy(file, targetFile, COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/MaterialConverter.java b/src/main/java/org/mvplugins/multiverse/core/utils/MaterialConverter.java new file mode 100644 index 000000000..402be759a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/MaterialConverter.java @@ -0,0 +1,27 @@ +package org.mvplugins.multiverse.core.utils; + +import de.themoep.idconverter.IdMappings; +import org.bukkit.Material; +import org.jetbrains.annotations.Nullable; + +/** + * A tool for converting values which may be an old type ID to a Material. + */ +public final class MaterialConverter { + + /** + * Converts a string representing a numeric id or flattened material name to a Material. + * + * @param value The value to convert. + * @return The converted Material type or null if no matching type. + */ + @Nullable + public static Material stringToMaterial(@Nullable String value) { + IdMappings.Mapping mapping = IdMappings.getById(value != null ? value : ""); + if (mapping != null) { + return Material.matchMaterial(mapping.getFlatteningType()); + } else { + return Material.matchMaterial(value != null ? value : ""); + } + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/PlayerFinder.java b/src/main/java/org/mvplugins/multiverse/core/utils/PlayerFinder.java similarity index 73% rename from src/main/java/com/onarandombox/MultiverseCore/utils/PlayerFinder.java rename to src/main/java/org/mvplugins/multiverse/core/utils/PlayerFinder.java index 318450084..601e5c094 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/PlayerFinder.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/PlayerFinder.java @@ -1,26 +1,37 @@ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import com.dumptruckman.minecraft.util.Logging; +import com.google.common.base.Strings; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - /** * Helper class to get {@link Player} from name, UUID or Selectors. */ -public class PlayerFinder { +public final class PlayerFinder { 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(","); + /** + * Get a {@link Player} based on an identifier of name UUID or selector. + * + * @param playerIdentifier An identifier of name UUID or selector. + * @return The player if found, else null. + */ + public static @Nullable Player get(@Nullable String playerIdentifier) { + return get(playerIdentifier, Bukkit.getConsoleSender()); + } + /** * Get a {@link Player} based on an identifier of name UUID or selector. * @@ -28,21 +39,34 @@ public class PlayerFinder { * @param sender Target sender for selector. * @return The player if found, else null. */ - @Nullable - public static Player get(@NotNull String playerIdentifier, - @NotNull CommandSender sender) { + public static @Nullable Player get(@Nullable String playerIdentifier, @NotNull CommandSender sender) { + if (playerIdentifier == null) { + return null; + } Player targetPlayer = getByName(playerIdentifier); if (targetPlayer != null) { return targetPlayer; } + targetPlayer = getByUuid(playerIdentifier); if (targetPlayer != null) { return targetPlayer; } + return getBySelector(playerIdentifier, sender); } + /** + * Get multiple {@link Player} based on many identifiers of name UUID or selector. + * + * @param playerIdentifiers An identifier of multiple names, UUIDs or selectors, separated by comma. + * @return A list of all the {@link Player} found. + */ + public static @NotNull List getMulti(@Nullable String playerIdentifiers) { + return getMulti(playerIdentifiers, Bukkit.getConsoleSender()); + } + /** * Get multiple {@link Player} based on many identifiers of name UUID or selector. * @@ -50,16 +74,16 @@ public static Player get(@NotNull String playerIdentifier, * @param sender Target sender for selector. * @return A list of all the {@link Player} found. */ - @Nullable - public static List getMulti(@NotNull String playerIdentifiers, - @NotNull CommandSender sender) { + public static @NotNull List getMulti(@Nullable String playerIdentifiers, + @NotNull CommandSender sender + ) { + List playerResults = new ArrayList<>(); - String[] playerIdentifierArray = COMMA_SPLIT.split(playerIdentifiers); - if (playerIdentifierArray == null || playerIdentifierArray.length == 0) { - return null; + if (playerIdentifiers == null || Strings.isNullOrEmpty(playerIdentifiers)) { + return playerResults; } - List playerResults = new ArrayList<>(); + String[] playerIdentifierArray = COMMA_SPLIT.split(playerIdentifiers); for (String playerIdentifier : playerIdentifierArray) { Player targetPlayer = getByName(playerIdentifier); if (targetPlayer != null) { @@ -96,8 +120,7 @@ public static Player getByName(@NotNull String playerName) { * @param playerUuid UUID of a player. * @return The player if found, else null. */ - @Nullable - public static Player getByUuid(@NotNull String playerUuid) { + public static @Nullable Player getByUuid(@NotNull String playerUuid) { if (!UUID_REGEX.matcher(playerUuid).matches()) { return null; } @@ -116,8 +139,7 @@ public static Player getByUuid(@NotNull String playerUuid) { * @param playerUuid UUID of a player. * @return The player if found, else null. */ - @Nullable - public static Player getByUuid(@NotNull UUID playerUuid) { + public static @Nullable Player getByUuid(@NotNull UUID playerUuid) { return Bukkit.getPlayer(playerUuid); } @@ -129,10 +151,9 @@ public static Player getByUuid(@NotNull UUID playerUuid) { * @param sender Target sender for selector. * @return The player if only one found, else null. */ - @Nullable - public static Player getBySelector(@NotNull String playerSelector, - @NotNull CommandSender sender) { - + public static @Nullable Player getBySelector(@NotNull String playerSelector, + @NotNull CommandSender sender + ) { List matchedPlayers = getMultiBySelector(playerSelector, sender); if (matchedPlayers == null || matchedPlayers.isEmpty()) { Logging.fine("No player found with selector '%s' for %s.", playerSelector, sender.getName()); @@ -154,10 +175,9 @@ public static Player getBySelector(@NotNull String playerSelector, * @param sender Target sender for selector. * @return A list of all the {@link Player} found. */ - @Nullable - public static List getMultiBySelector(@NotNull String playerSelector, - @NotNull CommandSender sender) { - + public static @Nullable List getMultiBySelector(@NotNull String playerSelector, + @NotNull CommandSender sender + ) { if (playerSelector.charAt(0) != '@') { return null; } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/ReflectHelper.java b/src/main/java/org/mvplugins/multiverse/core/utils/ReflectHelper.java similarity index 94% rename from src/main/java/com/onarandombox/MultiverseCore/utils/ReflectHelper.java rename to src/main/java/org/mvplugins/multiverse/core/utils/ReflectHelper.java index 9e81bfe20..fc87f82b5 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/ReflectHelper.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/ReflectHelper.java @@ -1,14 +1,14 @@ -package com.onarandombox.MultiverseCore.utils; - -import org.jetbrains.annotations.Nullable; +package org.mvplugins.multiverse.core.utils; import java.lang.reflect.Field; import java.lang.reflect.Method; +import org.jetbrains.annotations.Nullable; + /** * Utility class used to help in doing various reflection actions. */ -public class ReflectHelper { +public final class ReflectHelper { /** * Try to get the {@link Class} based on its classpath. @@ -130,9 +130,10 @@ public static Field getField(C classInstance, String fieldName) { * @return The field value if any, else null. */ @Nullable - public static V getFieldValue(C classInstance, Field field) { + public static V getFieldValue(C classInstance, Field field, Class fieldType) { try { - return (V) field.get(classInstance); + Object value = field.get(classInstance); + return fieldType.isInstance(value) ? fieldType.cast(value) : null; } catch (IllegalAccessException e) { return null; } diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/ServerProperties.java b/src/main/java/org/mvplugins/multiverse/core/utils/ServerProperties.java new file mode 100644 index 000000000..b34ebd39a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/ServerProperties.java @@ -0,0 +1,54 @@ +package org.mvplugins.multiverse.core.utils; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import jakarta.inject.Inject; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; + +@Service +public final class ServerProperties { + + private final Map properties; + private final FileUtils fileUtils; + + @Inject + public ServerProperties(@NotNull FileUtils fileUtils) { + this.fileUtils = fileUtils; + properties = new HashMap<>(); + parseServerPropertiesFile(); + } + + private void parseServerPropertiesFile() { + if (fileUtils.getServerProperties() == null) { + return; + } + try { + Files.readAllLines(fileUtils.getServerProperties().toPath()).stream() + .map(String::strip) + .filter(line -> !line.startsWith("#")) + .map(line -> line.split("=", 2)) + .filter(line -> line.length == 2) + .forEach(line -> properties.put(line[0], line[1])); + } catch (IOException e) { + Logging.warning("Could not read server.properties file! Some features such as default world may not work as expected."); + } + } + + public Option getLevelName() { + return getProperty("level-name"); + } + + public boolean getAllowNether() { + return getProperty("allow-nether").map(Boolean::parseBoolean).getOrElse(true); + } + + public Option getProperty(String key) { + return Option.of(properties.get(key)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java b/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java new file mode 100644 index 000000000..4b6a2a69b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/StringFormatter.java @@ -0,0 +1,57 @@ +package org.mvplugins.multiverse.core.utils; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Common string formatting methods used throughout Multiverse plugins. + */ +public final class StringFormatter { + + private StringFormatter() { + // no instance + } + + + /** + * Concatenates a list of strings into a single string, using a comma and a space as separators, + * and " and " as the separator before the last element. + *
+ * This method is useful for creating human-readable lists, such as "John, Mary and David". + * + * @param list the list of strings to join. If the list is empty, an empty string is returned. + * @return the concatenated string + */ + public static @NotNull String joinAnd(List list) { + return join(list, ", ", " and "); + } + + /** + * Concatenates a list of strings into a single string, using a specified separator and a different separator + * for the last element. + * + * @param list the list of strings to join. If the list is empty, an empty string is returned. + * @param separator the separator to use between elements, except for the last one. For example, a comma and a space. + * @param lastSeparator the separator to use before the last element. For example, " and ". + * @return the concatenated string + */ + public static @NotNull String join(List list, String separator, String lastSeparator) { + if (list.isEmpty()) { + return ""; + } + + StringBuilder authors = new StringBuilder(); + authors.append(list.get(0)); + + for (int i = 1; i < list.size(); i++) { + if (i == list.size() - 1) { + authors.append(lastSeparator).append(list.get(i)); + } else { + authors.append(separator).append(list.get(i)); + } + } + + return authors.toString(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/WorldTickDeferrer.java b/src/main/java/org/mvplugins/multiverse/core/utils/WorldTickDeferrer.java new file mode 100644 index 000000000..1feed3f3e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/WorldTickDeferrer.java @@ -0,0 +1,66 @@ +package org.mvplugins.multiverse.core.utils; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.MultiverseCore; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Objects; + +@Service +public final class WorldTickDeferrer { + + private final MultiverseCore plugin; + + private Object console = null; + private Field isIteratingOverLevelsMethod = null; + + @Inject + WorldTickDeferrer(@NotNull MultiverseCore plugin, @NotNull Server server) { + this.plugin = plugin; + Method getServerMethod = ReflectHelper.getMethod(server.getClass(), "getServer"); + if (getServerMethod == null) { + Logging.fine("Unable to find getServer method."); + return; + } + this.console = ReflectHelper.invokeMethod(server, getServerMethod); + if (console == null) { + Logging.fine("Unable to find console."); + return; + } + this.isIteratingOverLevelsMethod = Try.of(() -> console.getClass().getField("isIteratingOverLevels")).getOrNull(); + if (isIteratingOverLevelsMethod == null) { + Logging.fine("Unable to find isIteratingOverLevels field."); + } + } + + public void deferWorldTick(Runnable action) { + if (!isIteratingOverLevels()) { + action.run(); + return; + } + Logging.fine("Deferring world tick..."); + new BukkitRunnable() { + @Override + public void run() { + action.run(); + } + }.runTaskLater(this.plugin, 1L); + } + + public boolean isIteratingOverLevels() { + if (console == null || isIteratingOverLevelsMethod == null) { + return false; + } + return Objects.requireNonNullElse( + ReflectHelper.getFieldValue(console, isIteratingOverLevelsMethod, Boolean.class), + false); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/package-info.java b/src/main/java/org/mvplugins/multiverse/core/utils/package-info.java similarity index 53% rename from src/main/java/com/onarandombox/MultiverseCore/utils/package-info.java rename to src/main/java/org/mvplugins/multiverse/core/utils/package-info.java index e35f9681b..4232a6a74 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/package-info.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/package-info.java @@ -1,4 +1,4 @@ /** * This package contains all Utility-clases. */ -package com.onarandombox.MultiverseCore.utils; +package org.mvplugins.multiverse.core.utils; diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/Async.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/Async.java new file mode 100644 index 000000000..3d6de95f5 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/Async.java @@ -0,0 +1,156 @@ +package org.mvplugins.multiverse.core.utils.result; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Represents the result of an asynchronous operation that wraps a {@link CompletableFuture}. + * + * @param The type of the value. + */ +public final class Async { + + /** + * Returns a new AsyncResult that is completed when all of the given AsyncResult complete. + * + * @param results The results to wait for. + * @return A new AsyncResult that is completed when all of the given AsyncResult complete. + */ + public static Async allOf(Async... results) { + return new Async<>(CompletableFuture.allOf(Arrays.stream(results) + .map(result -> result.future) + .toArray(CompletableFuture[]::new))); + } + + /** + * Returns a new AsyncResult that is completed when all of the given AsyncResult with the same type complete. + * + * @param results The results to wait for. + * @param The type of the AsyncResult. + * @return A new AsyncResult that is completed when all of the given AsyncResult complete. + */ + public static Async> allOf(List> results) { + return new Async<>(CompletableFuture.allOf(results.stream() + .map(result -> result.future) + .toArray(CompletableFuture[]::new)) + .thenApply(v -> results.stream() + .map(result -> result.future.join()) + .toList())); + } + + /** + * Wraps a CompletableFuture in an AsyncResult. + * + * @param future The future to wrap. + * @param The type of the future. + * @return A new AsyncResult that is completed when the given future completes. + */ + public static Async of(CompletableFuture future) { + return new Async<>(future); + } + + /** + * Returns a new AsyncResult that is already completed with the given value. + * + * @param value The value to complete the AsyncResult with. + * @param The type of the value. + * @return The completed AsyncResult. + */ + public static Async completedFuture(T value) { + return new Async<>(CompletableFuture.completedFuture(value)); + } + + /** + * Returns a new CompletableFuture that is already completed exceptionally with the given exception. + * + * @param throwable The exception to complete the AsyncResult with. + * @param The type of the value. + * @return The completed AsyncResult. + */ + public static Async failedFuture(Throwable throwable) { + return new Async<>(CompletableFuture.failedFuture(throwable)); + } + + private final CompletableFuture future; + + private Async(CompletableFuture future) { + this.future = future; + } + + /** + * If not already completed, sets the value returned by related methods to the given value. + * + * @param value The value to complete the AsyncResult with. + * @return true if this invocation caused this AsyncResult to transition to a completed state, else false. + */ + public boolean complete(T value) { + return future.complete(value); + } + + /** + * If not already completed, causes invocations of related methods to throw the given exception. + * + * @param throwable The exception to complete the AsyncResult with. + * @return true if this invocation caused this AsyncResult to transition to a completed state, else false. + */ + public boolean completeExceptionally(Throwable throwable) { + return future.completeExceptionally(throwable); + } + + /** + * Executes the given action when this AsyncResult completes. + * + * @param consumer The action to perform. + * @return This AsyncResult. + */ + public Async thenAccept(Consumer consumer) { + return new Async<>(future.thenAccept(consumer)); + } + + /** + * Executes the given action when this AsyncResult completes. + * + * @param runnable The action to perform. + * @return This AsyncResult. + */ + public Async thenRun(Runnable runnable) { + return new Async<>(future.thenRun(runnable)); + } + + /** + * Executes the given action when this AsyncResult completes and returns a new AsyncResult with the new value. + * + * @param function The action to perform. + * @param The type of the new value. + * @return A new AsyncResult with the new value. + */ + public Async thenApply(Function function) { + return new Async<>(future.thenApply(function)); + } + + /** + * Executes the given action when this AsyncResult completes with an exception. + * + * @param consumer The action to perform. + * @return This AsyncResult. + */ + public Async exceptionally(Consumer consumer) { + return new Async<>(future.exceptionally(throwable -> { + consumer.accept(throwable); + return null; + })); + } + + /** + * Executes the given action when this AsyncResult completes with an exception and returns a new AsyncResult with the new value. + * + * @param function The action to perform. + * @return A new AsyncResult with the new value. + */ + public Async exceptionally(Function function) { + return new Async<>(future.exceptionally(function)); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/AsyncAttempt.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/AsyncAttempt.java new file mode 100644 index 000000000..6f3907621 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/AsyncAttempt.java @@ -0,0 +1,137 @@ +package org.mvplugins.multiverse.core.utils.result; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +import io.vavr.control.Either; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; + +/** + * + * + * @param The success value type. + * @param The failure reason type. + */ +public final class AsyncAttempt { + + /** + * From a list of attempts. + * + * @param attempts + * @return + * @param + * @param + */ + public static Async>> allOf(List> attempts) { + return Async.of(CompletableFuture.allOf(attempts.stream() + .map(attempt -> attempt.future) + .toArray(CompletableFuture[]::new)) + .thenApply(v -> attempts.stream() + .map(attempt -> attempt.future.join()) + .toList())); + } + + /** + * Create from a completable future. + * + * @param future + * @param completionHandler + * @return + * @param + * @param + */ + public static AsyncAttempt of( + CompletableFuture future, + BiFunction> completionHandler) { + return new AsyncAttempt<>(future.handle(completionHandler)); + } + + /** + * + * @param future + * @param exceptionHandler + * @return + * @param + * @param + */ + public static AsyncAttempt of( + CompletableFuture future, + Function> exceptionHandler) { + BiFunction> completionHandler = (result, exception) -> exception != null + ? exceptionHandler.apply(exception) + : Attempt.success(result); + return of(future, completionHandler); + } + + /** + * + * @param attempt + * @return + * @param + * @param + */ + public static AsyncAttempt fromAttempt(Attempt attempt) { + return new AsyncAttempt<>(CompletableFuture.completedFuture(attempt)); + } + + public static AsyncAttempt success() { + return new AsyncAttempt<>(CompletableFuture.completedFuture(null)); + } + + public static AsyncAttempt failure( + F failureReason, + MessageReplacement... messageReplacements) { + return new AsyncAttempt<>(CompletableFuture.completedFuture( + Attempt.failure(failureReason, messageReplacements))); + } + + private final CompletableFuture> future; + + private AsyncAttempt(CompletableFuture> future) { + this.future = future; + } + + public AsyncAttempt thenRun(Runnable runnable) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.thenRun(runnable))); + } + + public AsyncAttempt thenAccept(Consumer> consumer) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.thenAccept(consumer))); + } + + public AsyncAttempt map(Function mapper) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.map(mapper))); + } + + public AsyncAttempt mapAttempt(Function> mapper) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.mapAttempt(mapper))); + } + + public AsyncAttempt mapAsyncAttempt(Function> mapper) { + return new AsyncAttempt<>(future.thenApplyAsync( + attempt -> attempt.mapAttempt(rasult -> mapper.apply(rasult).toAttempt()))); + } + + public AsyncAttempt onSuccess(Runnable runnable) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.onSuccess(runnable))); + } + + public AsyncAttempt onFailure(Runnable runnable) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.onFailure(runnable))); + } + + public AsyncAttempt onFailure(Consumer> consumer) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.onFailure(consumer))); + } + + public AsyncAttempt onFailureReason(Consumer consumer) { + return new AsyncAttempt<>(future.thenApply(attempt -> attempt.onFailureReason(consumer))); + } + + public Attempt toAttempt() { + return future.join(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/Attempt.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/Attempt.java new file mode 100644 index 000000000..4a6b33a99 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/Attempt.java @@ -0,0 +1,353 @@ +package org.mvplugins.multiverse.core.utils.result; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import io.vavr.control.Either; +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; + +/** + * Represents an attempt to process a value that can fail with a reason that has a localized message. + * + * @param The type of the value. + * @param The type of failure reason. + */ +public sealed interface Attempt permits Attempt.Success, Attempt.Failure { + + /** + * Creates a new success attempt. + * + * @param value The value. + * @param The type of the value. + * @param The type of failure reason. + * @return The new success attempt. + */ + static Attempt success(T value) { + return new Success<>(value); + } + + /** + * Creates a new failure attempt. + * + * @param failureReason The reason for failure. + * @param messageReplacements The replacements for the failure message. + * @param The type of the value. + * @param The type of failure reason. + * @return The new failure attempt. + */ + static Attempt failure( + F failureReason, MessageReplacement... messageReplacements) { + return new Failure<>(failureReason, Message.of(failureReason, "Failed!", messageReplacements)); + } + + /** + * Creates a new failure attempt with a custom message. + * + * @param failureReason The reason for failure. + * @param message The custom message for failure. This will override the default message. + * @param The type of the value. + * @param The type of failure reason. + * @return The new failure attempt. + */ + static Attempt failure(F failureReason, Message message) { + return new Failure<>(failureReason, message); + } + + /** + * Gets the value of this attempt. Exceptions will be thrown if this is a failure attempt. + * + * @return The value. + */ + T get(); + + /** + * Gets the reason for failure. Exceptions will be thrown if this is a success attempt. + * + * @return The reason for failure. + */ + F getFailureReason(); + + /** + * Gets the message for failure. Exceptions will be thrown if this is a success attempt. + * + * @return The message for failure. + */ + Message getFailureMessage(); + + /** + * Returns whether this attempt is a success. + * + * @return Whether this attempt is a success. + */ + default boolean isSuccess() { + return this instanceof Success; + } + + /** + * Returns whether this attempt is a failure. + * + * @return Whether this attempt is a failure. + */ + default boolean isFailure() { + return this instanceof Failure; + } + + default Attempt thenRun(Runnable runnable) { + runnable.run(); + return this; + } + + default Attempt thenAccept(Consumer> consumer) { + if (this instanceof Success) { + consumer.accept(Either.left(get())); + } else { + consumer.accept(Either.right(getFailureReason())); + } + return this; + } + + /** + * Peeks at the value if this is a success attempt. + * + * @param consumer The consumer with the value. + * @return This attempt. + */ + default Attempt peek(Consumer consumer) { + if (this instanceof Success) { + consumer.accept(get()); + } + return this; + } + + /** + * Maps the value to another value if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ + default Attempt map(Function mapper) { + if (this instanceof Success) { + return new Success<>(mapper.apply(get())); + } else { + return new Failure<>(getFailureReason(), getFailureMessage()); + } + } + + /** + * Maps the value to another attempt if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ + default Attempt map(Supplier mapper) { + if (this instanceof Success) { + return new Success<>(mapper.get()); + } else { + return new Failure<>(getFailureReason(), getFailureMessage()); + } + } + + /** + * Maps the value to another attempt with the same fail reason if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ + default Attempt mapAttempt(Function> mapper) { + if (this instanceof Success) { + return mapper.apply(get()); + } else { + return new Failure<>(getFailureReason(), getFailureMessage()); + } + } + + /** + * Maps the value to another attempt with the same fail reason if this is a success attempt. + * + * @param mapper The mapper. + * @param The type of the new value. + * @return The new attempt. + */ + default Attempt mapAttempt(Supplier> mapper) { + if (this instanceof Success) { + return mapper.get(); + } else { + return new Failure<>(getFailureReason(), getFailureMessage()); + } + } + + /** + * Maps to another attempt with a different fail reason. + * + * @param failureReason The new fail reason. + * @param The type of the new fail reason. + * @return The new attempt. + */ + default Attempt transform(UF failureReason) { + if (this instanceof Success) { + return new Success<>(get()); + } else { + return new Failure<>(failureReason, getFailureMessage()); + } + } + + /** + * Calls either the failure or success function depending on the result type. + * + * @param failureMapper The failure function. + * @param successMapper The success function. + * @param The type of the new value. + * @return The result of the function. + */ + default N fold(Function, N> failureMapper, Function successMapper) { + if (this instanceof Success) { + return successMapper.apply(get()); + } else { + return failureMapper.apply((Failure) this); + } + } + + /** + * Calls the given runnable if this is a success attempt. + * + * @param runnable The runnable. + * @return This attempt. + */ + default Attempt onSuccess(Runnable runnable) { + if (this instanceof Success) { + runnable.run(); + } + return this; + } + + /** + * Calls the given consumer if this is a success attempt. + * + * @param consumer The consumer with the value. + * @return This attempt. + */ + default Attempt onSuccess(Consumer consumer) { + if (this instanceof Success) { + consumer.accept(get()); + } + return this; + } + + /** + * Calls the given consumer if this is a failure attempt. + * + * @param runnable The runnable. + * @return This attempt. + */ + default Attempt onFailure(Runnable runnable) { + if (this instanceof Failure) { + runnable.run(); + } + return this; + } + + /** + * Calls the given consumer if this is a failure attempt. + * + * @param consumer The consumer with the failure instance. + * @return This attempt. + */ + default Attempt onFailure(Consumer> consumer) { + if (this instanceof Failure) { + consumer.accept((Failure) this); + } + return this; + } + + /** + * Calls the given runnable if this is a failure attempt. + * + * @param consumer The consumer with the failure reason. + * @return This attempt. + */ + default Attempt onFailureReason(Consumer consumer) { + if (this instanceof Failure) { + consumer.accept(getFailureReason()); + } + return this; + } + + /** + * Represents a successful attempt with a value. + * + * @param The type of the value. + * @param The type of failure reason. + */ + final class Success implements Attempt { + private final T value; + + Success(T value) { + this.value = value; + } + + @Override + public T get() { + return value; + } + + @Override + public F getFailureReason() { + throw new UnsupportedOperationException("No failure reason as attempt is a success"); + } + + @Override + public Message getFailureMessage() { + throw new UnsupportedOperationException("No failure message as attempt is a success"); + } + + @Override + public String toString() { + return "Success{" + + "value=" + value + + '}'; + } + } + + /** + * Represents a failed attempt with a reason. + * + * @param The type of the value. + * @param The type of failure reason. + */ + final class Failure implements Attempt { + private final F failureReason; + private final Message message; + + Failure(F failureReason, Message message) { + this.failureReason = failureReason; + this.message = message; + } + + @Override + public T get() { + throw new UnsupportedOperationException("No value as attempt is a failure"); + } + + @Override + public F getFailureReason() { + return failureReason; + } + + @Override + public Message getFailureMessage() { + return message; + } + + @Override + public String toString() { + return "Failure{" + + "reason=" + failureReason + + '}'; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/FailureReason.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/FailureReason.java new file mode 100644 index 000000000..c21eede1f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/FailureReason.java @@ -0,0 +1,15 @@ +package org.mvplugins.multiverse.core.utils.result; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; + +/** + * Represents a failure reason for an {@link Attempt}. + */ +public interface FailureReason extends MessageKeyProvider { + default MessageKey getMessageKey() { + return MVCorei18n.GENERIC_FAILURE.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/Result.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/Result.java new file mode 100644 index 000000000..8979b6d42 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/Result.java @@ -0,0 +1,311 @@ +package org.mvplugins.multiverse.core.utils.result; + +import java.util.function.Consumer; +import java.util.function.Function; + +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.locale.message.Message; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; + +/** + * Represents result of an operation with a reason for success or failure that has localized messages. + * + * @param The type of success reason. + * @param The type of failure reason. + */ +public sealed interface Result + permits Result.Success, Result.Failure { + + /** + * Creates a new success result. + * + * @param successReason The reason for success. + * @param replacements The replacements for the success message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new success result. + */ + static Result success( + S successReason, MessageReplacement... replacements) { + return new Success<>(successReason, replacements); + } + + /** + * Creates a new success result. + * + * @param successReason The reason for success. + * @param message The custom message for success. This will override the default message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new success result. + */ + static Result success(S successReason, Message message) { + return new Success<>(successReason, message); + } + + /** + * Creates a new failure result. + * + * @param failureReason The reason for failure. + * @param replacements The replacements for the failure message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new failure result. + */ + static Result failure( + F failureReason, MessageReplacement... replacements) { + return new Failure<>(failureReason, replacements); + } + + /** + * Creates a new failure result. + * + * @param failureReason The reason for failure. + * @param message The custom message for failure. This will override the default message. + * @param The type of failure reason. + * @param The type of success reason. + * @return The new failure result. + */ + static Result failure(F failureReason, Message message) { + return new Failure<>(failureReason, message); + } + + /** + * Returns whether this result is a success. + * + * @return Whether this result is a success. + */ + boolean isSuccess(); + + /** + * Returns whether this result is a failure. + * + * @return Whether this result is a failure. + */ + boolean isFailure(); + + /** + * Returns the reason for success. + * + * @return The reason for success. + */ + S getSuccessReason(); + + /** + * Returns the reason for failure. + * + * @return The reason for failure. + */ + F getFailureReason(); + + /** + * Returns the message for the reason of this result. + * + * @return The message for the reason. + */ + @NotNull Message getReasonMessage(); + + /** + * Executes the given consumer if this result is a success. + * + * @param consumer The consumer with success object. + * @return This result. + */ + default Result onSuccess(Consumer> consumer) { + if (this instanceof Success) { + consumer.accept((Success) this); + } + return this; + } + + /** + * Executes the given consumer if this result is a failure. + * + * @param consumer The consumer with failure object. + * @return This result. + */ + default Result onFailure(Consumer> consumer) { + if (this instanceof Failure) { + consumer.accept((Failure) this); + } + return this; + } + + /** + * Executes the given consumer if this result is a success and the success reason matches the given reason. + * + * @param successReason The success reason to match. + * @param consumer The consumer with success reason. + * @return This result. + */ + default Result onSuccessReason(S successReason, Consumer consumer) { + if (this.isSuccess() && this.getSuccessReason() == successReason) { + consumer.accept(this.getSuccessReason()); + } + return this; + } + + /** + * Executes the given consumer if this result is a failure and the failure reason matches the given reason. + * + * @param failureReason The failure reason to match. + * @param consumer The consumer with failure reason. + * @return This result. + */ + default Result onFailureReason(F failureReason, Consumer consumer) { + if (this.isFailure() && this.getFailureReason() == failureReason) { + consumer.accept(this.getFailureReason()); + } + return this; + } + + /** + * Executes the given function if this result is a success and returns the result of the function. + * + * @param function The function with success object. + * @return The result of the function. + */ + default Result onSuccessThen(Function, Result> function) { + if (this instanceof Success) { + return function.apply((Success) this); + } + return this; + } + + /** + * Executes the given function if this result is a failure and returns the result of the function. + * + * @param function The function with failure object. + * @return The result of the function. + */ + default Result onFailureThen(Function, Result> function) { + if (this instanceof Failure) { + return function.apply((Failure) this); + } + return this; + } + + /** + * Executes either the failure or success function depending on the result type. + * + * @param failureFunc The function with success reason. + * @param successFunc The function with success reason. + * @param The type of the result. + * @return The result of the function. + */ + default R fold(Function, R> failureFunc, Function, R> successFunc) { + if (this instanceof Failure) { + return failureFunc.apply((Failure) this); + } else if (this instanceof Success) { + return successFunc.apply((Success) this); + } + throw new IllegalStateException("Unknown result type: " + this.getClass().getName()); + } + + /** + * The class for a successful result. + * + * @param The type of failure reason. + * @param The type of success reason. + */ + final class Success implements Result { + private final S successReason; + private final Message message; + + Success(S successReason, Message message) { + this.successReason = successReason; + this.message = message; + } + + Success(S successReason, MessageReplacement[] replacements) { + this.successReason = successReason; + this.message = Message.of(successReason, "Success!", replacements); + } + + @Override + public boolean isSuccess() { + return true; + } + + @Override + public boolean isFailure() { + return false; + } + + @Override + public S getSuccessReason() { + return successReason; + } + + @Override + public F getFailureReason() { + throw new UnsupportedOperationException("No reason for success"); + } + + @Override + public @NotNull Message getReasonMessage() { + return message; + } + + @Override + public String toString() { + return "Success{" + + "reason=" + successReason + + '}'; + } + } + + /** + * The class for a failed result. + * + * @param The type of success reason. + * @param The type of failure reason. + */ + final class Failure implements Result { + private final F failureReason; + private final Message message; + + Failure(F failureReason, Message message) { + this.failureReason = failureReason; + this.message = message; + } + + Failure(F failureReason, MessageReplacement[] replacements) { + this.failureReason = failureReason; + this.message = Message.of(failureReason, "Failed!", replacements); + } + + @Override + public boolean isSuccess() { + return false; + } + + @Override + public boolean isFailure() { + return true; + } + + @Override + public S getSuccessReason() { + throw new UnsupportedOperationException("No reason for failure"); + } + + @Override + public F getFailureReason() { + return failureReason; + } + + @Override + public @NotNull Message getReasonMessage() { + return message; + } + + @Override + public String toString() { + return "Failure{" + + "reason=" + failureReason + + '}'; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/ResultChain.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/ResultChain.java new file mode 100644 index 000000000..97cf717bf --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/ResultChain.java @@ -0,0 +1,144 @@ +package org.mvplugins.multiverse.core.utils.result; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import com.google.common.collect.Iterables; +import io.vavr.control.Option; + +import org.mvplugins.multiverse.core.locale.message.Message; + +public class ResultChain { + public static Builder builder() { + return new Builder(true); + } + + public static Builder builder(boolean stopOnFailure) { + return new Builder(stopOnFailure); + } + + private final boolean isSuccess; + private final List> results; + + ResultChain(boolean isSuccess, List> results) { + this.isSuccess = isSuccess; + this.results = results; + } + + public boolean isSuccess() { + return isSuccess; + } + + public boolean isFailure() { + return !isSuccess; + } + + public ResultChain onSuccess(Runnable successRunnable) { + if (isSuccess) { + successRunnable.run(); + } + return this; + } + + public ResultChain onFailure(Runnable failureRunnable) { + if (isFailure()) { + failureRunnable.run(); + } + return this; + } + + public ResultChain onSuccess(Consumer successRunnable) { + if (isSuccess) { + successRunnable.accept(this); + } + return this; + } + + public ResultChain onFailure(Consumer failureRunnable) { + if (isFailure()) { + failureRunnable.accept(this); + } + return this; + } + + public ResultChain onSuccessReason(Class successReasonClass, Consumer successConsumer) { + getSuccessReason(successReasonClass).peek(successConsumer); + return this; + } + + public ResultChain onFailureReason(Class failureReasonClass, Consumer failureConsumer) { + getFailureReason(failureReasonClass).peek(failureConsumer); + return this; + } + + public ResultChain onSuccessReason(S successReason, Runnable successRunnable) { + getSuccessReason(successReason.getClass()).filter(successReason::equals).peek(reason -> successRunnable.run()); + return this; + } + + public Option getSuccessReason(Class successReasonClass) { + if (isFailure()) { + return Option.none(); + } + return Option.ofOptional(results.stream() + .map(Result::getSuccessReason) + .filter(successReasonClass::isInstance) + .map(successReasonClass::cast) + .findFirst()); + } + + public Option getFailureReason(Class failureReasonClass) { + if (isSuccess()) { + return Option.none(); + } + return Option.ofOptional(results.stream() + .map(Result::getFailureReason) + .filter(failureReasonClass::isInstance) + .map(failureReasonClass::cast) + .findFirst()); + } + + public Message getLastResultMessage() { + return Iterables.getLast(results).getReasonMessage(); + } + + @Override + public String toString() { + return "ResultGroup{" + + "isSuccess=" + isSuccess + + ", results={" + results.stream().map(Objects::toString).collect(Collectors.joining(", ")) + "}" + + '}'; + } + + public static class Builder { + private final boolean stopOnFailure; + private final List> results; + + private boolean isSuccess = true; + + public Builder(boolean stopOnFailure) { + this.stopOnFailure = stopOnFailure; + this.results = new ArrayList<>(); + } + + public Builder then(Supplier> resultSupplier) { + if (!isSuccess && stopOnFailure) { + return this; + } + Result result = resultSupplier.get(); + if (result.isFailure()) { + isSuccess = false; + } + results.add(result); + return this; + } + + public ResultChain build() { + return new ResultChain(isSuccess, results); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/result/SuccessReason.java b/src/main/java/org/mvplugins/multiverse/core/utils/result/SuccessReason.java new file mode 100644 index 000000000..685201fc0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/result/SuccessReason.java @@ -0,0 +1,12 @@ +package org.mvplugins.multiverse.core.utils.result; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; + +public interface SuccessReason extends MessageKeyProvider { + default MessageKey getMessageKey() { + return MVCorei18n.GENERIC_SUCCESS.getMessageKey(); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/BitlyURLShortener.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/BitlyURLShortener.java similarity index 88% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/BitlyURLShortener.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/BitlyURLShortener.java index 0d91411ad..2bdac14ce 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/BitlyURLShortener.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/BitlyURLShortener.java @@ -1,12 +1,12 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; - -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +package org.mvplugins.multiverse.core.utils.webpaste; import java.io.IOException; import java.util.Map; +import net.minidev.json.JSONObject; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; + /** * A {@link URLShortener} using {@code bit.ly}. Requires an access token. */ diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GitHubPasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/GitHubPasteService.java similarity index 79% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GitHubPasteService.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/GitHubPasteService.java index bee098dfe..f34b122f0 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GitHubPasteService.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/GitHubPasteService.java @@ -1,13 +1,13 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; - -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +package org.mvplugins.multiverse.core.utils.webpaste; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import net.minidev.json.JSONObject; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; + /** * Pastes to {@code gist.github.com}. Requires an access token with the {@code gist} scope. */ @@ -20,6 +20,7 @@ class GitHubPasteService extends PasteService { GitHubPasteService(boolean isPrivate) { super(GITHUB_POST_REQUEST, ACCESS_TOKEN); this.isPrivate = isPrivate; + //noinspection ConstantValue - this is a placeholder that should be replaced with a real access token if (ACCESS_TOKEN.endsWith("access-token")) { throw new UnsupportedOperationException(); } @@ -60,8 +61,8 @@ String encodeData(Map files) { @Override public String postData(String data) throws PasteFailedException { try { - String stringJSON = this.exec(encodeData(data), ContentType.JSON); - return (String) ((JSONObject) new JSONParser().parse(stringJSON)).get("html_url"); + String stringJson = this.exec(encodeData(data), ContentType.JSON); + return (String) ((JSONObject) new JSONParser(JSONParser.MODE_PERMISSIVE).parse(stringJson)).get("html_url"); } catch (IOException | ParseException e) { throw new PasteFailedException(e); } @@ -74,7 +75,7 @@ public String postData(String data) throws PasteFailedException { public String postData(Map data) throws PasteFailedException { try { String stringJSON = this.exec(encodeData(data), ContentType.JSON); - return (String) ((JSONObject) new JSONParser().parse(stringJSON)).get("html_url"); + return (String) ((JSONObject) new JSONParser(JSONParser.MODE_PERMISSIVE).parse(stringJSON)).get("html_url"); } catch (IOException | ParseException e) { throw new PasteFailedException(e); } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HttpAPIClient.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/HttpAPIClient.java similarity index 63% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HttpAPIClient.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/HttpAPIClient.java index 676ef7cd8..69672a7be 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HttpAPIClient.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/HttpAPIClient.java @@ -1,6 +1,5 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; -import javax.net.ssl.HttpsURLConnection; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -9,6 +8,8 @@ import java.nio.charset.StandardCharsets; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; + /** * HTTP API-client. */ @@ -26,6 +27,7 @@ abstract class HttpAPIClient { enum ContentType { JSON, PLAINTEXT, + PLAINTEXT_YAML, URLENCODED } @@ -44,16 +46,13 @@ enum ContentType { * @return The HTTP Content-Type header that corresponds with the type of data. */ private String getContentHeader(ContentType type) { - switch (type) { - case JSON: - return "application/json; charset=utf-8"; - case PLAINTEXT: - return "text/plain; charset=utf-8"; - case URLENCODED: - return "application/x-www-form-urlencoded; charset=utf-8"; - default: - throw new IllegalArgumentException("Unexpected value: " + type); - } + return switch (type) { + case JSON -> "application/json; charset=utf-8"; + case PLAINTEXT -> "text/plain; charset=utf-8"; + case PLAINTEXT_YAML -> "text/yaml"; + case URLENCODED -> "application/x-www-form-urlencoded; charset=utf-8"; + default -> throw new IllegalArgumentException("Unexpected value: " + type); + }; } /** @@ -80,48 +79,48 @@ private String getContentHeader(ContentType type) { * @throws IOException When the I/O-operation failed. */ final String exec(String payload, ContentType type) throws IOException { - BufferedReader rd = null; - OutputStreamWriter wr = null; + BufferedReader bufferedReader = null; + OutputStreamWriter streamWriter = null; try { - HttpsURLConnection conn = (HttpsURLConnection) new URL(this.url).openConnection(); - conn.setRequestMethod("POST"); - conn.setDoOutput(true); + HttpsURLConnection connection = (HttpsURLConnection) new URL(this.url).openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); // we can receive anything! - conn.addRequestProperty("Accept", "*/*"); + connection.addRequestProperty("Accept", "*/*"); // set a dummy User-Agent - conn.addRequestProperty("User-Agent", "placeholder"); + connection.addRequestProperty("User-Agent", "multiverse/dumps"); // this isn't required, but is technically correct - conn.addRequestProperty("Content-Type", getContentHeader(type)); + connection.addRequestProperty("Content-Type", getContentHeader(type)); // only some API requests require an access token if (this.accessToken != null) { - conn.addRequestProperty("Authorization", this.accessToken); + connection.addRequestProperty("Authorization", this.accessToken); } - wr = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8.newEncoder()); - wr.write(payload); - wr.flush(); + streamWriter = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8.newEncoder()); + streamWriter.write(payload); + streamWriter.flush(); String line; StringBuilder responseString = new StringBuilder(); // this has to be initialized AFTER the data has been flushed! - rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); - while ((line = rd.readLine()) != null) { + while ((line = bufferedReader.readLine()) != null) { responseString.append(line); } return responseString.toString(); } finally { - if (wr != null) { + if (streamWriter != null) { try { - wr.close(); + streamWriter.close(); } catch (IOException ignore) { } } - if (rd != null) { + if (bufferedReader != null) { try { - rd.close(); + bufferedReader.close(); } catch (IOException ignore) { } } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HastebinPasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/McloGsPasteService.java similarity index 52% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HastebinPasteService.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/McloGsPasteService.java index e1fa7272c..7a7104be1 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/HastebinPasteService.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/McloGsPasteService.java @@ -1,28 +1,23 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; - -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +package org.mvplugins.multiverse.core.utils.webpaste; import java.io.IOException; import java.util.Map; -/** - * Pastes to {@code hastebin.com}. - */ -class HastebinPasteService extends PasteService { - private static final String HASTEBIN_POST_REQUEST = "https://hastebin.com/documents"; +import net.minidev.json.JSONObject; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; + +public class McloGsPasteService extends PasteService { - HastebinPasteService() { - super(HASTEBIN_POST_REQUEST); + private static final String MCLOGS_POST_REQUEST = "https://api.mclo.gs/1/log"; + + McloGsPasteService() { + super(MCLOGS_POST_REQUEST); } - /** - * {@inheritDoc} - */ @Override String encodeData(String data) { - return data; + return "content=" + data; } /** @@ -33,35 +28,26 @@ String encodeData(Map data) { throw new UnsupportedOperationException(); } - /** - * {@inheritDoc} - */ @Override public String postData(String data) throws PasteFailedException { try { - String stringJSON = this.exec(encodeData(data), ContentType.PLAINTEXT); - return "https://hastebin.com/" + ((JSONObject) new JSONParser().parse(stringJSON)).get("key"); + String stringJSON = this.exec(encodeData(data), ContentType.URLENCODED); // Execute request + return String.valueOf(((JSONObject) new JSONParser().parse(stringJSON)).get("url")); // Interpret result } catch (IOException | ParseException e) { throw new PasteFailedException(e); } } - /** - * {@inheritDoc} - */ @Override public String postData(Map data) throws PasteFailedException { try { - String stringJSON = this.exec(encodeData(data), ContentType.PLAINTEXT); - return "https://hastebin.com/" + ((JSONObject) new JSONParser().parse(stringJSON)).get("key"); + String stringJSON = this.exec(encodeData(data), ContentType.JSON); // Execute request + return String.valueOf(((JSONObject) new JSONParser().parse(stringJSON)).get("url")); // Interpret result } catch (IOException | ParseException e) { throw new PasteFailedException(e); } } - /** - * {@inheritDoc} - */ @Override public boolean supportsMultiFile() { return false; diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteFailedException.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteFailedException.java similarity index 80% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteFailedException.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteFailedException.java index 82792f499..24ce6e122 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteFailedException.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteFailedException.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * Thrown when pasting fails. diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteGGPasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteGGPasteService.java similarity index 92% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteGGPasteService.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteGGPasteService.java index 956d71c1c..e92de826c 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteGGPasteService.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteGGPasteService.java @@ -1,14 +1,14 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +package org.mvplugins.multiverse.core.utils.webpaste; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; + /** * Pastes to {@code paste.gg}. */ diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteService.java similarity index 96% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteService.java index a6bc15c4f..95fd93f92 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteService.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; import java.util.Map; diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceFactory.java similarity index 50% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceFactory.java index f6f63a1ce..5d47a0168 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceFactory.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * Used to construct {@link PasteService}s. @@ -13,17 +13,12 @@ private PasteServiceFactory() { } * @return The newly created {@link PasteService}. */ public static PasteService getService(PasteServiceType type, boolean isPrivate) { - switch(type) { - case PASTEGG: - return new PasteGGPasteService(isPrivate); - case PASTEBIN: - return new PastebinPasteService(isPrivate); - case HASTEBIN: - return new HastebinPasteService(); - case GITHUB: - return new GitHubPasteService(isPrivate); - default: - return null; - } + return switch (type) { + case PASTEGG -> new PasteGGPasteService(isPrivate); + case PASTEBIN -> new PastebinPasteService(isPrivate); + case PASTESDEV -> new PastesDevPasteService(); + case GITHUB -> new GitHubPasteService(isPrivate); + case MCLOGS -> new McloGsPasteService(); + }; } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceType.java similarity index 66% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceType.java index 09424c0b5..9d8ae50cd 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PasteServiceType.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * An enum containing all known {@link PasteService}s. @@ -16,11 +16,15 @@ public enum PasteServiceType { */ PASTEBIN, /** - * @see HastebinPasteService + * @see PastesDevPasteService */ - HASTEBIN, + PASTESDEV, /** * @see GitHubPasteService */ - GITHUB + GITHUB, + /** + * @see McloGsPasteService + */ + MCLOGS } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastebinPasteService.java similarity index 58% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastebinPasteService.java index eff401933..18a39c5f8 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastebinPasteService.java @@ -1,8 +1,8 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -22,16 +22,12 @@ class PastebinPasteService extends PasteService { */ @Override String encodeData(String data) { - try { - return URLEncoder.encode("api_dev_key", "UTF-8") + "=" + URLEncoder.encode("d61d68d31e8e0392b59b50b277411c71", "UTF-8") + - "&" + URLEncoder.encode("api_option", "UTF-8") + "=" + URLEncoder.encode("paste", "UTF-8") + - "&" + URLEncoder.encode("api_paste_code", "UTF-8") + "=" + URLEncoder.encode(data, "UTF-8") + - "&" + URLEncoder.encode("api_paste_private", "UTF-8") + "=" + URLEncoder.encode(this.isPrivate ? "1" : "0", "UTF-8") + - "&" + URLEncoder.encode("api_paste_format", "UTF-8") + "=" + URLEncoder.encode("yaml", "UTF-8") + - "&" + URLEncoder.encode("api_paste_name", "UTF-8") + "=" + URLEncoder.encode("Multiverse-Core Debug Info", "UTF-8"); - } catch (UnsupportedEncodingException e) { - return ""; // should never hit here - } + return URLEncoder.encode("api_dev_key", StandardCharsets.UTF_8) + "=" + URLEncoder.encode("144d820f540e79a1242b32cb9ab274c6", StandardCharsets.UTF_8) + + "&" + URLEncoder.encode("api_option", StandardCharsets.UTF_8) + "=" + URLEncoder.encode("paste", StandardCharsets.UTF_8) + + "&" + URLEncoder.encode("api_paste_code", StandardCharsets.UTF_8) + "=" + URLEncoder.encode(data, StandardCharsets.UTF_8) + + "&" + URLEncoder.encode("api_paste_private", StandardCharsets.UTF_8) + "=" + URLEncoder.encode(this.isPrivate ? "1" : "0", StandardCharsets.UTF_8) + + "&" + URLEncoder.encode("api_paste_format", StandardCharsets.UTF_8) + "=" + URLEncoder.encode("yaml", StandardCharsets.UTF_8) + + "&" + URLEncoder.encode("api_paste_name", StandardCharsets.UTF_8) + "=" + URLEncoder.encode("Multiverse-Core Debug Info", StandardCharsets.UTF_8); } /** diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastesDevPasteService.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastesDevPasteService.java new file mode 100644 index 000000000..57501345a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/PastesDevPasteService.java @@ -0,0 +1,64 @@ +package org.mvplugins.multiverse.core.utils.webpaste; + +import java.io.IOException; +import java.util.Map; + +import net.minidev.json.JSONObject; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; + +/** + * Pastes to {@code hastebin.com}. + */ +class PastesDevPasteService extends PasteService { + private static final String PASTESDEV_POST_REQUEST = "https://api.pastes.dev/post"; + + PastesDevPasteService() { + super(PASTESDEV_POST_REQUEST); + } + + /** + * {@inheritDoc} + */ + @Override + String encodeData(String data) { + return data; + } + + /** + * {@inheritDoc} + */ + @Override + String encodeData(Map data) { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + */ + @Override + public String postData(String data) throws PasteFailedException { + try { + String stringJSON = this.exec(encodeData(data), ContentType.PLAINTEXT_YAML); + return "https://pastes.dev/" + ((JSONObject) new JSONParser().parse(stringJSON)).get("key"); + } catch (IOException | ParseException e) { + throw new PasteFailedException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String postData(Map data) throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean supportsMultiFile() { + return false; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortener.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortener.java similarity index 92% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortener.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortener.java index bde8ff1d8..f440b553d 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortener.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortener.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * An interface to a web-based URL Shortener. Classes extending this should diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerFactory.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerFactory.java similarity index 91% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerFactory.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerFactory.java index c0f3cafac..88c725e89 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerFactory.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerFactory.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * Used to construct {@link URLShortener}s. diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerType.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerType.java similarity index 78% rename from src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerType.java rename to src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerType.java index d2c809f51..02f7ad604 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/URLShortenerType.java +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/URLShortenerType.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.utils.webpaste; +package org.mvplugins.multiverse.core.utils.webpaste; /** * An enum containing all known {@link URLShortener}s. diff --git a/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/package-info.java b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/package-info.java new file mode 100644 index 000000000..701646b21 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/utils/webpaste/package-info.java @@ -0,0 +1,4 @@ +/** + * This package contains webpaste-utilities. + */ +package org.mvplugins.multiverse.core.utils.webpaste; diff --git a/src/main/java/com/onarandombox/MultiverseCore/enums/AllowedPortalType.java b/src/main/java/org/mvplugins/multiverse/core/world/AllowedPortalType.java similarity index 83% rename from src/main/java/com/onarandombox/MultiverseCore/enums/AllowedPortalType.java rename to src/main/java/org/mvplugins/multiverse/core/world/AllowedPortalType.java index 99e5a8239..8bda8fdec 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/enums/AllowedPortalType.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/AllowedPortalType.java @@ -5,7 +5,7 @@ * with this project. * ******************************************************************************/ -package com.onarandombox.MultiverseCore.enums; +package org.mvplugins.multiverse.core.world; import org.bukkit.PortalType; @@ -17,20 +17,23 @@ public enum AllowedPortalType { * No portals are allowed. */ NONE(PortalType.CUSTOM), + /** * All portal types are allowed. */ ALL(PortalType.CUSTOM), + /** * Only Nether style portals are allowed. */ NETHER(PortalType.NETHER), + /** * Only Ender style portals are allowed. */ END(PortalType.ENDER); - private PortalType type; + private final PortalType type; AllowedPortalType(PortalType type) { this.type = type; @@ -38,12 +41,19 @@ public enum AllowedPortalType { /** * Gets the text. + * * @return The text. */ public PortalType getActualPortalType() { return this.type; } + /** + * Checks if the given portal type is allowed. + * + * @param portalType The portal type. + * @return True if allowed, else false. + */ public boolean isPortalAllowed(PortalType portalType) { return this != NONE && (getActualPortalType() == portalType || this == ALL); } diff --git a/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java b/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java new file mode 100644 index 000000000..3f71b964e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/LoadedMultiverseWorld.java @@ -0,0 +1,174 @@ +package org.mvplugins.multiverse.core.world; + +import java.util.List; +import java.util.UUID; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.entity.Player; +import org.bukkit.generator.BiomeProvider; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.teleportation.BlockSafety; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.world.location.NullLocation; +import org.mvplugins.multiverse.core.world.location.SpawnLocation; + +/** + * Extension of {@link MultiverseWorld} that represents a world that is currently loaded with bukkit world object. + */ +public final class LoadedMultiverseWorld extends MultiverseWorld { + + private final UUID worldUid; + + private final BlockSafety blockSafety; + private final LocationManipulation locationManipulation; + + LoadedMultiverseWorld( + @NotNull World world, + @NotNull WorldConfig worldConfig, + @NotNull BlockSafety blockSafety, + @NotNull LocationManipulation locationManipulation) { + super(world.getName(), worldConfig); + this.worldUid = world.getUID(); + this.blockSafety = blockSafety; + this.locationManipulation = locationManipulation; + + setupWorldConfig(world); + setupSpawnLocation(world); + } + + private void setupWorldConfig(World world) { + worldConfig.setMVWorld(this); + worldConfig.load(); + BiomeProvider biomeProvider = world.getBiomeProvider(); + if (biomeProvider instanceof SingleBiomeProvider singleBiomeProvider) { + worldConfig.setBiome(singleBiomeProvider.getBiome()); + } + worldConfig.setEnvironment(world.getEnvironment()); + worldConfig.setSeed(world.getSeed()); + } + + private void setupSpawnLocation(World world) { + Location spawnLocation = worldConfig.getSpawnLocation(); + if (spawnLocation == null || spawnLocation instanceof NullLocation) { + SpawnLocation newLocation = new SpawnLocation(readSpawnFromWorld(world)); + worldConfig.setSpawnLocation(newLocation); + } + } + + private Location readSpawnFromWorld(World world) { + Location location = world.getSpawnLocation(); + + // Verify that location was safe + if (blockSafety.canSpawnAtLocationSafely(location)) { + return location; + } + + if (!this.getAdjustSpawn()) { + Logging.fine("Spawn location from world.dat file was unsafe!!"); + Logging.fine("NOT adjusting spawn for '" + this.getAlias() + "' because you told me not to."); + Logging.fine("To turn on spawn adjustment for this world simply type:"); + Logging.fine("/mvm set adjustspawn true " + this.getAlias()); + return location; + } + + // The location is not safe, so we need to find a better one. + Logging.warning("Spawn location from world.dat file was unsafe. Adjusting..."); + Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location)); + Location newSpawn = blockSafety.findSafeSpawnLocation(location); + // I think we could also do this, as I think this is what Notch does. + // Not sure how it will work in the nether... + //Location newSpawn = this.spawnLocation.getWorld().getHighestBlockAt(this.spawnLocation).getLocation(); + if (newSpawn != null) { + Logging.info("New Spawn for '%s' is located at: %s", + this.getName(), locationManipulation.locationToString(newSpawn)); + return newSpawn; + } + + // If it's a standard end world, let's check in a better place: + Logging.fine("Checking for a safe location using top block..."); + Location newerSpawn; + newerSpawn = blockSafety.getTopBlock(new Location(world, 0, 0, 0)); + if (newerSpawn != null) { + Logging.info("New Spawn for '%s' is located at: %s", + this.getName(), locationManipulation.locationToString(newerSpawn)); + return newerSpawn; + } + + Logging.severe("Safe spawn NOT found!!!"); + return location; + } + + /** + * Gets the Unique ID of this world. + * + * @return Unique ID of this world. + */ + public UUID getUID() { + return worldUid; + } + + /** + * Gets the Bukkit world object that this world describes. + * + * @return Bukkit world object. + */ + public Option getBukkitWorld() { + return Option.of(Bukkit.getWorld(worldUid)); + } + + /** + * Gets the type of this world. + * + * @return Type of this world. + */ + public Option getWorldType() { + //noinspection deprecation + return getBukkitWorld().map(World::getWorldType); + } + + /** + * Gets whether or not structures are being generated. + * + * @return True if structures are being generated. + */ + public Option canGenerateStructures() { + return getBukkitWorld().map(World::canGenerateStructures); + } + + /** + * Get a list of all players in this World. + * + * @return A list of all Players currently residing in this world + */ + public Option> getPlayers() { + return getBukkitWorld().map(World::getPlayers); + } + + /** + * {@inheritDoc} + */ + @Override + void setWorldConfig(WorldConfig worldConfig) { + super.setWorldConfig(worldConfig); + setupWorldConfig(getBukkitWorld().get()); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "LoadedMultiverseWorld{" + + "name='" + worldName + "', " + + "env='" + getEnvironment() + "', " + + "type='" + getWorldType().getOrNull() + "', " + + "gen='" + getGenerator() + "'" + + '}'; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java b/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java new file mode 100644 index 000000000..17cf63946 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/MultiverseWorld.java @@ -0,0 +1,763 @@ +package org.mvplugins.multiverse.core.world; + +import java.util.List; + +import com.google.common.base.Strings; +import io.vavr.control.Try; +import org.bukkit.Bukkit; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.world.location.SpawnLocation; + +/** + * Represents a world handled by Multiverse which has all the custom properties provided by Multiverse. + */ +public sealed class MultiverseWorld permits LoadedMultiverseWorld { + /** + * This world's name. + */ + protected final String worldName; + + /** + * This world's configuration. + */ + protected WorldConfig worldConfig; + + MultiverseWorld(String worldName, WorldConfig worldConfig) { + this.worldName = worldName; + this.worldConfig = worldConfig; + this.worldConfig.setMVWorld(this); + } + + /** + * Gets the name of this world. The name cannot be changed. + *
+ * Note for plugin developers: Usually {@link #getAlias()} + * is what you want to use instead of this method. + * + * @return The name of the world as a String. + */ + public String getName() { + return worldName; + } + + /** + * Gets whether this world is loaded. + * + * @return True if the world is loaded, else false. + */ + public boolean isLoaded() { + return worldConfig.isLoadedWorld(); + } + + /** + * Gets the properties handler of this world. + * + * @return The properties handler of this world. + */ + public StringPropertyHandle getStringPropertyHandle() { + return worldConfig.getStringPropertyHandle(); + } + + /** + * Gets whether or not Multiverse should auto-adjust the spawn for this world. + * + * @return True if Multiverse should adjust the spawn, false if not. + */ + public boolean getAdjustSpawn() { + return worldConfig.getAdjustSpawn(); + } + + /** + * Sets whether or not Multiverse should auto-adjust the spawn for this world. + * + * @param adjustSpawn True if multiverse should adjust the spawn, false if not. + * @return Result of setting property. + */ + public Try setAdjustSpawn(boolean adjustSpawn) { + return worldConfig.setAdjustSpawn(adjustSpawn); + } + + /** + * Gets the alias of this world. + *
+ * This alias allows users to have a world named "world" but show up in the list as "FernIsland" + * + * @return The alias of the world as a String. + */ + public String getAlias() { + return Strings.isNullOrEmpty(worldConfig.getAlias()) ? worldName : worldConfig.getAlias(); + } + + /** + * Sets the alias of the world. + * + * @param alias A string that is the new alias. + * @return Result of setting property. + */ + public Try setAlias(String alias) { + return worldConfig.setAlias(alias); + } + + /** + * Whether or not players are allowed to fly in this world. + * + * @return True if players allowed to fly in this world. + */ + public boolean getAllowFlight() { + return worldConfig.getAllowFlight(); + } + + /** + * Sets whether or not players are allowed to fly in this world. + * + * @param allowFlight True to allow flight in this world. + * @return Result of setting property. + */ + public Try setAllowFlight(boolean allowFlight) { + return worldConfig.setAllowFlight(allowFlight); + } + + /** + * Gets whether weather is enabled in this world. + * + * @return True if weather events will occur, false if not. + */ + public boolean getAllowWeather() { + return worldConfig.getAllowWeather(); + } + + /** + * Sets whether or not there will be weather events in a given world. + * If set to false, Multiverse will disable the weather in the world immediately. + * + * @param allowWeather True if weather events should occur in a world, false if not. + * @return Result of setting property. + */ + public Try setAllowWeather(boolean allowWeather) { + return worldConfig.setAllowWeather(allowWeather); + } + + /** + * Gets whether or not a player who dies in this world will respawn in their + * anchor or follow the normal respawn pattern. + * + * @return True if players dying in this world should respawn at their anchor. + */ + public boolean getAnchorRespawn() { + return worldConfig.getAnchorRespawn(); + } + + /** + * Sets whether or not a player who dies in this world will respawn in their + * anchor or follow the normal respawn pattern. + *
+ * True is default. + * + * @param anchorSpawn True if players dying in this world respawn at their anchor. + * @return Result of setting property. + */ + public Try setAnchorSpawn(boolean anchorSpawn) { + return worldConfig.setAnchorSpawn(anchorSpawn); + } + + /** + * Gets whether or not a world will auto-heal players if the difficulty is on peaceful. + * + * @return True if the world should heal (default), false if not. + */ + public boolean getAutoHeal() { + return worldConfig.getAutoHeal(); + } + + /** + * Sets whether or not a world will auto-heal players if the difficulty is on peaceful. + * + * @param autoHeal True if the world will heal. + * @return Result of setting property. + */ + public Try setAutoHeal(boolean autoHeal) { + return worldConfig.setAutoHeal(autoHeal); + } + + /** + * Gets whether or not Multiverse should auto-load this world. + * + * @return True if Multiverse should auto-load this world. + */ + public boolean getAutoLoad() { + return worldConfig.getAutoLoad(); + } + + /** + * Sets whether or not Multiverse should auto-load this world. + *
+ * True is default. + * + * @param autoLoad True if multiverse should autoload this world the spawn, false if not. + * @return Result of setting property. + */ + public Try setAutoLoad(boolean autoLoad) { + return worldConfig.setAutoLoad(autoLoad); + } + + /** + * Gets whether or not a player who dies in this world will respawn in their + * bed or follow the normal respawn pattern. + * + * @return True if players dying in this world should respawn at their bed. + */ + public boolean getBedRespawn() { + return worldConfig.getBedRespawn(); + } + + /** + * Sets whether or not a player who dies in this world will respawn in their + * bed or follow the normal respawn pattern. + *
+ * True is default. + * + * @param bedRespawn True if players dying in this world respawn at their bed. + * @return Result of setting property. + */ + public Try setBedRespawn(boolean bedRespawn) { + return worldConfig.setBedRespawn(bedRespawn); + } + + /** + * Gets the single biome used for this world. This may be null, in which case the biome from the generator will be used. + * If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @return The biome used for this world + */ + public @Nullable Biome getBiome() { + return worldConfig.getBiome(); + } + + /** + * Gets the type of currency that will be used when users enter this world. A value of null indicates a non-item + * based currency is used. + * + * @return The type of currency that will be used when users enter this world. + */ + public Material getCurrency() { + return worldConfig.getEntryFeeCurrency(); + } + + /** + * Sets the type of item that will be required given the price is not 0. + * Use a value of null to specify a non-item based currency. + * + * @param currency The Type of currency that will be used when users enter this world. + * @return Result of setting property. + */ + public Try setCurrency(Material currency) { + return worldConfig.setEntryFeeCurrency(currency); + } + + /** + * Gets the difficulty of this world. + * + * @return The difficulty of this world. + */ + public Difficulty getDifficulty() { + return worldConfig.getDifficulty(); + } + + /** + * Sets the difficulty of this world and returns {@code true} on success. + * Valid string values are either an integer of difficulty(0-3) or + * the name that resides in the Bukkit enum, ex. PEACEFUL + * + * @param difficulty The new difficulty. + * @return Result of setting property. + */ + public Try setDifficulty(Difficulty difficulty) { + return worldConfig.setDifficulty(difficulty); + } + + /** + * Gets the environment of this world. You cannot change this after world creation. + * + * @return A {@link World.Environment}. + */ + public World.Environment getEnvironment() { + return worldConfig.getEnvironment(); + } + + /** + * Gets the GameMode of this world. + * + * @return The GameMode of this world. + */ + public GameMode getGameMode() { + return worldConfig.getGameMode(); + } + + /** + * Sets the game mode of this world. + * + * @param gameMode The new {@link GameMode}. + * @return Result of setting property. + */ + public Try setGameMode(GameMode gameMode) { + return worldConfig.setGameMode(gameMode); + } + + /** + * Gets the generator string of this world. You cannot change this after world creation. + * + * @return The name of the generator. + */ + public String getGenerator() { + return worldConfig.getGenerator(); + } + + /** + * Gets whether or not this world will display in chat, mvw and mvl regardless if a user has the + * access permissions to go to this world. + * + * @return True if the world will be hidden, false if not. + */ + public boolean isHidden() { + return worldConfig.isHidden(); + } + + /** + * Sets whether or not this world will display in chat, mvw and mvl regardless if a user has the + * access permissions to go to this world. + * + * @param hidden True if the world should be hidden, false if not. + * @return Result of setting property. + */ + public Try setHidden(boolean hidden) { + return worldConfig.setHidden(hidden); + } + + /** + * Gets whether or not the hunger level of players will go down in a world. + * + * @return True if it will go down, false if it will remain steady. + */ + public boolean getHunger() { + return worldConfig.getHunger(); + } + + /** + * Sets whether or not the hunger level of players will go down in a world. + * + * @param hunger True if hunger will go down, false to keep it at the level they entered a world with. + * @return Result of setting property. + */ + public Try setHunger(boolean hunger) { + return worldConfig.setHunger(hunger); + } + + /** + * Gets whether or not CraftBukkit is keeping the chunks for this world in memory. + * + * @return True if CraftBukkit is keeping spawn chunks in memory. + */ + public boolean getKeepSpawnInMemory() { + return worldConfig.getKeepSpawnInMemory(); + } + + /** + * If true, tells Craftbukkit to keep a worlds spawn chunks loaded in memory (default: true) + * If not, CraftBukkit will attempt to free memory when players have not used that world. + * This will not happen immediately. + * + * @param keepSpawnInMemory If true, CraftBukkit will keep the spawn chunks loaded in memory. + * @return Result of setting property. + */ + public Try setKeepSpawnInMemory(boolean keepSpawnInMemory) { + return worldConfig.setKeepSpawnInMemory(keepSpawnInMemory); + } + + /** + * Gets the player limit for this world after which players without an override + * permission node will not be allowed in. A value of -1 or less signifies no limit + * + * @return The player limit + */ + public int getPlayerLimit() { + return worldConfig.getPlayerLimit(); + } + + /** + * Sets the player limit for this world after which players without an override + * permission node will not be allowed in. A value of -1 or less signifies no limit + * + * @param playerLimit The new limit + * @return Result of setting property. + */ + public Try setPlayerLimit(int playerLimit) { + return worldConfig.setPlayerLimit(playerLimit); + } + + /** + * Gets which type(s) of portals are allowed to be constructed in this world. + * + * @return The type of portals that are allowed. + */ + public AllowedPortalType getPortalForm() { + return worldConfig.getPortalForm(); + } + + /** + * Sets The types of portals that are allowed in this world. + * + * @param portalForm The type of portals allowed in this world. + * @return Result of setting property. + */ + public Try setPortalForm(AllowedPortalType portalForm) { + return worldConfig.setPortalForm(portalForm); + } + + /** + * Gets if entry fee is needed when entering this world + * + * @return True if entry fee is needed + */ + public boolean isEntryFeeEnabled() { + return worldConfig.isEntryFeeEnabled(); + } + + /** + * Sets if entry fee is needed when entering this world + * + * @param entryFeeEnabled True to enable use of entry fee + * @return Result of setting property. + */ + public Try setEntryFeeEnabled(boolean entryFeeEnabled) { + return worldConfig.setEntryFeeEnabled(entryFeeEnabled); + } + + /** + * Gets the amount of currency it requires to enter this world. + * + * @return The amount it costs to enter this world. + */ + public double getPrice() { + return worldConfig.getEntryFeeAmount(); + } + + /** + * Sets the price for entry to this world. + * You can think of this like an amount. + * The type can be set with {@link #setCurrency(Material)} + * + * @param price The Amount of money/item to enter the world. + * @return Result of setting property. + */ + public Try setPrice(double price) { + return worldConfig.setEntryFeeAmount(price); + } + + /** + * Gets whether or not PVP is enabled in this world in some form (fake or not). + * + * @return True if players can take damage from other players. + */ + public boolean getPvp() { + return worldConfig.getPvp(); + } + + /** + * Turn pvp on or off. This setting is used to set the world's PVP mode. + * + * @param pvp True to enable PVP damage, false to disable it. + * @return Result of setting property. + */ + public Try setPvp(boolean pvp) { + return worldConfig.setPvp(pvp); + } + + /** + * Gets the world name players will respawn in if they die in this one. + * + * @return A world name that exists on the server. + */ + public String getRespawnWorldName() { + return worldConfig.getRespawnWorld(); + } + + /** + * Gets the world players will respawn in if they die in this one. + * + * @return A world that exists on the server. + */ + public @Nullable World getRespawnWorld() { + return Bukkit.getWorld(worldConfig.getRespawnWorld()); + } + + /** + * Sets the world players will respawn in if they die in this one. + * Returns true upon success, false upon failure. + * + * @param respawnWorld The name of a world that exists on the server. + * @return Result of setting property. + */ + public Try setRespawnWorld(World respawnWorld) { + return worldConfig.setRespawnWorld(respawnWorld.getName()); + } + + /** + * Sets the world players will respawn in if they die in this one. + * Returns true upon success, false upon failure. + * + * @param respawnWorld The name of a world that exists on the server. + * @return Result of setting property. + */ + public Try setRespawnWorld(MultiverseWorld respawnWorld) { + return worldConfig.setRespawnWorld(respawnWorld.getName()); + } + + /** + * Sets the world players will respawn in if they die in this one. + * Returns true upon success, false upon failure. + * + * @param respawnWorld The name of a world that exists on the server. + * @return Result of setting property. + */ + public Try setRespawnWorld(String respawnWorld) { + return worldConfig.setRespawnWorld(respawnWorld); + } + + /** + * Gets the scaling value of this world.Really only has an effect if you use + * Multiverse-NetherPortals. + * + * @return This world's non-negative, non-zero scale. + */ + public double getScale() { + return worldConfig.getScale(); + } + + /** + * Sets the scale of this world. Really only has an effect if you use + * Multiverse-NetherPortals. + * + * @param scale A scaling value, cannot be negative or 0. + * @return Result of setting property. + */ + public Try setScale(double scale) { + return worldConfig.setScale(scale); + } + + /** + * Gets the world seed of this world. This cannot be changed after world creation. + * + * @return The Long version of the seed. + */ + public long getSeed() { + return worldConfig.getSeed(); + } + + /** + * Gets the spawn location of this world. + * + * @return The spawn location of this world. + */ + public SpawnLocation getSpawnLocation() { + return worldConfig.getSpawnLocation(); + } + + /** + * Sets the spawn location for a world. + * + * @param spawnLocation The spawn location for a world. + * @return Result of setting property. + */ + public Try setSpawnLocation(Location spawnLocation) { + return setSpawnLocation(new SpawnLocation(spawnLocation)); + } + + /** + * Sets the spawn location for a world. + * + * @param spawnLocation The spawn location for a world. + * @return Result of setting property. + */ + public Try setSpawnLocation(SpawnLocation spawnLocation) { + //todo: Maybe check of safe location if adjust spawn is enabled + return worldConfig.setSpawnLocation(spawnLocation); + } + + /** + * Gets whether or not animals are allowed to spawn in this world. + * + * @return True if ANY animal can, false if no animals can spawn. + */ + public boolean getSpawningAnimals() { + return worldConfig.getSpawningAnimals(); + } + + /** + * Sets whether or not animals can spawn. + *
+ * If there are values in {@link #getSpawningAnimalsExceptions()} and this is false, + * those animals become the exceptions, and will spawn + * + * @param spawningAnimals True to allow spawning of monsters, false to prevent. + * @return Result of setting property. + */ + public Try setSpawningAnimals(boolean spawningAnimals) { + return worldConfig.setSpawningAnimals(spawningAnimals); + } + + /** + * Gets the amount of ticks between animal spawns. + * + * @return The amount of ticks between animal spawns. + */ + public int getSpawningAnimalsTicks() { + return worldConfig.getSpawningAnimalsTicks(); + } + + /** + * Sets the amount of ticks between animal spawns. Set to -1 to use bukkit default. + * + * @param spawningAnimalsAmount The amount of ticks between animal spawns. + * @return Result of setting property. + */ + public Try setSpawningAnimalsTicks(int spawningAnimalsAmount) { + return worldConfig.setSpawningAnimalsTicks(spawningAnimalsAmount); + } + + /** + * Returns a list of animals. This list always negates the {@link #getSpawningAnimals()} result. + * + * @return A list of animals that will spawn if {@link #getSpawningAnimals()} is false. + */ + public List getSpawningAnimalsExceptions() { + return worldConfig.getSpawningAnimalsExceptions(); + } + + /** + * Sets the list of animals that will spawn if {@link #getSpawningAnimals()} is false. + * + * @param spawningAnimalsExceptions The list of animals that will spawn if {@link #getSpawningAnimals()} is false. + * @return Result of setting property. + */ + public Try setSpawningAnimalsExceptions(List spawningAnimalsExceptions) { + return worldConfig.setSpawningAnimalsExceptions(spawningAnimalsExceptions); + } + + /** + * Gets whether or not monsters are allowed to spawn in this world. + * + * @return True if ANY monster can, false if no monsters can spawn. + */ + public boolean getSpawningMonsters() { + return worldConfig.getSpawningMonsters(); + } + + /** + * Sets whether or not monsters can spawn. + * If there are values in {@link #getSpawningMonstersExceptions()} and this is false, + * those monsters become the exceptions, and will spawn + * + * @param spawningMonsters True to allow spawning of monsters, false to prevent. + * @return Result of setting property. + */ + public Try setSpawningMonsters(boolean spawningMonsters) { + return worldConfig.setSpawningMonsters(spawningMonsters); + } + + /** + * Gets the amount of ticks between monster spawns. + * + * @return The amount of ticks between monster spawns. + */ + public int getSpawningMonstersTicks() { + return worldConfig.getSpawningMonstersTicks(); + } + + /** + * Sets the amount of ticks between monster spawns. Set to -1 to use bukkit default. + * + * @param spawningMonstersAmount The amount of ticks between monster spawns. + * @return Result of setting property. + */ + public Try setSpawningMonstersTicks(int spawningMonstersAmount) { + return worldConfig.setSpawningMonstersTicks(spawningMonstersAmount); + } + + /** + * Returns a list of monsters. This list always negates the {@link #getSpawningMonsters()} result. + * + * @return A list of monsters that will spawn if {@link #getSpawningMonsters()} is false. + */ + public List getSpawningMonstersExceptions() { + return worldConfig.getSpawningMonstersExceptions(); + } + + /** + * Sets the list of monsters that will spawn if {@link #getSpawningMonsters()} is false. + * + * @param spawningMonstersExceptions The list of monsters that will spawn if {@link #getSpawningMonsters()} + * is false. + * @return Result of setting property. + */ + public Try setSpawningMonstersExceptions(List spawningMonstersExceptions) { + return worldConfig.setSpawningMonstersExceptions(spawningMonstersExceptions); + } + + /** + * Gets a list of all the worlds that players CANNOT travel to from this world,regardless of their access + * permissions. + * + * @return A List of world names. + */ + public List getWorldBlacklist() { + return worldConfig.getWorldBlacklist(); + } + + /** + * Sets the list of worlds that players CANNOT travel to from this world, regardless of their access permissions. + * + * @param worldBlacklist A List of world names. + * @return Result of setting property. + */ + public Try setWorldBlacklist(List worldBlacklist) { + return worldConfig.setWorldBlacklist(worldBlacklist); + } + + /** + * Gets the world config. Only for internal use. + * + * @return The world config. + */ + WorldConfig getWorldConfig() { + return worldConfig; + } + + /** + * Sets the world config. Only for internal use. + * + * @param worldConfig The world config. + */ + void setWorldConfig(WorldConfig worldConfig) { + this.worldConfig = worldConfig; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "MultiverseWorld{" + + "name='" + worldName + "', " + + "env='" + getEnvironment() + "', " + + "gen='" + getGenerator() + "'" + + '}'; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/NewAndRemovedWorlds.java b/src/main/java/org/mvplugins/multiverse/core/world/NewAndRemovedWorlds.java new file mode 100644 index 000000000..b7caf525f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/NewAndRemovedWorlds.java @@ -0,0 +1,12 @@ +package org.mvplugins.multiverse.core.world; + +import java.util.List; + +/** + * A record containing a list of the new WorldConfigs added and a list of the worlds removed from the config. + * + * @param newWorlds List of the new WorldConfigs added + * @param removedWorlds List of the worlds removed from the config + */ +record NewAndRemovedWorlds(List newWorlds, List removedWorlds) { +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/SingleBiomeProvider.java b/src/main/java/org/mvplugins/multiverse/core/world/SingleBiomeProvider.java new file mode 100644 index 000000000..a8dffcb2b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/SingleBiomeProvider.java @@ -0,0 +1,37 @@ +package org.mvplugins.multiverse.core.world; + +import org.bukkit.WorldCreator; +import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Helps create a world with only 1 type of Biome specified. Used in {@link WorldCreator#biomeProvider(BiomeProvider)} + */ +final class SingleBiomeProvider extends BiomeProvider { + + private final Biome biome; + private final List biomes; + + public SingleBiomeProvider(Biome biome) { + this.biome = biome; + this.biomes = List.of(biome); + } + + @Override + public @NotNull Biome getBiome(@NotNull WorldInfo worldInfo, int x, int y, int z) { + return this.biome; + } + + @Override + public @NotNull List getBiomes(@NotNull WorldInfo worldInfo) { + return this.biomes; + } + + public Biome getBiome() { + return biome; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldConfig.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfig.java new file mode 100644 index 000000000..00b088569 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfig.java @@ -0,0 +1,510 @@ +package org.mvplugins.multiverse.core.world; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import org.bukkit.ChatColor; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.configuration.handle.MemoryConfigurationHandle; +import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.configuration.migration.BooleanMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.configuration.migration.DeleteMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.DoubleMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.IntegerMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.LongMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.MigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.MoveMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.NullStringMigratorAction; +import org.mvplugins.multiverse.core.configuration.migration.VersionMigrator; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.world.location.SpawnLocation; + +/** + * Represents a world configuration. + */ +final class WorldConfig { + + private final String worldName; + private final WorldConfigNodes configNodes; + private final MemoryConfigurationHandle configHandle; + private final StringPropertyHandle stringPropertyHandle; + + WorldConfig( + @NotNull String worldName, + @NotNull ConfigurationSection configSection, + @NotNull MultiverseCore multiverseCore) { + this.worldName = worldName; + this.configNodes = new WorldConfigNodes(multiverseCore); + this.configHandle = MemoryConfigurationHandle.builder(configSection, configNodes.getNodes()) + .logger(Logging.getLogger()) + .migrator(ConfigMigrator.builder(configNodes.VERSION) + .addVersionMigrator(initialVersionMigrator()) + .build()) + .build(); + this.stringPropertyHandle = new StringPropertyHandle(configHandle); + load(); + } + + private VersionMigrator initialVersionMigrator() { + return VersionMigrator.builder(1.0) + .addAction(MoveMigratorAction.of("adjustSpawn", "adjust-spawn")) + .addAction(BooleanMigratorAction.of("adjust-spawn")) + .addAction(MoveMigratorAction.of("allowFlight", "allow-flight")) + .addAction(BooleanMigratorAction.of("allow-flight")) + .addAction(MoveMigratorAction.of("allowWeather", "allow-weather")) + .addAction(BooleanMigratorAction.of("allow-weather")) + .addAction(MoveMigratorAction.of("autoHeal", "auto-heal")) + .addAction(BooleanMigratorAction.of("auto-heal")) + .addAction(MoveMigratorAction.of("autoLoad", "auto-load")) + .addAction(BooleanMigratorAction.of("auto-load")) + .addAction(MoveMigratorAction.of("bedRespawn", "bed-respawn")) + .addAction(BooleanMigratorAction.of("bed-respawn")) + //.addAction(MoveMigratorAction.of("difficulty", "difficulty")) + .addAction(MoveMigratorAction.of("entryfee.amount", "entry-fee.amount")) + .addAction(DoubleMigratorAction.of("entry-fee.amount")) + .addAction(MoveMigratorAction.of("entryfee.currency", "entry-fee.currency")) + .addAction(DeleteMigratorAction.of("entryfee")) + //.addAction(MoveMigratorAction.of("environment", "environment")) + .addAction(MoveMigratorAction.of("gameMode", "gamemode")) + //.addAction(MoveMigratorAction.of("generator", "generator")) + .addAction(NullStringMigratorAction.of("generator")) + //.addAction(MoveMigratorAction.of("hidden", "hidden")) + .addAction(BooleanMigratorAction.of("hidden")) + //.addAction(MoveMigratorAction.of("hunger", "hunger")) + .addAction(BooleanMigratorAction.of("hunger")) + .addAction(MoveMigratorAction.of("keepSpawnInMemory", "keep-spawn-in-memory")) + .addAction(BooleanMigratorAction.of("keep-spawn-in-memory")) + .addAction(MoveMigratorAction.of("playerLimit", "player-limit")) + .addAction(IntegerMigratorAction.of("player-limit")) + .addAction(MoveMigratorAction.of("portalForm", "portal-form")) + //.addAction(MoveMigratorAction.of("pvp", "pvp")) + .addAction(BooleanMigratorAction.of("pvp")) + .addAction(MoveMigratorAction.of("respawnWorld", "respawn-world")) + //.addAction(MoveMigratorAction.of("scale", "scale")) + .addAction(DoubleMigratorAction.of("scale")) + //.addAction(MoveMigratorAction.of("seed", "seed")) + .addAction(LongMigratorAction.of("seed")) + .addAction(MoveMigratorAction.of("spawnLocation", "spawn-location")) + //.addAction(MoveMigratorAction.of("spawning.animals.spawn", "spawning.animals.spawn")) + .addAction(BooleanMigratorAction.of("spawning.animals.spawn")) + .addAction(MoveMigratorAction.of("spawning.animals.spawnrate", "spawning.animals.tick-rate")) + .addAction(IntegerMigratorAction.of("spawning.animals.tick-rate")) + //.addAction(MoveMigratorAction.of("spawning.animals.exceptions", "spawning.animals.exceptions")) + //.addAction(MoveMigratorAction.of("spawning.monsters.spawn", "spawning.monsters.spawn")) + .addAction(BooleanMigratorAction.of("spawning.monsters.spawn")) + .addAction(MoveMigratorAction.of("spawning.monsters.spawnrate", "spawning.monsters.tick-rate")) + .addAction(IntegerMigratorAction.of("spawning.monsters.tick-rate")) + //.addAction(MoveMigratorAction.of("spawning.monsters.exceptions", "spawning.monsters.exceptions")) + .addAction(MoveMigratorAction.of("worldBlacklist", "world-blacklist")) + .addAction(new EntryFeeMigrator()) + .addAction(new LegacyAliasMigrator()) + .build(); + } + + Try load() { + return configHandle.load(); + } + + Try load(ConfigurationSection section) { + return configHandle.load(section); + } + + StringPropertyHandle getStringPropertyHandle() { + return stringPropertyHandle; + } + + String getWorldName() { + return worldName; + } + + boolean getAdjustSpawn() { + return configHandle.get(configNodes.ADJUST_SPAWN); + } + + Try setAdjustSpawn(boolean adjustSpawn) { + return configHandle.set(configNodes.ADJUST_SPAWN, adjustSpawn); + } + + @Nullable String getAlias() { + return configHandle.get(configNodes.ALIAS); + } + + Try setAlias(String alias) { + return configHandle.set(configNodes.ALIAS, alias); + } + + boolean getAllowFlight() { + return configHandle.get(configNodes.ALLOW_FLIGHT); + } + + Try setAllowFlight(boolean allowFlight) { + return configHandle.set(configNodes.ALLOW_FLIGHT, allowFlight); + } + + boolean getAllowWeather() { + return configHandle.get(configNodes.ALLOW_WEATHER); + } + + Try setAllowWeather(boolean allowWeather) { + return configHandle.set(configNodes.ALLOW_WEATHER, allowWeather); + } + boolean getAnchorRespawn() { + return configHandle.get(configNodes.ANCHOR_RESPAWN); + } + + Try setAnchorSpawn(boolean anchorSpawn) { + return configHandle.set(configNodes.ANCHOR_RESPAWN, anchorSpawn); + } + + boolean getAutoHeal() { + return configHandle.get(configNodes.AUTO_HEAL); + } + + Try setAutoHeal(boolean autoHeal) { + return configHandle.set(configNodes.AUTO_HEAL, autoHeal); + } + + boolean getAutoLoad() { + return configHandle.get(configNodes.AUTO_LOAD); + } + + Try setAutoLoad(boolean autoLoad) { + return configHandle.set(configNodes.AUTO_LOAD, autoLoad); + } + + Biome getBiome() { + return configHandle.get(configNodes.BIOME); + } + + Try setBiome(Biome biome) { + return configHandle.set(configNodes.BIOME, biome); + } + + boolean getBedRespawn() { + return configHandle.get(configNodes.BED_RESPAWN); + } + + Try setBedRespawn(boolean bedRespawn) { + return configHandle.set(configNodes.BED_RESPAWN, bedRespawn); + } + + Difficulty getDifficulty() { + return configHandle.get(configNodes.DIFFICULTY); + } + + Try setDifficulty(Difficulty difficulty) { + return configHandle.set(configNodes.DIFFICULTY, difficulty); + } + + boolean isEntryFeeEnabled() { + return configHandle.get(configNodes.ENTRY_FEE_ENABLED); + } + + Try setEntryFeeEnabled(boolean entryFeeEnabled) { + return configHandle.set(configNodes.ENTRY_FEE_ENABLED, entryFeeEnabled); + } + + double getEntryFeeAmount() { + return configHandle.get(configNodes.ENTRY_FEE_AMOUNT); + } + + Try setEntryFeeAmount(double entryFeeAmount) { + return configHandle.set(configNodes.ENTRY_FEE_AMOUNT, entryFeeAmount); + } + + Material getEntryFeeCurrency() { + return configHandle.get(configNodes.ENTRY_FEE_CURRENCY); + } + + Try setEntryFeeCurrency(Material entryFeeCurrency) { + return configHandle.set(configNodes.ENTRY_FEE_CURRENCY, entryFeeCurrency); + } + + World.Environment getEnvironment() { + return configHandle.get(configNodes.ENVIRONMENT); + } + + Try setEnvironment(World.Environment environment) { + return configHandle.set(configNodes.ENVIRONMENT, environment); + } + + GameMode getGameMode() { + return configHandle.get(configNodes.GAMEMODE); + } + + Try setGameMode(GameMode gamemode) { + return configHandle.set(configNodes.GAMEMODE, gamemode); + } + + @Nullable String getGenerator() { + return configHandle.get(configNodes.GENERATOR); + } + + Try setGenerator(String generator) { + return configHandle.set(configNodes.GENERATOR, generator); + } + + boolean isHidden() { + return configHandle.get(configNodes.HIDDEN); + } + + Try setHidden(boolean hidden) { + return configHandle.set(configNodes.HIDDEN, hidden); + } + + boolean getHunger() { + return configHandle.get(configNodes.HUNGER); + } + + Try setHunger(boolean hunger) { + return configHandle.set(configNodes.HUNGER, hunger); + } + + boolean getKeepSpawnInMemory() { + return configHandle.get(configNodes.KEEP_SPAWN_IN_MEMORY); + } + + Try setKeepSpawnInMemory(boolean keepSpawnInMemory) { + return configHandle.set(configNodes.KEEP_SPAWN_IN_MEMORY, keepSpawnInMemory); + } + + int getPlayerLimit() { + return configHandle.get(configNodes.PLAYER_LIMIT); + } + + Try setPlayerLimit(int playerLimit) { + return configHandle.set(configNodes.PLAYER_LIMIT, playerLimit); + } + + AllowedPortalType getPortalForm() { + return configHandle.get(configNodes.PORTAL_FORM); + } + + Try setPortalForm(AllowedPortalType portalForm) { + return configHandle.set(configNodes.PORTAL_FORM, portalForm); + } + + boolean getPvp() { + return configHandle.get(configNodes.PVP); + } + + Try setPvp(boolean pvp) { + return configHandle.set(configNodes.PVP, pvp); + } + + String getRespawnWorld() { + return configHandle.get(configNodes.RESPAWN_WORLD); + } + + Try setRespawnWorld(String respawnWorld) { + return configHandle.set(configNodes.RESPAWN_WORLD, respawnWorld); + } + + double getScale() { + return configHandle.get(configNodes.SCALE); + } + + Try setScale(double scale) { + return configHandle.set(configNodes.SCALE, scale); + } + + long getSeed() { + return configHandle.get(configNodes.SEED); + } + + Try setSeed(long seed) { + return configHandle.set(configNodes.SEED, seed); + } + + SpawnLocation getSpawnLocation() { + return configHandle.get(configNodes.SPAWN_LOCATION); + } + + Try setSpawnLocation(SpawnLocation spawnLocation) { + return configHandle.set(configNodes.SPAWN_LOCATION, spawnLocation); + } + + boolean getSpawningAnimals() { + return configHandle.get(configNodes.SPAWNING_ANIMALS); + } + + Try setSpawningAnimals(boolean spawningAnimals) { + return configHandle.set(configNodes.SPAWNING_ANIMALS, spawningAnimals); + } + + int getSpawningAnimalsTicks() { + return configHandle.get(configNodes.SPAWNING_ANIMALS_TICKS); + } + + Try setSpawningAnimalsTicks(int spawningAnimalsAmount) { + return configHandle.set(configNodes.SPAWNING_ANIMALS_TICKS, spawningAnimalsAmount); + } + + List getSpawningAnimalsExceptions() { + return configHandle.get(configNodes.SPAWNING_ANIMALS_EXCEPTIONS); + } + + Try setSpawningAnimalsExceptions(List spawningAnimalsExceptions) { + return configHandle.set(configNodes.SPAWNING_ANIMALS_EXCEPTIONS, spawningAnimalsExceptions); + } + + boolean getSpawningMonsters() { + return configHandle.get(configNodes.SPAWNING_MONSTERS); + } + + Try setSpawningMonsters(boolean spawningMonsters) { + return configHandle.set(configNodes.SPAWNING_MONSTERS, spawningMonsters); + } + + int getSpawningMonstersTicks() { + return configHandle.get(configNodes.SPAWNING_MONSTERS_TICKS); + } + + Try setSpawningMonstersTicks(int spawningMonstersAmount) { + return configHandle.set(configNodes.SPAWNING_MONSTERS_TICKS, spawningMonstersAmount); + } + + List getSpawningMonstersExceptions() { + return configHandle.get(configNodes.SPAWNING_MONSTERS_EXCEPTIONS); + } + + Try setSpawningMonstersExceptions(List spawningMonstersExceptions) { + return configHandle.set(configNodes.SPAWNING_MONSTERS_EXCEPTIONS, spawningMonstersExceptions); + } + + List getWorldBlacklist() { + return configHandle.get(configNodes.WORLD_BLACKLIST); + } + + Try setWorldBlacklist(List worldBlacklist) { + return configHandle.set(configNodes.WORLD_BLACKLIST, worldBlacklist); + } + + void setMVWorld(@NotNull MultiverseWorld world) { + configNodes.setWorld(world); + } + + boolean isLoadedWorld() { + return configNodes.getWorld() instanceof LoadedMultiverseWorld; + } + + void deferenceMVWorld() { + configNodes.setWorld(null); + } + + ConfigurationSection getConfigurationSection() { + return configHandle.getConfig(); + } + + /** + * Migrates the entry fee settings. Assumes entry fee is disabled if currency is not set. + */ + static final class EntryFeeMigrator implements MigratorAction { + @Override + public void migrate(ConfigurationSection config) { + String currency = config.getString("entry-fee.currency", ""); + Logging.info("Entry fee currency: %s", currency); + if (currency.isEmpty()) { + config.set("entry-fee.enabled", false); + config.set("entry-fee.currency", MVEconomist.VAULT_ECONOMY_CODE); + } else { + config.set("entry-fee.enabled", true); + } + } + } + + /** + * Migrates the alias settings. + */ + private static final class LegacyAliasMigrator implements MigratorAction { + @Override + public void migrate(ConfigurationSection config) { + AtomicReference alias = new AtomicReference<>(config.getString("alias", "")); + String color = config.getString("color", ""); + String style = config.getString("style", ""); + config.set("color", null); + config.set("style", null); + + if (alias.get().isEmpty()) return; + + Try.of(() -> Enum.valueOf(EnglishChatColor.class, color.toUpperCase())) + .map(c -> c.color) + .onSuccess(c -> { + if (c != ChatColor.WHITE) { + alias.set("&" + c.getChar() + alias.get()); + } + }); + + Try.of(() -> Enum.valueOf(EnglishChatStyle.class, style.toUpperCase())) + .map(c -> c.color) + .onSuccess(s -> { + if (s != null) { + alias.set("&" + s.getChar() + alias.get()); + } + }); + + config.set("alias", alias.get()); + } + + private enum EnglishChatColor { + // BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable + AQUA(ChatColor.AQUA), + BLACK(ChatColor.BLACK), + BLUE(ChatColor.BLUE), + DARKAQUA(ChatColor.DARK_AQUA), + DARKBLUE(ChatColor.DARK_BLUE), + DARKGRAY(ChatColor.DARK_GRAY), + DARKGREEN(ChatColor.DARK_GREEN), + DARKPURPLE(ChatColor.DARK_PURPLE), + DARKRED(ChatColor.DARK_RED), + GOLD(ChatColor.GOLD), + GRAY(ChatColor.GRAY), + GREEN(ChatColor.GREEN), + LIGHTPURPLE(ChatColor.LIGHT_PURPLE), + RED(ChatColor.RED), + YELLOW(ChatColor.YELLOW), + WHITE(ChatColor.WHITE); + // END CHECKSTYLE-SUPPRESSION: JavadocVariable + + private final ChatColor color; + //private final String text; + + EnglishChatColor(ChatColor color) { + this.color = color; + } + } + + private enum EnglishChatStyle { + // BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable + /** + * No style. + */ + NORMAL(null), + MAGIC(ChatColor.MAGIC), + BOLD(ChatColor.BOLD), + STRIKETHROUGH(ChatColor.STRIKETHROUGH), + UNDERLINE(ChatColor.UNDERLINE), + ITALIC(ChatColor.ITALIC); + // END CHECKSTYLE-SUPPRESSION: JavadocVariable + + private final ChatColor color; + + EnglishChatStyle(ChatColor color) { + this.color = color; + } + + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java new file mode 100644 index 000000000..ebd57a7c6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java @@ -0,0 +1,324 @@ +package org.mvplugins.multiverse.core.world; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import org.bukkit.Bukkit; +import org.bukkit.Difficulty; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.configuration.functions.NodeSerializer; +import org.mvplugins.multiverse.core.event.MVWorldPropertyChangeEvent; +import org.mvplugins.multiverse.core.configuration.node.ConfigNode; +import org.mvplugins.multiverse.core.configuration.node.ListConfigNode; +import org.mvplugins.multiverse.core.configuration.node.NodeGroup; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.utils.MaterialConverter; +import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler; +import org.mvplugins.multiverse.core.world.location.NullLocation; +import org.mvplugins.multiverse.core.world.location.SpawnLocation; + +/** + * Represents nodes in a world configuration. + */ +final class WorldConfigNodes { + private static final double CONFIG_VERSION = 1.0; + + private final NodeGroup nodes = new NodeGroup(); + private WorldManager worldManager; + private EnforcementHandler enforcementHandler; + private MultiverseWorld world = null; + + WorldConfigNodes(@NotNull MultiverseCore multiverseCore) { + this.worldManager = multiverseCore.getServiceLocator().getService(WorldManager.class); + this.enforcementHandler = multiverseCore.getServiceLocator().getService(EnforcementHandler.class); + } + + MultiverseWorld getWorld() { + return world; + } + + void setWorld(MultiverseWorld world) { + this.world = world; + } + + NodeGroup getNodes() { + return nodes; + } + + private ConfigNode node(ConfigNode.Builder nodeBuilder) { + nodeBuilder.onSetValue((oldValue, newValue) -> { + if (Objects.equals(oldValue, newValue)) return; + if (world == null) return; + MVWorldPropertyChangeEvent mvWorldPropertyChangeEvent = new MVWorldPropertyChangeEvent<>( + world, Option.of(nodeBuilder.name()).getOrElse(nodeBuilder.path()), oldValue, newValue); + Bukkit.getPluginManager().callEvent(mvWorldPropertyChangeEvent); + Logging.finer("MVWorldPropertyChangeEvent fired for world '%s' with name '%s' and value '%s'", + world.getName(), nodeBuilder.path(), newValue); + }); + + ConfigNode node = nodeBuilder.build(); + nodes.add(node); + return node; + } + + // BEGIN CHECKSTYLE-SUPPRESSION: Javadoc + // BEGIN CHECKSTYLE-SUPPRESSION: MemberName + // BEGIN CHECKSTYLE-SUPPRESSION: Abbreviation + // BEGIN CHECKSTYLE-SUPPRESSION: VisibilityModifier + + final ConfigNode ADJUST_SPAWN = node(ConfigNode.builder("adjust-spawn", Boolean.class) + .defaultValue(false)); + + final ConfigNode ALIAS = node(ConfigNode.builder("alias", String.class) + .defaultValue("")); + + final ConfigNode ALLOW_FLIGHT = node(ConfigNode.builder("allow-flight", Boolean.class) + .defaultValue(false) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + enforcementHandler.handleAllFlightEnforcement(loadedWorld); + })); + + final ConfigNode ALLOW_WEATHER = node(ConfigNode.builder("allow-weather", Boolean.class) + .defaultValue(true) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(world -> { + if (!world.isClearWeather() && !newValue) { + world.setThundering(false); + world.setStorm(false); + } + }); + })); + + final ConfigNode ANCHOR_RESPAWN = node(ConfigNode.builder("anchor-respawn", Boolean.class) + .defaultValue(true)); + + final ConfigNode AUTO_HEAL = node(ConfigNode.builder("auto-heal", Boolean.class) + .defaultValue(true)); + + final ConfigNode AUTO_LOAD = node(ConfigNode.builder("auto-load", Boolean.class) + .defaultValue(true)); + + final ConfigNode BED_RESPAWN = node(ConfigNode.builder("bed-respawn", Boolean.class) + .defaultValue(true)); + + final ConfigNode BIOME = node(ConfigNode.builder("biome", Biome.class) + .defaultValue(Biome.CUSTOM) + .name(null) + .serializer(new NodeSerializer<>() { + private static final String VANILLA_BIOME_BEHAVIOUR = "@vanilla"; + + @Override + public Biome deserialize(Object object, Class type) { + if (object instanceof Biome) { + return (Biome) object; + } + try { + String biomeStr = String.valueOf(object); + if (biomeStr.equalsIgnoreCase(VANILLA_BIOME_BEHAVIOUR)) { + return null; + } + return Biome.valueOf(biomeStr.toUpperCase()); + } catch (IllegalArgumentException e) { + return null; + } + } + + @Override + public Object serialize(Biome biome, Class type) { + if (biome == null || biome == Biome.CUSTOM) { + return VANILLA_BIOME_BEHAVIOUR; + } + return biome.name().toLowerCase(); + } + })); + + final ConfigNode DIFFICULTY = node(ConfigNode.builder("difficulty", Difficulty.class) + .defaultValue(Difficulty.NORMAL) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setDifficulty(newValue)); + })); + + final ConfigNode ENTRY_FEE_ENABLED = node(ConfigNode.builder("entry-fee.enabled", Boolean.class) + .defaultValue(false) + .name("entryfee-enabled")); + + final ConfigNode ENTRY_FEE_AMOUNT = node(ConfigNode.builder("entry-fee.amount", Double.class) + .defaultValue(0.0) + .name("entryfee-amount")); + + final ConfigNode ENTRY_FEE_CURRENCY = node(ConfigNode.builder("entry-fee.currency", Material.class) + .defaultValue(MVEconomist.VAULT_ECONOMY_MATERIAL) + .name("entryfee-currency") + .serializer(new NodeSerializer<>() { + @Override + public Material deserialize(Object object, Class type) { + return Option.of(object) + .map(String::valueOf) + .map(materialStr -> { + if (materialStr.equalsIgnoreCase(MVEconomist.VAULT_ECONOMY_CODE)) { + return MVEconomist.VAULT_ECONOMY_MATERIAL; + } + return MaterialConverter.stringToMaterial(materialStr); + }) + .getOrElse(MVEconomist.VAULT_ECONOMY_MATERIAL); + } + + @Override + public Object serialize(Material object, Class type) { + return Option.of(object) + .map(material -> material == MVEconomist.VAULT_ECONOMY_MATERIAL + ? MVEconomist.VAULT_ECONOMY_CODE + : material.name()) + .getOrElse(MVEconomist.VAULT_ECONOMY_CODE); + } + })); + + final ConfigNode ENVIRONMENT = node(ConfigNode + .builder("environment", World.Environment.class) + .defaultValue(World.Environment.NORMAL) + .name(null)); + + final ConfigNode GAMEMODE = node(ConfigNode.builder("gamemode", GameMode.class) + .defaultValue(GameMode.SURVIVAL) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + enforcementHandler.handleAllGameModeEnforcement(loadedWorld); + })); + + final ConfigNode GENERATOR = node(ConfigNode.builder("generator", String.class) + // this should be set on world creation, if @error is shown in config, something went wrong + .defaultValue("@error") + .name(null)); + + final ConfigNode HIDDEN = node(ConfigNode.builder("hidden", Boolean.class) + .defaultValue(false)); + + final ConfigNode HUNGER = node(ConfigNode.builder("hunger", Boolean.class) + .defaultValue(true)); + + final ConfigNode KEEP_SPAWN_IN_MEMORY = node(ConfigNode + .builder("keep-spawn-in-memory", Boolean.class) + .defaultValue(true) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setKeepSpawnInMemory(newValue)); + })); + + final ConfigNode PLAYER_LIMIT = node(ConfigNode.builder("player-limit", Integer.class) + .defaultValue(-1)); + + final ConfigNode PORTAL_FORM = node(ConfigNode + .builder("portal-form", AllowedPortalType.class) + .defaultValue(AllowedPortalType.ALL)); + + final ConfigNode PVP = node(ConfigNode.builder("pvp", Boolean.class) + .defaultValue(true) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setPVP(newValue)); + })); + + final ConfigNode RESPAWN_WORLD = node(ConfigNode.builder("respawn-world", String.class) + .defaultValue("") + .suggester(input -> { + if (worldManager == null) return Collections.emptyList(); + return worldManager.getWorlds().stream().map(MultiverseWorld::getName).toList(); + })); + + final ConfigNode SCALE = node(ConfigNode.builder("scale", Double.class) + .defaultValue(() -> { + if (world == null) return 1.0; + return switch (world.getEnvironment()) { + case NETHER -> 8.0; + case THE_END -> 16.0; + default -> 1.0; + }; + })); + + final ConfigNode SEED = node(ConfigNode.builder("seed", Long.class) + .defaultValue(Long.MIN_VALUE) + .name(null)); + + final ConfigNode SPAWN_LOCATION = node(ConfigNode.builder("spawn-location", SpawnLocation.class) + .defaultValue(NullLocation.get()) + .name(null) + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + if (newValue == null || newValue instanceof NullLocation) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> { + bukkitWorld.setSpawnLocation(newValue.getBlockX(), newValue.getBlockY(), newValue.getBlockZ()); + newValue.setWorld(bukkitWorld); + }); + })); + + final ConfigNode SPAWNING_ANIMALS = node(ConfigNode.builder("spawning.animals.spawn", Boolean.class) + .defaultValue(true) + .name("spawning-animals") + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> + bukkitWorld.setSpawnFlags(bukkitWorld.getAllowMonsters(), newValue)); + })); + + final ConfigNode SPAWNING_ANIMALS_TICKS = node(ConfigNode + .builder("spawning.animals.tick-rate", Integer.class) + .defaultValue(-1) + .name("spawning-animals-ticks") + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setTicksPerAnimalSpawns(newValue)); + })); + + final ConfigNode> SPAWNING_ANIMALS_EXCEPTIONS = node(ListConfigNode + .listBuilder("spawning.animals.exceptions", String.class) + .defaultValue(new ArrayList<>()) + .name("spawning-animals-exceptions")); + + final ConfigNode SPAWNING_MONSTERS = node(ConfigNode + .builder("spawning.monsters.spawn", Boolean.class) + .defaultValue(true) + .name("spawning-monsters") + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> + bukkitWorld.setSpawnFlags(newValue, bukkitWorld.getAllowAnimals())); + })); + + final ConfigNode SPAWNING_MONSTERS_TICKS = node(ConfigNode + .builder("spawning.monsters.tick-rate", Integer.class) + .defaultValue(-1) + .name("spawning-monsters-ticks") + .onSetValue((oldValue, newValue) -> { + if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; + loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setTicksPerMonsterSpawns(newValue)); + })); + + final ConfigNode> SPAWNING_MONSTERS_EXCEPTIONS = node(ListConfigNode + .listBuilder("spawning.monsters.exceptions", String.class) + .defaultValue(new ArrayList<>()) + .name("spawning-monsters-exceptions")); + + final ConfigNode> WORLD_BLACKLIST = node(ListConfigNode.listBuilder("world-blacklist", String.class)); + + final ConfigNode VERSION = node(ConfigNode.builder("version", Double.class) + .defaultValue(CONFIG_VERSION) + .name(null)); + + // END CHECKSTYLE-SUPPRESSION: Javadoc + // END CHECKSTYLE-SUPPRESSION: MemberName + // END CHECKSTYLE-SUPPRESSION: Abbreviation + // END CHECKSTYLE-SUPPRESSION: VisibilityModifier +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java new file mode 100644 index 000000000..1978d14d0 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldManager.java @@ -0,0 +1,954 @@ +package org.mvplugins.multiverse.core.world; + +import java.io.File; +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.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import com.dumptruckman.minecraft.util.Logging; +import com.google.common.base.Strings; +import io.vavr.control.Option; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.bukkit.block.Biome; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.event.MVWorldDeleteEvent; +import org.mvplugins.multiverse.core.exceptions.MultiverseException; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.permissions.CorePermissions; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; +import org.mvplugins.multiverse.core.teleportation.BlockSafety; +import org.mvplugins.multiverse.core.teleportation.LocationManipulation; +import org.mvplugins.multiverse.core.utils.ServerProperties; +import org.mvplugins.multiverse.core.utils.result.Attempt; +import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.utils.FileUtils; +import org.mvplugins.multiverse.core.world.generators.GeneratorProvider; +import org.mvplugins.multiverse.core.world.helpers.DataStore.GameRulesStore; +import org.mvplugins.multiverse.core.world.helpers.DataTransfer; +import org.mvplugins.multiverse.core.world.helpers.WorldNameChecker; +import org.mvplugins.multiverse.core.world.options.CloneWorldOptions; +import org.mvplugins.multiverse.core.world.options.CreateWorldOptions; +import org.mvplugins.multiverse.core.world.options.ImportWorldOptions; +import org.mvplugins.multiverse.core.world.options.KeepWorldSettingsOptions; +import org.mvplugins.multiverse.core.world.options.RegenWorldOptions; +import org.mvplugins.multiverse.core.world.options.UnloadWorldOptions; +import org.mvplugins.multiverse.core.world.reasons.CloneFailureReason; +import org.mvplugins.multiverse.core.world.reasons.CreateFailureReason; +import org.mvplugins.multiverse.core.world.reasons.DeleteFailureReason; +import org.mvplugins.multiverse.core.world.reasons.ImportFailureReason; +import org.mvplugins.multiverse.core.world.reasons.LoadFailureReason; +import org.mvplugins.multiverse.core.world.reasons.RegenFailureReason; +import org.mvplugins.multiverse.core.world.reasons.RemoveFailureReason; +import org.mvplugins.multiverse.core.world.reasons.UnloadFailureReason; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; +import static org.mvplugins.multiverse.core.world.helpers.DataStore.WorldBorderStore; +import static org.mvplugins.multiverse.core.world.helpers.DataStore.WorldConfigStore; + +@Service // SUPPRESS CHECKSTYLE: ClassFanOutComplexity This is the world manager, it's going to be complex. +public final class WorldManager { + + private static final List CLONE_IGNORE_FILES = Arrays.asList("uid.dat", "session.lock"); + + private final Map worldsMap; + private final Map loadedWorldsMap; + private final List unloadTracker; + private final List loadTracker; + private final WorldsConfigManager worldsConfigManager; + private final WorldNameChecker worldNameChecker; + private final GeneratorProvider generatorProvider; + private final FileUtils fileUtils; + private final BlockSafety blockSafety; + private final LocationManipulation locationManipulation; + private final PluginManager pluginManager; + private final CorePermissions corePermissions; + private final ServerProperties serverProperties; + + @Inject + WorldManager( + @NotNull WorldsConfigManager worldsConfigManager, + @NotNull WorldNameChecker worldNameChecker, + @NotNull GeneratorProvider generatorProvider, + @NotNull FileUtils fileUtils, + @NotNull BlockSafety blockSafety, + @NotNull LocationManipulation locationManipulation, + @NotNull PluginManager pluginManager, + @NotNull CorePermissions corePermissions, + @NotNull ServerProperties serverProperties) { + this.serverProperties = serverProperties; + this.worldsMap = new HashMap<>(); + this.loadedWorldsMap = new HashMap<>(); + this.unloadTracker = new ArrayList<>(); + this.loadTracker = new ArrayList<>(); + + this.worldsConfigManager = worldsConfigManager; + this.worldNameChecker = worldNameChecker; + this.generatorProvider = generatorProvider; + this.fileUtils = fileUtils; + this.blockSafety = blockSafety; + this.locationManipulation = locationManipulation; + this.pluginManager = pluginManager; + this.corePermissions = corePermissions; + } + + /** + * Loads all worlds from the worlds config. + * + * @return The result of the load. + */ + public Try initAllWorlds() { + return updateWorldsFromConfig().andThenTry(() -> { + loadDefaultWorlds(); + autoLoadWorlds(); + saveWorldsConfig(); + }); + } + + /** + * Updates the current set of worlds to match the worlds config. + * + * @return A successful Try if the worlds.yml config was loaded successfully. + */ + private Try updateWorldsFromConfig() { + return worldsConfigManager.load().mapTry(result -> { + loadNewWorldConfigs(result.newWorlds()); + removeWorldsNotInConfigs(result.removedWorlds()); + return null; + }); + } + + private void loadNewWorldConfigs(Collection newWorldConfigs) { + newWorldConfigs.forEach(worldConfig -> Option.of(worldsMap.get(worldConfig.getWorldName())) + .peek(unloadedWorld -> unloadedWorld.setWorldConfig(worldConfig)) + .onEmpty(() -> newMultiverseWorld(worldConfig.getWorldName(), worldConfig))); + } + + private void removeWorldsNotInConfigs(Collection removedWorlds) { + removedWorlds.forEach(worldName -> removeWorld(worldName) + .onFailure(failure -> Logging.severe("Failed to unload world %s: %s", worldName, failure)) + .onSuccess(success -> Logging.fine("Unloaded world %s as it was removed from config", worldName))); + } + + /** + * Load worlds that are already loaded by bukkit before Multiverse-Core is loaded. + */ + private void loadDefaultWorlds() { + Bukkit.getWorlds().forEach(bukkitWorld -> { + if (isWorld(bukkitWorld.getName())) { + return; + } + importWorld(ImportWorldOptions.worldName(bukkitWorld.getName()) + .environment(bukkitWorld.getEnvironment()) + .generator(generatorProvider.getDefaultGeneratorForWorld(bukkitWorld.getName()))); + }); + } + + /** + * Loads all worlds that are set to autoload. + */ + private void autoLoadWorlds() { + getWorlds().forEach(world -> { + if (isLoadedWorld(world) || !world.getAutoLoad()) { + return; + } + loadWorld(world).onFailure(failure -> Logging.severe("Failed to load world %s: %s", + world.getName(), failure)); + }); + } + + /** + * Creates a new world. + * + * @param options The options for customizing the creation of a new world. + * @return The result of the creation. + */ + public Attempt createWorld(CreateWorldOptions options) { + return validateCreateWorldOptions(options).mapAttempt(this::createValidatedWorld); + } + + private Attempt validateCreateWorldOptions( + CreateWorldOptions options) { + if (!worldNameChecker.isValidWorldName(options.worldName())) { + return worldActionResult(CreateFailureReason.INVALID_WORLDNAME, options.worldName()); + } else if (getLoadedWorld(options.worldName()).isDefined()) { + return worldActionResult(CreateFailureReason.WORLD_EXIST_LOADED, options.worldName()); + } else if (getWorld(options.worldName()).isDefined()) { + return worldActionResult(CreateFailureReason.WORLD_EXIST_UNLOADED, options.worldName()); + } else if (worldNameChecker.hasWorldFolder(options.worldName())) { + return worldActionResult(CreateFailureReason.WORLD_EXIST_FOLDER, options.worldName()); + } + return worldActionResult(options); + } + + private Attempt createValidatedWorld( + CreateWorldOptions options) { + String parsedGenerator = parseGenerator(options.worldName(), options.generator()); + WorldCreator worldCreator = WorldCreator.name(options.worldName()) + .biomeProvider(createSingleBiomeProvider(options.biome())) + .environment(options.environment()) + .generateStructures(options.generateStructures()) + .generator(parsedGenerator) + .seed(options.seed()) + .type(options.worldType()); + return createBukkitWorld(worldCreator).fold( + exception -> worldActionResult(CreateFailureReason.BUKKIT_CREATION_FAILED, + options.worldName(), exception), + world -> { + LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld( + world, + parsedGenerator, + options.useSpawnAdjust()); + return worldActionResult(loadedWorld); + }); + } + + /** + * Imports an existing world folder. + * + * @param options The options for customizing the import of an existing world folder. + * @return The result of the import. + */ + public Attempt importWorld( + ImportWorldOptions options) { + return validateImportWorldOptions(options).mapAttempt(this::doImportWorld); + } + + private Attempt validateImportWorldOptions( + ImportWorldOptions options) { + String worldName = options.worldName(); + if (!worldNameChecker.isValidWorldName(worldName)) { + return worldActionResult(ImportFailureReason.INVALID_WORLDNAME, worldName); + } else if (!worldNameChecker.isValidWorldFolder(worldName)) { + return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, worldName); + } else if (isLoadedWorld(worldName)) { + return worldActionResult(ImportFailureReason.WORLD_EXIST_LOADED, worldName); + } else if (isWorld(worldName)) { + return worldActionResult(ImportFailureReason.WORLD_EXIST_UNLOADED, worldName); + } + return worldActionResult(options); + } + + private Attempt doImportWorld( + ImportWorldOptions options) { + String parsedGenerator = parseGenerator(options.worldName(), options.generator()); + WorldCreator worldCreator = WorldCreator.name(options.worldName()) + .biomeProvider(createSingleBiomeProvider(options.biome())) + .environment(options.environment()) + .generator(parsedGenerator); + return createBukkitWorld(worldCreator).fold( + exception -> worldActionResult(ImportFailureReason.BUKKIT_CREATION_FAILED, + options.worldName(), exception), + world -> { + LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld(world, + parsedGenerator, + options.useSpawnAdjust()); + return worldActionResult(loadedWorld); + }); + } + + /** + * Parses generator string and defaults to generator in bukkit.yml if not specified. + * + * @param worldName The name of the world. + * @param generator The input generator string. + * @return The parsed generator string. + */ + private @Nullable String parseGenerator(@NotNull String worldName, @Nullable String generator) { + return Strings.isNullOrEmpty(generator) + ? generatorProvider.getDefaultGeneratorForWorld(worldName) + : generator; + } + + private MultiverseWorld newMultiverseWorld(String worldName, WorldConfig worldConfig) { + MultiverseWorld mvWorld = new MultiverseWorld(worldName, worldConfig); + worldsMap.put(mvWorld.getName(), mvWorld); + corePermissions.addWorldPermissions(mvWorld); + return mvWorld; + } + + /** + * Creates a new loaded multiverseWorld from a bukkit world. + * + * @param world The bukkit world to create a multiverse world from. + * @param generator The generator string. + * @param adjustSpawn Whether to adjust spawn. + */ + private LoadedMultiverseWorld newLoadedMultiverseWorld( + @NotNull World world, @Nullable String generator, boolean adjustSpawn) { + WorldConfig worldConfig = worldsConfigManager.addWorldConfig(world.getName()); + worldConfig.setAdjustSpawn(adjustSpawn); + worldConfig.setGenerator(generator == null ? "" : generator); + + MultiverseWorld mvWorld = newMultiverseWorld(world.getName(), worldConfig); + LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld( + world, + worldConfig, + blockSafety, + locationManipulation); + setDefaultEnvironmentScale(mvWorld); + loadedWorldsMap.put(loadedWorld.getName(), loadedWorld); + saveWorldsConfig(); + return loadedWorld; + } + + private void setDefaultEnvironmentScale(MultiverseWorld world) { + double scale = switch (world.getEnvironment()) { + case NETHER -> 8.0; + case THE_END -> 16.0; + default -> 1.0; + }; + world.setScale(scale); + } + + /** + * Loads an existing world in config. + * + * @param worldName The name of the world to load. + * @return The result of the load. + */ + public Attempt loadWorld(@NotNull String worldName) { + return getWorld(worldName) + .map(this::loadWorld) + .getOrElse(() -> worldNameChecker.isValidWorldFolder(worldName) + ? worldActionResult(LoadFailureReason.WORLD_EXIST_FOLDER, worldName) + : worldActionResult(LoadFailureReason.WORLD_NON_EXISTENT, worldName)); + } + + /** + * Loads an existing world in config. + * + * @param world The world to load. + * @return The result of the load. + */ + public Attempt loadWorld(@NotNull MultiverseWorld world) { + return validateWorldToLoad(world).mapAttempt(this::doLoadWorld); + } + + private Attempt validateWorldToLoad( + @NotNull MultiverseWorld mvWorld) { + if (loadTracker.contains(mvWorld.getName())) { + // This is to prevent recursive calls by WorldLoadEvent + Logging.fine("World already loading: " + mvWorld.getName()); + return worldActionResult(LoadFailureReason.WORLD_ALREADY_LOADING, mvWorld.getName()); + } else if (isLoadedWorld(mvWorld)) { + Logging.severe("World already loaded: " + mvWorld.getName()); + return worldActionResult(LoadFailureReason.WORLD_EXIST_LOADED, mvWorld.getName()); + } + return worldActionResult(mvWorld); + } + + private Attempt doLoadWorld(@NotNull MultiverseWorld mvWorld) { + return createBukkitWorld(WorldCreator.name(mvWorld.getName()) + .biomeProvider(createSingleBiomeProvider(mvWorld.getBiome())) + .environment(mvWorld.getEnvironment()) + .generator(Strings.isNullOrEmpty(mvWorld.getGenerator()) ? null : mvWorld.getGenerator()) + .seed(mvWorld.getSeed())).fold( + exception -> worldActionResult(LoadFailureReason.BUKKIT_CREATION_FAILED, + mvWorld.getName(), exception), + world -> { + WorldConfig worldConfig = worldsConfigManager.getWorldConfig(mvWorld.getName()).get(); + LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld( + world, + worldConfig, + blockSafety, + locationManipulation); + loadedWorldsMap.put(loadedWorld.getName(), loadedWorld); + saveWorldsConfig(); + return worldActionResult(loadedWorld); + }); + } + + /** + * Creates a single biome provider for the specified biome. + * @param biome The biome + * @return The single biome provider or null if biome is null or custom + */ + private @Nullable BiomeProvider createSingleBiomeProvider(@Nullable Biome biome) { + if (biome == null || biome == Biome.CUSTOM) { + return null; + } + return new SingleBiomeProvider(biome); + } + + /** + * Unloads an existing multiverse world. It will still remain as an unloaded world. + * + * @param options The options for customizing the unloading of a world. + * @return The result of the unload action. + */ + public Attempt unloadWorld(@NotNull UnloadWorldOptions options) { + LoadedMultiverseWorld world = options.world(); + + if (unloadTracker.contains(world.getName())) { + // This is to prevent recursive calls by WorldUnloadEvent + Logging.fine("World already unloading: " + world.getName()); + return worldActionResult(UnloadFailureReason.WORLD_ALREADY_UNLOADING, world.getName()); + } + + return unloadBukkitWorld(world.getBukkitWorld().getOrNull(), options.saveBukkitWorld()).fold( + exception -> worldActionResult(UnloadFailureReason.BUKKIT_UNLOAD_FAILED, + world.getName(), exception), + success -> Option.of(loadedWorldsMap.remove(world.getName())).fold( + () -> { + Logging.severe("Failed to remove world from map: " + world.getName()); + return worldActionResult(UnloadFailureReason.WORLD_NON_EXISTENT, world.getName()); + }, + mvWorld -> { + Logging.fine("Removed MultiverseWorld from map: " + world.getName()); + var unloadedWorld = Objects.requireNonNull(worldsMap.get(world.getName()), + "For some reason, the unloaded world isn't in the map... BUGGG"); + mvWorld.getWorldConfig().setMVWorld(unloadedWorld); + return worldActionResult(unloadedWorld); + })); + } + + /** + * Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an + * unloaded world. World files will not be deleted. + * + * @param worldName The name of the world to remove. + * @return The result of the remove. + */ + public Attempt removeWorld( + @NotNull String worldName) { + return getWorld(worldName) + .map(this::removeWorld) + .getOrElse(() -> worldActionResult(RemoveFailureReason.WORLD_NON_EXISTENT, worldName)); + } + + /** + * Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an + * unloaded world. World files will not be deleted. + * + * @param world The multiverse world to remove. + * @return The result of the remove. + */ + public Attempt removeWorld(@NotNull MultiverseWorld world) { + return getLoadedWorld(world).fold( + () -> removeWorldFromConfig(world), + this::removeWorld); + } + + /** + * Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an + * unloaded world. World files will not be deleted. + * + * @param loadedWorld The multiverse world to remove. + * @return The result of the remove. + */ + public Attempt removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) { + return unloadWorld(UnloadWorldOptions.world(loadedWorld)) + .transform(RemoveFailureReason.UNLOAD_FAILED) + .mapAttempt(this::removeWorldFromConfig); + } + + /** + * Removes an existing multiverse world from the world's config. It will no longer be a world known to Multiverse. + * + * @param world The multiverse world to remove. + * @return The result of the remove. + */ + private Attempt removeWorldFromConfig(@NotNull MultiverseWorld world) { + // Remove world from config + worldsMap.remove(world.getName()); + ((MultiverseWorld) world).getWorldConfig().deferenceMVWorld(); + worldsConfigManager.deleteWorldConfig(world.getName()); + saveWorldsConfig(); + corePermissions.removeWorldPermissions(world); + return worldActionResult(world.getName()); + } + + /** + * Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded. + * Warning: This will delete all world files. + * + * @param worldName The name of the world to delete. + * @return The result of the delete action. + */ + public Attempt deleteWorld(@NotNull String worldName) { + return getWorld(worldName) + .map(this::deleteWorld) + .getOrElse(() -> worldActionResult(DeleteFailureReason.WORLD_NON_EXISTENT, worldName)); + } + + /** + * Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded. + * Warning: This will delete all world files. + * + * @param world The world to delete. + * @return The result of the delete action. + */ + public Attempt deleteWorld(@NotNull MultiverseWorld world) { + return getLoadedWorld(world).fold( + () -> loadWorld(world) + .transform(DeleteFailureReason.LOAD_FAILED) + .mapAttempt(this::deleteWorld), + this::deleteWorld); + } + + /** + * Deletes an existing multiverse world entirely. Warning: This will delete all world files. + * + * @param world The multiverse world to delete. + * @return The result of the delete action. + */ + public Attempt deleteWorld(@NotNull LoadedMultiverseWorld world) { + AtomicReference worldFolder = new AtomicReference<>(); + return validateWorldToDelete(world) + .peek(worldFolder::set) + .mapAttempt(() -> { + MVWorldDeleteEvent event = new MVWorldDeleteEvent(world); + pluginManager.callEvent(event); + return event.isCancelled() + ? Attempt.failure(DeleteFailureReason.EVENT_CANCELLED) + : Attempt.success(null); + }) + .mapAttempt(() -> removeWorld(world).transform(DeleteFailureReason.REMOVE_FAILED)) + .mapAttempt(() -> fileUtils.deleteFolder(worldFolder.get()).fold( + exception -> worldActionResult(DeleteFailureReason.FAILED_TO_DELETE_FOLDER, + world.getName(), exception), + success -> worldActionResult(world.getName()))); + } + + private Attempt validateWorldToDelete( + @NotNull LoadedMultiverseWorld world) { + File worldFolder = world.getBukkitWorld().map(World::getWorldFolder).getOrNull(); + if (worldFolder == null || !worldNameChecker.isValidWorldFolder(worldFolder)) { + Logging.severe("Failed to get world folder for world: " + world.getName()); + return worldActionResult(DeleteFailureReason.WORLD_FOLDER_NOT_FOUND, world.getName()); + } + return worldActionResult(worldFolder); + } + + /** + * Clones an existing multiverse world. + * + * @param options The options for customizing the cloning of a world. + * @return The result of the clone. + */ + public Attempt cloneWorld(@NotNull CloneWorldOptions options) { + return cloneWorldValidateWorld(options) + .mapAttempt(this::cloneWorldCopyFolder) + .mapAttempt(validatedOptions -> { + ImportWorldOptions importWorldOptions = ImportWorldOptions + .worldName(validatedOptions.newWorldName()) + .biome(validatedOptions.world().getBiome()) + .environment(validatedOptions.world().getEnvironment()) + .generator(validatedOptions.world().getGenerator()); + return importWorld(importWorldOptions).transform(CloneFailureReason.IMPORT_FAILED); + }) + .onSuccess(newWorld -> { + cloneWorldTransferData(options, newWorld); + if (options.keepWorldConfig()) { + newWorld.setSpawnLocation(options.world().getSpawnLocation()); + } + saveWorldsConfig(); + }); + } + + private Attempt cloneWorldValidateWorld( + @NotNull CloneWorldOptions options) { + String newWorldName = options.newWorldName(); + if (!worldNameChecker.isValidWorldName(newWorldName)) { + Logging.severe("Invalid world name: " + newWorldName); + return worldActionResult(CloneFailureReason.INVALID_WORLDNAME, newWorldName); + } + if (isLoadedWorld(newWorldName)) { + Logging.severe("World already loaded when attempting to clone: " + newWorldName); + return worldActionResult(CloneFailureReason.WORLD_EXIST_LOADED, newWorldName); + } + if (isWorld(newWorldName)) { + Logging.severe("World already exist unloaded: " + newWorldName); + return worldActionResult(CloneFailureReason.WORLD_EXIST_UNLOADED, newWorldName); + } + if (worldNameChecker.hasWorldFolder(newWorldName)) { + return worldActionResult(CloneFailureReason.WORLD_EXIST_FOLDER, newWorldName); + } + return worldActionResult(options); + } + + private Attempt cloneWorldCopyFolder( + @NotNull CloneWorldOptions options) { + File worldFolder = options.world().getBukkitWorld().map(World::getWorldFolder).get(); + File newWorldFolder = new File(Bukkit.getWorldContainer(), options.newWorldName()); + return fileUtils.copyFolder(worldFolder, newWorldFolder, CLONE_IGNORE_FILES).fold( + exception -> worldActionResult(CloneFailureReason.COPY_FAILED, + options.world().getName(), exception), + success -> worldActionResult(options)); + } + + private void cloneWorldTransferData(@NotNull CloneWorldOptions options, @NotNull LoadedMultiverseWorld newWorld) { + DataTransfer dataTransfer = transferData(options, options.world()); + dataTransfer.pasteAllTo(newWorld); + } + + private DataTransfer transferData( + @NotNull KeepWorldSettingsOptions options, @NotNull LoadedMultiverseWorld world) { + DataTransfer dataTransfer = new DataTransfer<>(); + + if (options.keepWorldConfig()) { + dataTransfer.addDataStore(new WorldConfigStore(), world); + } + if (options.keepGameRule()) { + dataTransfer.addDataStore(new GameRulesStore(), world); + } + if (options.keepWorldBorder()) { + dataTransfer.addDataStore(new WorldBorderStore(), world); + } + + return dataTransfer; + } + + /** + * Regenerates a world. + * + * @param options The options for customizing the regeneration of a world. + * @return The result of the regeneration. + */ + public Attempt regenWorld(@NotNull RegenWorldOptions options) { + LoadedMultiverseWorld world = options.world(); + DataTransfer dataTransfer = transferData(options, world); + boolean shouldKeepSpawnLocation = options.keepWorldConfig() && options.seed() == world.getSeed(); + Location spawnLocation = world.getSpawnLocation(); + + CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName()) + .environment(world.getEnvironment()) + .generateStructures(world.canGenerateStructures().getOrElse(true)) + .generator(world.getGenerator()) + .seed(options.seed()) + .useSpawnAdjust(!shouldKeepSpawnLocation && world.getAdjustSpawn()) + .worldType(world.getWorldType().getOrElse(WorldType.NORMAL)); + + return deleteWorld(world) + .transform(RegenFailureReason.DELETE_FAILED) + .mapAttempt(() -> createWorld(createWorldOptions).transform(RegenFailureReason.CREATE_FAILED)) + .onSuccess(newWorld -> { + dataTransfer.pasteAllTo(newWorld); + if (shouldKeepSpawnLocation) { + // Special case for spawn location to prevent unsafe location if world was regen using a + // different seed. + newWorld.setSpawnLocation(spawnLocation); + } + saveWorldsConfig(); + }); + } + + private Attempt worldActionResult(@NotNull T value) { + return Attempt.success(value); + } + + private Attempt worldActionResult( + @NotNull F failureReason, @NotNull String worldName) { + return Attempt.failure(failureReason, Replace.WORLD.with(worldName)); + } + + private Attempt worldActionResult( + @NotNull F failureReason, @NotNull String worldName, @NotNull Throwable error) { + // TODO: Localize error message if its a MultiverseException + return Attempt.failure(failureReason, Replace.WORLD.with(worldName), replace("{error}").with(error.getMessage())); + } + + /** + * Creates a bukkit world. + * + * @param worldCreator The world parameters. + * @return The created world. + */ + private Try createBukkitWorld(WorldCreator worldCreator) { + return Try.of(() -> { + this.loadTracker.add(worldCreator.name()); + World world = worldCreator.createWorld(); + if (world == null) { + // TODO: Localize this + throw new Exception("World created returned null!"); + } + Logging.fine("Bukkit created world: " + world.getName()); + return world; + }).onFailure(exception -> { + Logging.severe("Failed to create bukkit world: " + worldCreator.name()); + exception.printStackTrace(); + }).andFinally(() -> this.loadTracker.remove(worldCreator.name())); + } + + /** + * Unloads a bukkit world. + * + * @param world The bukkit world to unload. + * @return The unloaded world. + */ + private Try unloadBukkitWorld(World world, boolean save) { + return Try.run(() -> { + if (world == null) { + return; + } + unloadTracker.add(world.getName()); + if (!Bukkit.unloadWorld(world, save)) { + // TODO: Localize this, maybe with MultiverseException + var defaultWorldName = getDefaultWorld().map(LoadedMultiverseWorld::getName).getOrElse(""); + if (Objects.equals(world.getName(), defaultWorldName)) { + throw new MultiverseException("You can't unload the default world! World " + world.getName() + + " is the default world defined in server.properties `level-name`."); + } + if (!world.getPlayers().isEmpty()) { + throw new MultiverseException("There are still players in the world! Please use --remove-players flag to " + + "your command if wish to teleport all players out of the world."); + } + throw new MultiverseException("An unknown error occurred while unloading world: " + world.getName()); + } + Logging.fine("Bukkit unloaded world: " + world.getName()); + }).andFinally(() -> unloadTracker.remove(world.getName())); + } + + /** + * Gets a list of all potential worlds that can be loaded from the server folders. + * Checks based on folder contents and name. + * + * @return A list of all potential worlds. + */ + public List getPotentialWorlds() { + File[] files = Bukkit.getWorldContainer().listFiles(); + if (files == null) { + return Collections.emptyList(); + } + return Arrays.stream(files) + .filter(file -> !isWorld(file.getName())) + .filter(worldNameChecker::isValidWorldFolder) + .map(File::getName) + .toList(); + } + + /** + * Get a world that may or may not be loaded. It will an {@link LoadedMultiverseWorld} if the world is loaded, + * otherwise returns an {@link MultiverseWorld} instance. + * + * @param world The bukkit world to get. + * @return The world if it exists. + */ + public Option getWorld(@Nullable World world) { + return Option.of(world).map(World::getName).flatMap(this::getWorld); + } + + /** + * Get a world that may or may not be loaded. It will an {@link LoadedMultiverseWorld} if the world is loaded, + * otherwise returns an {@link MultiverseWorld} instance. + * + * @param worldName The name of the world to get. + * @return The world if it exists. + */ + public Option getWorld(@Nullable String worldName) { + return getLoadedWorld(worldName).fold(() -> getUnloadedWorld(worldName), Option::of); + } + + /** + * Get a world that may or may not be loaded by name or alias. It will an {@link LoadedMultiverseWorld} if the world is loaded, + * otherwise returns an {@link MultiverseWorld} instance. World name will still be prioritized over alias. + * + * @param worldNameOrAlias The name or alias of the world to get. + * @return The world if it exists. + */ + public Option getWorldByNameOrAlias(@Nullable String worldNameOrAlias) { + return getLoadedWorldByNameOrAlias(worldNameOrAlias) + .fold(() -> getUnloadedWorldByNameOrAlias(worldNameOrAlias), Option::of); + } + + /** + *

Get a list of all worlds that may or may not be loaded. It will an {@link LoadedMultiverseWorld} if the world + * is loaded, otherwise you will get an {@link MultiverseWorld} instance.

+ * + *

If you want only unloaded worlds, use {@link #getUnloadedWorlds()}. If you want only loaded worlds, use + * {@link #getLoadedWorlds()}.

+ * + * @return A list of all worlds that may or may not be loaded. + */ + public Collection getWorlds() { + return worldsMap.values().stream() + .map(world -> getLoadedWorld(world) + .fold(() -> (MultiverseWorld) world, loadedWorld -> loadedWorld)) + .toList(); + } + + /** + * Check if a world is a world is known to multiverse, but may or may not be loaded. + * + * @param worldName The name of the world to check. + * @return True if the world is a world is known to multiverse, but may or may not be loaded. + */ + public boolean isWorld(@Nullable String worldName) { + return worldsMap.containsKey(worldName); + } + + /** + * Get a world that is not loaded. + * + * @param worldName The name of the world to get. + * @return The world if it exists. + */ + public Option getUnloadedWorld(@Nullable String worldName) { + return isLoadedWorld(worldName) ? Option.none() : Option.of(worldsMap.get(worldName)); + } + + /** + * Get a world that is not loaded by name or alias. World name will still be prioritized over alias. + * + * @param worldNameOrAlias The name or alias of the world to get. + * @return The world if it exists. + */ + public Option getUnloadedWorldByNameOrAlias(@Nullable String worldNameOrAlias) { + return getUnloadedWorld(worldNameOrAlias) + .orElse(() -> Option.ofOptional(worldsMap.values().stream() + .filter(world -> !world.isLoaded()) + .filter(world -> world.getAlias().equalsIgnoreCase(worldNameOrAlias)) + .findFirst())); + } + + /** + * Get a list of all worlds that are not loaded. + * + * @return A list of all worlds that are not loaded. + */ + public Collection getUnloadedWorlds() { + return worldsMap.values().stream() + .filter(world -> !world.isLoaded()) + .map(world -> (MultiverseWorld) world) + .toList(); + } + + /** + * Check if a world is a world that is not loaded. + * + * @param worldName The name of the world to check. + * @return True if the world is a world that is not loaded. + */ + public boolean isUnloadedWorld(@Nullable String worldName) { + return !isLoadedWorld(worldName) && isWorld(worldName); + } + + /** + * Get a multiverse world that is loaded. + * + * @param world The bukkit world that should be loaded. + * @return The multiverse world if it exists. + */ + public Option getLoadedWorld(@Nullable World world) { + return world == null ? Option.none() : Option.of((LoadedMultiverseWorld) loadedWorldsMap.get(world.getName())); + } + + /** + * Get a multiverse world that is loaded. + * + * @param world The world that should be loaded. + * @return The multiverse world if it exists. + */ + public Option getLoadedWorld(@Nullable MultiverseWorld world) { + return world == null ? Option.none() : Option.of((LoadedMultiverseWorld) loadedWorldsMap.get(world.getName())); + } + + /** + * Get a multiverse world that is loaded. + * + * @param worldName The name of the world to get. + * @return The multiverse world if it exists. + */ + public Option getLoadedWorld(@Nullable String worldName) { + return Option.of((LoadedMultiverseWorld) loadedWorldsMap.get(worldName)); + } + + /** + * Get a multiverse world that is loaded by name or alias. World name will still be prioritized over alias. + * + * @param worldNameOrAlias The name or alias of the world to get. + * @return The multiverse world if it exists. + */ + public Option getLoadedWorldByNameOrAlias(@Nullable String worldNameOrAlias) { + return getLoadedWorld(worldNameOrAlias) + .orElse(() -> Option.ofOptional(loadedWorldsMap.values().stream() + .filter(world -> world.getAlias().equalsIgnoreCase(worldNameOrAlias)) + .map(world -> (LoadedMultiverseWorld) world) + .findFirst())); + } + + /** + * Get a read-only list of all multiverse worlds that are loaded. + * + * @return A list of all multiverse worlds that are loaded. + */ + public Collection getLoadedWorlds() { + return loadedWorldsMap.values().stream() + .map(world -> (LoadedMultiverseWorld) world) + .toList(); + } + + /** + * Check if a world is a multiverse world that is loaded. + * + * @param world The bukkit world to check. + * @return True if the world is a multiverse world that is loaded. + */ + public boolean isLoadedWorld(@Nullable World world) { + return world != null && isLoadedWorld(world.getName()); + } + + /** + * Check if a world is a multiverse world that is loaded. + * + * @param world The world to check. + * @return True if the world is a multiverse world that is loaded. + */ + public boolean isLoadedWorld(@Nullable MultiverseWorld world) { + return world != null && isLoadedWorld(world.getName()); + } + + /** + * Check if a world is a multiverse world that is loaded. + * + * @param worldName The name of the world to check. + * @return True if the world is a multiverse world that is loaded. + */ + public boolean isLoadedWorld(@Nullable String worldName) { + return loadedWorldsMap.containsKey(worldName); + } + + /** + * Gets the default world defined by `level-name` in server.properties. If server.properties is not found, + * the first world in the bukkit world list will be returned. + *
+ * This world cannot be unloaded. + * + * @return The default world. + */ + public Option getDefaultWorld() { + return serverProperties.getLevelName().flatMap(this::getLoadedWorld) + .orElse(getLoadedWorld(Bukkit.getWorlds().get(0))); + } + + /** + * Saves the worlds.yml config. + * + * @return true if it had successfully saved the file. + */ + public boolean saveWorldsConfig() { + return worldsConfigManager.save() + .onFailure(failure -> { + Logging.severe("Failed to save worlds config: %s", failure); + failure.printStackTrace(); + }) + .isSuccess(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java new file mode 100644 index 000000000..86d2ee351 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldsConfigManager.java @@ -0,0 +1,228 @@ +package org.mvplugins.multiverse.core.world; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Option; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; + +import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; + +/** + * Manages the worlds.yml file. + */ +@Service +final class WorldsConfigManager { + private static final String CONFIG_FILENAME = "worlds.yml"; + + private final SortedMap worldConfigMap; + private final File worldConfigFile; + private YamlConfiguration worldsConfig; + + private final MultiverseCore multiverseCore; + + @Inject + WorldsConfigManager(@NotNull MultiverseCore core, @NotNull MultiverseCore multiverseCore) { + worldConfigMap = new TreeMap<>(); + worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile(); + + this.multiverseCore = multiverseCore; + } + + /** + * Loads the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist. + * + * @return A {@link NewAndRemovedWorlds} record. + */ + public Try load() { + return Try.of(() -> { + loadWorldYmlFile(); + return parseNewAndRemovedWorlds(); + }); + } + + /** + * Loads the worlds.yml file. + * + * @throws IOException If an error occurs while loading the file. + * @throws InvalidConfigurationException If the file is not a valid YAML file. + */ + private void loadWorldYmlFile() throws IOException, InvalidConfigurationException { + boolean exists = worldConfigFile.exists(); + if (!exists && !worldConfigFile.createNewFile()) { + throw new IllegalStateException("Could not create worlds.yml config file"); + } + if (exists) { + migrateRemoveOldConfigSerializable(); + } + worldsConfig = new YamlConfiguration(); + worldsConfig.load(worldConfigFile); + } + + private void migrateRemoveOldConfigSerializable() { + Try.of(() -> Files.readString(worldConfigFile.toPath())) + .mapTry(configData -> { + if (!configData.contains("==: MVWorld")) { + throw new ConfigMigratedException(); + } + + // Copy old config file to `worlds.yml.old` + Path oldWorldConfig = worldConfigFile.toPath().getParent().resolve(CONFIG_FILENAME + ".old"); + Files.copy(worldConfigFile.toPath(), oldWorldConfig, COPY_ATTRIBUTES); + + return configData.replace("==: MVWorld", "") + .replace("==: MVSpawnSettings", "") + .replace("==: MVSpawnSubSettings", "") + .replace("==: MVEntryFee", ""); + }) + .andThenTry(configData -> Files.writeString(worldConfigFile.toPath(), configData)) + .andThenTry(() -> { + YamlConfiguration config = YamlConfiguration.loadConfiguration(worldConfigFile); + List worlds = config.getConfigurationSection("worlds") + .getKeys(false) + .stream() + .map(worldName -> config.getConfigurationSection("worlds." + worldName)) + .toList(); + + config.set("worlds", null); + + for (ConfigurationSection world : worlds) { + config.createSection(world.getName(), world.getValues(true)); + } + config.save(worldConfigFile); + }) + .onFailure(e -> { + if (e instanceof ConfigMigratedException) { + Logging.fine("Config already migrated"); + return; + } + Logging.warning("Failed to migrate old worlds.yml file: %s", e.getMessage()); + e.printStackTrace(); + }); + } + + /** + * Parses the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist. + * + * @return A tuple containing a list of the new WorldConfigs added and a list of the worlds removed from the config. + */ + private NewAndRemovedWorlds parseNewAndRemovedWorlds() { + Set allWorldsInConfig = worldsConfig.getKeys(false); + List newWorldsAdded = new ArrayList<>(); + + for (String worldName : allWorldsInConfig) { + getWorldConfig(worldName) + .peek(config -> config.load(getWorldConfigSection(worldName))) + .onEmpty(() -> { + WorldConfig newWorldConfig = new WorldConfig( + worldName, + getWorldConfigSection(worldName), + multiverseCore); + worldConfigMap.put(worldName, newWorldConfig); + newWorldsAdded.add(newWorldConfig); + }); + } + + List worldsRemoved = worldConfigMap.keySet().stream() + .filter(worldName -> !allWorldsInConfig.contains(worldName)) + .toList(); + + for (String s : worldsRemoved) { + worldConfigMap.remove(s); + } + + return new NewAndRemovedWorlds(newWorldsAdded, worldsRemoved); + } + + /** + * Whether the worlds.yml file has been loaded. + * + * @return Whether the worlds.yml file has been loaded. + */ + public boolean isLoaded() { + return worldsConfig != null; + } + + /** + * Saves the worlds.yml file. + * + * @return Whether the save was successful or the error that occurred. + */ + public Try save() { + return Try.run(() -> { + if (!isLoaded()) { + throw new IllegalStateException("WorldsConfigManager is not loaded!"); + } + worldsConfig = new YamlConfiguration(); + worldConfigMap.forEach((worldName, worldConfig) -> + worldsConfig.set(worldName, worldConfig.getConfigurationSection())); + worldsConfig.save(worldConfigFile); + }); + } + + /** + * Gets the {@link WorldConfig} instance of all worlds in the worlds.yml file. + * + * @param worldName The name of the world to check. + * @return The {@link WorldConfig} instance of the world, or empty option if it doesn't exist. + */ + public @NotNull Option getWorldConfig(@NotNull String worldName) { + return Option.of(worldConfigMap.get(worldName)); + } + + /** + * Add a new world to the worlds.yml file. If a world with the given name already exists, an exception is thrown. + * + * @param worldName The name of the world to add. + * @return The newly created {@link WorldConfig} instance. + */ + public @NotNull WorldConfig addWorldConfig(@NotNull String worldName) { + if (worldConfigMap.containsKey(worldName)) { + throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists."); + } + WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName), multiverseCore); + worldConfigMap.put(worldName, worldConfig); + return worldConfig; + } + + /** + * Deletes the world config for the given world. + * + * @param worldName The name of the world to delete. + */ + public void deleteWorldConfig(@NotNull String worldName) { + worldConfigMap.remove(worldName); + worldsConfig.set(worldName, null); + } + + /** + * Gets the {@link ConfigurationSection} for the given world in the worlds.yml file. If the section doesn't exist, + * it is created. + * + * @param worldName The name of the world. + * @return The {@link ConfigurationSection} for the given world. + */ + private ConfigurationSection getWorldConfigSection(String worldName) { + return worldsConfig.isConfigurationSection(worldName) + ? worldsConfig.getConfigurationSection(worldName) + : worldsConfig.createSection(worldName); + } + + private static final class ConfigMigratedException extends RuntimeException { + private ConfigMigratedException() { + super("Config migrated"); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/BlacklistResult.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/BlacklistResult.java new file mode 100644 index 000000000..c721a2daf --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/BlacklistResult.java @@ -0,0 +1,40 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.utils.result.SuccessReason; + +/** + * Result of a world blacklist check. + */ +public final class BlacklistResult { + /** + * Success reasons for a blacklist check. + */ + public enum Success implements SuccessReason { + UNKNOWN_FROM_WORLD, + BYPASSED_BLACKLISTED, + NOT_BLACKLISTED + } + + /** + * Failure reasons for a blacklist check. + */ + public enum Failure implements FailureReason { + BLACKLISTED(MVCorei18n.ENTRYCHECK_BLACKLISTED); + + private final MessageKeyProvider message; + + Failure(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/EntryFeeResult.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/EntryFeeResult.java new file mode 100644 index 000000000..cef65c0a8 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/EntryFeeResult.java @@ -0,0 +1,42 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.utils.result.SuccessReason; + +/** + * Result of a world entry fee check. + */ +public final class EntryFeeResult { + /** + * Success reasons for an entry fee check. + */ + public enum Success implements SuccessReason { + FREE_ENTRY, + ENOUGH_MONEY, + EXEMPT_FROM_ENTRY_FEE, + CONSOLE_OR_BLOCK_COMMAND_SENDER + } + + /** + * Failure reasons for an entry fee check. + */ + public enum Failure implements FailureReason { + NOT_ENOUGH_MONEY(MVCorei18n.ENTRYCHECK_NOTENOUGHMONEY), + CANNOT_PAY_ENTRY_FEE(MVCorei18n.ENTRYCHECK_CANNOTPAYENTRYFEE); + + private final MessageKeyProvider message; + + Failure(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/PlayerLimitResult.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/PlayerLimitResult.java new file mode 100644 index 000000000..79a184a5a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/PlayerLimitResult.java @@ -0,0 +1,40 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.utils.result.SuccessReason; + +/** + * Result of a world player limit check. + */ +public final class PlayerLimitResult { + /** + * Success reasons for a player limit check. + */ + public enum Success implements SuccessReason { + NO_PLAYERLIMIT, + WITHIN_PLAYERLIMIT, + BYPASS_PLAYERLIMIT + } + + /** + * Failure reasons for a player limit check. + */ + public enum Failure implements FailureReason { + EXCEED_PLAYERLIMIT(MVCorei18n.ENTRYCHECK_EXCEEDPLAYERLIMIT); + + private final MessageKeyProvider message; + + Failure(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldAccessResult.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldAccessResult.java new file mode 100644 index 000000000..101bd9533 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldAccessResult.java @@ -0,0 +1,39 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; +import org.mvplugins.multiverse.core.utils.result.SuccessReason; + +/** + * Result of a world access check. + */ +public final class WorldAccessResult { + /** + * Success reasons for a world access check. + */ + public enum Success implements SuccessReason { + NO_ENFORCE_WORLD_ACCESS, + HAS_WORLD_ACCESS + } + + /** + * Failure reasons for a world access check. + */ + public enum Failure implements FailureReason { + NO_WORLD_ACCESS(MVCorei18n.ENTRYCHECK_NOWORLDACCESS); + + private final MessageKeyProvider message; + + Failure(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryChecker.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryChecker.java new file mode 100644 index 000000000..c50a15cc8 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryChecker.java @@ -0,0 +1,157 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import java.util.Collection; + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.utils.result.Result; +import org.mvplugins.multiverse.core.utils.result.ResultChain; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace; + +/** + * Checks if a player can enter a world. + */ +public final class WorldEntryChecker { + private final @NotNull MVCoreConfig config; + private final @NotNull MVEconomist economist; + private final @NotNull CorePermissionsChecker permissionsChecker; + + private final @NotNull CommandSender sender; + + WorldEntryChecker( + @NotNull MVCoreConfig config, + @NotNull CorePermissionsChecker permissionsChecker, + @NotNull MVEconomist economist, + @NotNull CommandSender sender) { + this.config = config; + this.permissionsChecker = permissionsChecker; + this.economist = economist; + this.sender = sender; + } + + /** + * Checks if the sender have access to be in the world. + * + * @param world The world to check. + * @return The result of the check. + */ + public ResultChain canStayInWorld(@NotNull LoadedMultiverseWorld world) { + return ResultChain.builder() + .then(() -> canAccessWorld(world)) + .then(() -> isWithinPlayerLimit(world)) + .build(); + } + + /** + * Checks if the sender can enter the given world. + * + * @param fromWorld The world the sender is coming from. + * @param toWorld The world the sender is going to. + * @return The result of the check. + */ + public ResultChain canEnterWorld( + @Nullable MultiverseWorld fromWorld, @NotNull LoadedMultiverseWorld toWorld) { + return ResultChain.builder() + .then(() -> canAccessWorld(toWorld)) + .then(() -> isWithinPlayerLimit(toWorld)) + .then(() -> isNotBlacklisted(fromWorld, toWorld)) + .then(() -> canPayEntryFee(toWorld)) + .build(); + } + + /** + * Checks if the sender can access the given world. + * + * @param world The world to check. + * @return The result of the check. + */ + public Result canAccessWorld(@NotNull MultiverseWorld world) { + if (!config.getEnforceAccess()) { + return Result.success(WorldAccessResult.Success.NO_ENFORCE_WORLD_ACCESS); + } + return permissionsChecker.hasWorldAccessPermission(this.sender, world) + ? Result.success(WorldAccessResult.Success.HAS_WORLD_ACCESS) + : Result.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS); + } + + /** + * Checks if the sender is within the player limit of the given world. + * + * @param world The world to check. + * @return The result of the check. + */ + public Result isWithinPlayerLimit( + @NotNull LoadedMultiverseWorld world) { + final int playerLimit = world.getPlayerLimit(); + if (playerLimit <= -1) { + return Result.success(PlayerLimitResult.Success.NO_PLAYERLIMIT); + } + if (permissionsChecker.hasPlayerLimitBypassPermission(sender, world)) { + return Result.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT); + } + int numberOfPlayersInWorld = world.getBukkitWorld().map(World::getPlayers) + .map(Collection::size) + .getOrElse(0); + return playerLimit > numberOfPlayersInWorld + ? Result.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT) + : Result.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT); + } + + /** + * Checks if the sender is not blacklisted from the given world. + * + * @param fromWorld The world the sender is coming from. + * @param toWorld The world the sender is going to. + * @return The result of the check. + */ + public Result isNotBlacklisted( + @Nullable MultiverseWorld fromWorld, @NotNull MultiverseWorld toWorld) { + if (fromWorld == null) { + return Result.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD); + } + return toWorld.getWorldBlacklist().contains(fromWorld.getName()) + ? Result.failure(BlacklistResult.Failure.BLACKLISTED, Replace.WORLD.with(fromWorld.getAlias())) + : Result.success(BlacklistResult.Success.NOT_BLACKLISTED); + } + + /** + * Checks if the sender can pay the entry fee for the given world. + * + * @param world The world to check. + * @return The result of the check. + */ + public Result canPayEntryFee(MultiverseWorld world) { + double price = world.getPrice(); + if (!world.isEntryFeeEnabled() || price == 0D) { + return Result.success(EntryFeeResult.Success.FREE_ENTRY); + } + if (sender instanceof ConsoleCommandSender || sender instanceof BlockCommandSender) { + return Result.success(EntryFeeResult.Success.CONSOLE_OR_BLOCK_COMMAND_SENDER); + } + if (permissionsChecker.hasWorldExemptPermission(sender, world)) { + return Result.success(EntryFeeResult.Success.EXEMPT_FROM_ENTRY_FEE); + } + if (!(sender instanceof Player player)) { + return Result.failure(EntryFeeResult.Failure.CANNOT_PAY_ENTRY_FEE); + } + Material currency = world.getCurrency(); + return economist.isPlayerWealthyEnough(player, price, currency) + ? Result.success(EntryFeeResult.Success.ENOUGH_MONEY) + : Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY, + replace("{amount}").with(economist.formatPrice(price, currency))); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryCheckerProvider.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryCheckerProvider.java new file mode 100644 index 000000000..28ce10b7d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/WorldEntryCheckerProvider.java @@ -0,0 +1,32 @@ +package org.mvplugins.multiverse.core.world.entrycheck; + +import jakarta.inject.Inject; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.config.MVCoreConfig; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; + +@Service +public final class WorldEntryCheckerProvider { + + private final @NotNull MVCoreConfig config; + private final @NotNull MVEconomist economist; + private final @NotNull CorePermissionsChecker permissionsChecker; + + @Inject + WorldEntryCheckerProvider( + @NotNull MVCoreConfig config, + @NotNull MVEconomist economist, + @NotNull CorePermissionsChecker permissionsChecker) { + this.config = config; + this.economist = economist; + this.permissionsChecker = permissionsChecker; + } + + public @NotNull WorldEntryChecker forSender(@NotNull CommandSender sender) { + return new WorldEntryChecker(config, permissionsChecker, economist, sender); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/package-info.java new file mode 100644 index 000000000..399c47b19 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/entrycheck/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.entrycheck; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorPlugin.java b/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorPlugin.java new file mode 100644 index 000000000..8273e9e0c --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorPlugin.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.world.generators; + +import java.util.Collection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + *

A generator API for Multiverse.

+ * + *

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.

+ */ +public interface GeneratorPlugin { + /** + *

Suggest possible generator ids. To be used in command tab-completion.

+ * + *

These suggestions can be static without relying on currentIdInput, or dynamically changed based + * on the currentIdInput.

+ * + * @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 suggestIds(@Nullable String currentIdInput); + + /** + *

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.

+ * + *

An example command: '/mv create myworld normal -g CoolGen:FunWorld'

+ * + *

Notes on usage of this method:

+ *
    + *
  • Feel free to have colors in your command usage, but not Multiverse won't parse color codes for you.
  • + *
  • Please include the starting slash '/' in your usage examples.
  • + *
  • We suggest keeping the usage to at most 5 examples.
  • + *
  • 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.
  • + *
+ * + * @return A collection of command usage examples. + */ + @Nullable Collection getExampleUsages(); + + /** + *

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.

+ * + *

An example info: 'Click on https://www.amazinggenerator.io ;)'

+ * + *

Some suggested places you can link to are: spigot resource page, github repo or your own plugin site.

+ * + * @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 String getPluginName(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorProvider.java b/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorProvider.java new file mode 100644 index 000000000..8dd4fd006 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/generators/GeneratorProvider.java @@ -0,0 +1,212 @@ +package org.mvplugins.multiverse.core.world.generators; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import jakarta.inject.Inject; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +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.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.MultiverseCore; +import org.mvplugins.multiverse.core.utils.FileUtils; + +/** + * Parse the default world generators from the bukkit config and load any generator plugins. + * Helps in suggesting and validating generator strings. + */ +@Service +public final class GeneratorProvider implements Listener { + private final Map defaultGenerators; + private final Map generatorPlugins; + private final FileUtils fileUtils; + + @Inject + GeneratorProvider(@NotNull MultiverseCore multiverseCore, @NotNull FileUtils fileUtils) { + this.fileUtils = fileUtils; + defaultGenerators = new HashMap<>(); + generatorPlugins = new HashMap<>(); + + Bukkit.getPluginManager().registerEvents(this, multiverseCore); + loadDefaultWorldGenerators(); + loadPluginGenerators(); + } + + /** + * Load the default world generators string from the bukkit config. + */ + private void loadDefaultWorldGenerators() { + File bukkitConfigFile = fileUtils.getBukkitConfig(); + if (bukkitConfigFile == null) { + Logging.warning("Any default world generators will not be loaded!"); + return; + } + + FileConfiguration bukkitConfig = YamlConfiguration.loadConfiguration(bukkitConfigFile); + ConfigurationSection worldSection = bukkitConfig.getConfigurationSection("worlds"); + if (worldSection != null) { + Set keys = worldSection.getKeys(false); + keys.forEach(key -> defaultGenerators.put(key, bukkitConfig.getString("worlds." + key + ".generator", ""))); + } + } + + /** + * Find generator plugins from plugins loaded and register them. + */ + private void loadPluginGenerators() { + Arrays.stream(Bukkit.getPluginManager().getPlugins()).forEach(plugin -> { + if (testIsGeneratorPlugin(plugin)) { + registerGeneratorPlugin(new SimpleGeneratorPlugin(plugin.getName())); + } + }); + } + + /** + * Basic test if a plugin is a generator plugin. + * + * @param plugin The plugin to test. + * @return True if the plugin is a generator plugin, else false. + */ + private boolean testIsGeneratorPlugin(Plugin plugin) { + String worldName = Bukkit.getWorlds().stream().findFirst().map(World::getName).orElse("world"); + return Try.of(() -> plugin.getDefaultWorldGenerator(worldName, "") != null) + .recover(IllegalArgumentException.class, true) + .recover(throwable -> { + Logging.warning("Plugin %s threw an exception when testing if it is a generator plugin!", + plugin.getName()); + throwable.printStackTrace(); + return false; + }).getOrElse(false); + } + + /** + * Gets the default generator for a world from the bukkit.yml config. + * + * @param worldName The name of the world. + * @return The default generator string for the world, or null if none. + */ + public @Nullable String getDefaultGeneratorForWorld(String worldName) { + return defaultGenerators.getOrDefault(worldName, null); + } + + /** + * Attempts to register a plugin as {@link SimpleGeneratorPlugin}. + * + * @param generatorPlugin The plugin to register. + * @return True if registered successfully, else false. + */ + public boolean registerGeneratorPlugin(@NotNull GeneratorPlugin generatorPlugin) { + var registeredGenerator = generatorPlugins.get(generatorPlugin.getPluginName()); + if (registeredGenerator == null || registeredGenerator instanceof SimpleGeneratorPlugin) { + generatorPlugins.put(generatorPlugin.getPluginName(), generatorPlugin); + return true; + } + Logging.severe("Generator plugin with name %s is already registered!", generatorPlugin.getPluginName()); + return false; + } + + /** + * Unregisters a plugin. + * + * @param pluginName The plugin to unregister. + * @return True if the plugin was present and now unregistered, else false. + */ + public boolean unregisterGeneratorPlugin(@NotNull String pluginName) { + if (generatorPlugins.containsKey(pluginName)) { + generatorPlugins.remove(pluginName); + return true; + } + Logging.severe("Generator plugin with name %s is not registered!", pluginName); + return false; + } + + /** + * Whether a plugin is registered as a generator plugin. + * + * @param pluginName The name of the plugin. + * @return True if the plugin is registered, else false. + */ + public boolean isGeneratorPluginRegistered(@NotNull String pluginName) { + return generatorPlugins.containsKey(pluginName); + } + + /** + * Gets a generator plugin by name. + * + * @param pluginName The name of the plugin. + * @return The generator plugin, or null if not registered. + */ + public @Nullable GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName) { + return generatorPlugins.get(pluginName); + } + + /** + * Auto complete generator strings, used in command tab completion. + * + * @param currentInput The current input from the user. + * @return A collection of suggestions. + */ + public Collection suggestGeneratorString(@Nullable String currentInput) { + String[] genSpilt = currentInput == null ? new String[0] : currentInput.split(":", 2); + List suggestions = new ArrayList<>(generatorPlugins.keySet()); + if (genSpilt.length < 2) { + return suggestions; + } + GeneratorPlugin generatorPlugin = generatorPlugins.get(genSpilt[0]); + if (generatorPlugin == null) { + return suggestions; + } + suggestions.addAll(generatorPlugin.suggestIds(genSpilt[1]).stream().map(id -> genSpilt[0] + ":" + id).toList()); + return suggestions; + } + + /** + * Listen to plugins enabled to see if they are generator plugins. + * + * @param event The plugin enable event. + */ + @EventHandler + private void onPluginEnable(PluginEnableEvent event) { + if (!testIsGeneratorPlugin(event.getPlugin())) { + Logging.finest("Plugin %s is not a generator plugin.", event.getPlugin().getName()); + return; + } + if (!registerGeneratorPlugin(new SimpleGeneratorPlugin(event.getPlugin().getName()))) { + Logging.severe("Failed to register generator plugin %s!", event.getPlugin().getName()); + } + } + + /** + * Listen to plugins disabled to see if they are generator plugins. If so, unregister them. + * + * @param event The plugin disable event. + */ + @EventHandler + private void onPluginDisable(PluginDisableEvent event) { + if (!isGeneratorPluginRegistered(event.getPlugin().getName())) { + Logging.finest("Plugin %s is not a generator plugin.", event.getPlugin().getName()); + return; + } + if (!unregisterGeneratorPlugin(event.getPlugin().getName())) { + Logging.severe("Failed to unregister generator plugin %s!", event.getPlugin().getName()); + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/generators/SimpleGeneratorPlugin.java b/src/main/java/org/mvplugins/multiverse/core/world/generators/SimpleGeneratorPlugin.java new file mode 100644 index 000000000..5a1c435ce --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/generators/SimpleGeneratorPlugin.java @@ -0,0 +1,51 @@ +package org.mvplugins.multiverse.core.world.generators; + +import java.util.Collection; +import java.util.Collections; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A default implementation of {@link GeneratorPlugin} for those generator plugins that do not provide their own + * custom {@link GeneratorPlugin} implementation to Multiverse. + */ +public final class SimpleGeneratorPlugin implements GeneratorPlugin { + private final String pluginName; + + SimpleGeneratorPlugin(@NotNull String pluginName) { + this.pluginName = pluginName; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Collection suggestIds(@Nullable String currentIdInput) { + return Collections.emptyList(); + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable Collection getExampleUsages() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable String getInfoLink() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull String getPluginName() { + return pluginName; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/generators/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/generators/package-info.java new file mode 100644 index 000000000..1765e7fb1 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/generators/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.generators; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataStore.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataStore.java new file mode 100644 index 000000000..b24a7b3be --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataStore.java @@ -0,0 +1,170 @@ +package org.mvplugins.multiverse.core.world.helpers; + +import java.util.HashMap; +import java.util.Map; + +import com.dumptruckman.minecraft.util.Logging; +import io.vavr.control.Try; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + +/** + * A data store for storing and restoring data from an object. + * + * @param The type of the object to store data from. + */ +@Service +public interface DataStore { + /** + * Stores the data from the given object in this {@link DataStore} instance. + * + * @param object The object to copy data from. + * @return This {@link DataStore} instance. + */ + DataStore copyFrom(T object); + + /** + * Copies the data from this {@link DataStore} instance to the given object. + * + * @param object The object to paste data to. + * @return This {@link DataStore} instance. + */ + DataStore pasteTo(T object); + + /** + * A {@link DataStore} for storing and restoring game rules for a multiverse world. + */ + class GameRulesStore implements DataStore { + private Map, Object> gameRuleMap; + + /** + * {@inheritDoc} + */ + @Override + public GameRulesStore copyFrom(LoadedMultiverseWorld world) { + this.gameRuleMap = new HashMap<>(); + world.getBukkitWorld().peek(bukkitWorld -> { + for (String gameRule : bukkitWorld.getGameRules()) { + GameRule gameRuleEnum = GameRule.getByName(gameRule); + if (gameRuleEnum == null) { + continue; + } + gameRuleMap.put(gameRuleEnum, bukkitWorld.getGameRuleValue(gameRuleEnum)); + } + }); + Logging.finer("Copied " + gameRuleMap.size() + " game rules from world " + world.getName()); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public GameRulesStore pasteTo(LoadedMultiverseWorld world) { + if (gameRuleMap == null) { + return this; + } + world.getBukkitWorld().peek(bukkitWorld -> { + for (String gameRule : bukkitWorld.getGameRules()) { + GameRule gameRuleEnum = GameRule.getByName(gameRule); + if (gameRuleEnum == null) { + continue; + } + setGameRuleValue(bukkitWorld, gameRuleEnum, gameRuleMap.get(gameRuleEnum)).onFailure(e -> { + Logging.warning("Failed to set game rule " + gameRuleEnum.getName() + " to " + gameRuleMap.get(gameRuleEnum)); + e.printStackTrace(); + }); + } + }); + return this; + } + + private Try setGameRuleValue(World world, GameRule gameRule, Object value) { + return Try.run(() -> world.setGameRule(gameRule, (T) value)); + } + } + + /** + * A {@link DataStore} for storing and restoring world properties for a multiverse world. + */ + class WorldConfigStore implements DataStore { + private Map configMap; + + /** + * {@inheritDoc} + */ + @Override + public WorldConfigStore copyFrom(LoadedMultiverseWorld world) { + this.configMap = new HashMap<>(); + StringPropertyHandle worldPropertyHandler = world.getStringPropertyHandle(); + worldPropertyHandler.getAllPropertyNames().forEach(name -> worldPropertyHandler.getProperty(name) + .peek(value -> configMap.put(name, value)).onFailure(e -> { + Logging.warning("Failed to get property " + name + " from world " + + world.getName() + ": " + e.getMessage()); + })); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public WorldConfigStore pasteTo(LoadedMultiverseWorld world) { + if (configMap == null) { + return this; + } + StringPropertyHandle worldPropertyHandler = world.getStringPropertyHandle(); + configMap.forEach((name, value) -> worldPropertyHandler.setProperty(name, value).onFailure(e -> { + Logging.warning("Failed to set property %s to %s for world %s: %s", + name, value, world.getName(), e.getMessage()); + })); + return this; + } + } + + /** + * A {@link DataStore} for storing and restoring world border properties for a multiverse world. + */ + class WorldBorderStore implements DataStore { + private double borderCenterX; + private double borderCenterZ; + private double borderDamageAmount; + private double borderDamageBuffer; + private double borderSize; + private int borderTimeRemaining; + + /** + * {@inheritDoc} + */ + @Override + public WorldBorderStore copyFrom(LoadedMultiverseWorld world) { + world.getBukkitWorld().peek(bukkitWorld -> { + borderCenterX = bukkitWorld.getWorldBorder().getCenter().getX(); + borderCenterZ = bukkitWorld.getWorldBorder().getCenter().getZ(); + borderDamageAmount = bukkitWorld.getWorldBorder().getDamageAmount(); + borderDamageBuffer = bukkitWorld.getWorldBorder().getDamageBuffer(); + borderSize = bukkitWorld.getWorldBorder().getSize(); + borderTimeRemaining = bukkitWorld.getWorldBorder().getWarningTime(); + }); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public WorldBorderStore pasteTo(LoadedMultiverseWorld world) { + world.getBukkitWorld().peek(bukkitWorld -> { + bukkitWorld.getWorldBorder().setCenter(borderCenterX, borderCenterZ); + bukkitWorld.getWorldBorder().setDamageAmount(borderDamageAmount); + bukkitWorld.getWorldBorder().setDamageBuffer(borderDamageBuffer); + bukkitWorld.getWorldBorder().setSize(borderSize); + bukkitWorld.getWorldBorder().setWarningTime(borderTimeRemaining); + }); + return this; + } + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataTransfer.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataTransfer.java new file mode 100644 index 000000000..690412063 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/DataTransfer.java @@ -0,0 +1,43 @@ +package org.mvplugins.multiverse.core.world.helpers; + +import java.util.ArrayList; +import java.util.List; + +/** + * A data transfer for storing and restoring data from multiple {@link DataStore} from one object to another. + * + * @param The type of the object to transfer data from and to. + */ +public final class DataTransfer { + private final List> dataStores; + + /** + * Creates a new {@link DataTransfer} instance. + */ + public DataTransfer() { + this.dataStores = new ArrayList<>(); + } + + /** + * Adds a {@link DataStore} to this {@link DataTransfer} instance. + * + * @param dataStore The {@link DataStore} to add. + * @param object The object to copy data from. + * @return This {@link DataTransfer} instance. + */ + public DataTransfer addDataStore(DataStore dataStore, T object) { + this.dataStores.add(dataStore.copyFrom(object)); + return this; + } + + /** + * Copies the data from all {@link DataStore} instances in this {@link DataTransfer} instance to the given object. + * + * @param object The object to paste data to. + * @return This {@link DataTransfer} instance. + */ + public DataTransfer pasteAllTo(T object) { + this.dataStores.forEach(dataStore -> dataStore.pasteTo(object)); + return this; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/EnforcementHandler.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/EnforcementHandler.java new file mode 100644 index 000000000..038ce3a9d --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/EnforcementHandler.java @@ -0,0 +1,87 @@ +package org.mvplugins.multiverse.core.world.helpers; + +import com.dumptruckman.minecraft.util.Logging; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.WorldManager; + +@Service +public final class EnforcementHandler { + + private final CorePermissionsChecker permissionsChecker; + private final Provider worldManagerProvider; + + @Inject + EnforcementHandler(CorePermissionsChecker permissionsChecker, Provider worldManagerProvider) { + this.permissionsChecker = permissionsChecker; + this.worldManagerProvider = worldManagerProvider; + } + + /** + * Teleports all players to the given location. + * + * @param world The world to teleport players to. + */ + public void handleAllGameModeEnforcement(@NotNull LoadedMultiverseWorld world) { + world.getPlayers().peek(players -> players.forEach(this::handleGameModeEnforcement)); + } + + /** + * Handles game mode enforcement for the given player in the world they are currently in. + * + * @param player The player to enforce game mode for. + */ + public void handleGameModeEnforcement(@NotNull Player player) { + worldManagerProvider.get().getLoadedWorld(player.getWorld()).peek(world -> { + if (permissionsChecker.hasGameModeBypassPermission(player, world)) { + Logging.finer("Player is immune to gamemode enforcement: %s", player.getName()); + return; + } + Logging.finer("Handling gamemode for player in world '%s': %s, Changing to %s", + world.getName(), player.getName(), world.getGameMode()); + player.setGameMode(world.getGameMode()); + }).onEmpty(() -> { + Logging.fine("Player %s is not in a Multiverse world, gamemode enforcement will not apply", + player.getName()); + }); + } + + /** + * Handles flight enforcement for all players in the given world. + * + * @param world The world to enforce flight in. + */ + public void handleAllFlightEnforcement(@NotNull LoadedMultiverseWorld world) { + world.getPlayers().peek(players -> players.forEach(this::handleFlightEnforcement)); + } + + /** + * Handles flight enforcement for the given player in the world they are currently in. + * + * @param player The player to enforce flight for. + */ + public void handleFlightEnforcement(@NotNull Player player) { + worldManagerProvider.get().getLoadedWorld(player.getWorld()).peek(world -> { + if (player.getAllowFlight() && !world.getAllowFlight() && player.getGameMode() != GameMode.CREATIVE) { + player.setAllowFlight(false); + if (player.isFlying()) { + player.setFlying(false); + } + } else if (world.getAllowFlight()) { + if (player.getGameMode() == GameMode.CREATIVE) { + player.setAllowFlight(true); + } + } + }).onEmpty(() -> { + Logging.fine("Player %s is not in a Multiverse world, flight enforcement will not apply", + player.getName()); + }); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/PlayerWorldTeleporter.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/PlayerWorldTeleporter.java new file mode 100644 index 000000000..4db6364a7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/PlayerWorldTeleporter.java @@ -0,0 +1,99 @@ +package org.mvplugins.multiverse.core.world.helpers; + +import java.util.List; + +import jakarta.inject.Inject; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; + +import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.TeleportFailureReason; +import org.mvplugins.multiverse.core.utils.result.Async; +import org.mvplugins.multiverse.core.utils.result.Attempt; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +/** + * Handles all player actions that need to be done when a change in world related activity occurs. + */ +@Service +public final class PlayerWorldTeleporter { + private final AsyncSafetyTeleporter safetyTeleporter; + + @Inject + PlayerWorldTeleporter(@NotNull AsyncSafetyTeleporter safetyTeleporter) { + this.safetyTeleporter = safetyTeleporter; + } + + /** + * Removes all players from the given world. + * + * @param world The world to remove all players from. + * @return A list of async futures that represent the teleportation result of each player. + */ + public Async>> removeFromWorld(@NotNull LoadedMultiverseWorld world) { + // TODO: Better handling of fallback world + World toWorld = Bukkit.getWorlds().get(0); + return transferFromWorldTo(world, toWorld); + } + + /** + * Transfers all players from the given world to another world's spawn location. + * + * @param from The world to transfer players from. + * @param to The location to transfer players to. + * @return A list of async futures that represent the teleportation result of each player. + */ + public Async>> transferFromWorldTo( + @NotNull LoadedMultiverseWorld from, + @NotNull MultiverseWorld to) { + return transferAllFromWorldToLocation(from, to.getSpawnLocation()); + } + + /** + * Transfers all players from the given world to another world's spawn location. + * + * @param from The world to transfer players from. + * @param to The world to transfer players to. + * @return A list of async futures that represent the teleportation result of each player. + */ + public Async>> transferFromWorldTo( + @NotNull LoadedMultiverseWorld from, + @NotNull World to) { + return transferAllFromWorldToLocation(from, to.getSpawnLocation()); + } + + /** + * Transfers all players from the given world to the given location. + * + * @param world The world to transfer players from. + * @param location The location to transfer players to. + * @return A list of async futures that represent the teleportation result of each player. + */ + public Async>> transferAllFromWorldToLocation( + @NotNull LoadedMultiverseWorld world, + @NotNull Location location) { + return world.getPlayers() + .map(players -> safetyTeleporter.to(location).teleport(players)) + .getOrElse(() -> Async.failedFuture( + new IllegalStateException("Unable to get players from world" + world.getName()))); + } + + /** + * Teleports all players to the given world's spawn location. + * + * @param players The players to teleport. + * @param world The world to teleport players to. + * @return A list of async futures that represent the teleportation result of each player. + */ + public Async>> teleportPlayersToWorld( + @NotNull List players, + @NotNull MultiverseWorld world) { + Location spawnLocation = world.getSpawnLocation(); + return safetyTeleporter.to(spawnLocation).teleport(players); + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldNameChecker.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java similarity index 64% rename from src/main/java/com/onarandombox/MultiverseCore/utils/WorldNameChecker.java rename to src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java index 7e4841675..6d4abc720 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/WorldNameChecker.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldNameChecker.java @@ -1,30 +1,32 @@ -package com.onarandombox.MultiverseCore.utils; - -import org.bukkit.Bukkit; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +package org.mvplugins.multiverse.core.world.helpers; import java.io.File; -import java.util.Collections; -import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; +import io.vavr.control.Option; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; + /** *

Utility class in helping to check the status of a world name and it's associated world folder.

* *

Note this is for preliminary checks and better command output. A valid result will suggest but not * 100% determine that a world name can be created, loaded or imported.

*/ -public class WorldNameChecker { +@Service +public final class WorldNameChecker { private static final Pattern WORLD_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9/._-]+"); - private static final Set BLACKLIST_NAMES = Collections.unmodifiableSet(new HashSet() {{ - add("plugins"); - add("logs"); - add("cache"); - add("crash-reports"); - }}); + private static final Set BLACKLIST_NAMES = Set.of( + "cache", + "config", + "crash-reports", + "logs", + "plugins", + "versions"); /** * Checks if a world name is valid. @@ -32,7 +34,7 @@ public class WorldNameChecker { * @param worldName The world name to check on. * @return True if check result is valid, else false. */ - public static boolean isValidWorldName(@Nullable String worldName) { + public boolean isValidWorldName(@Nullable String worldName) { return checkName(worldName) == NameStatus.VALID; } @@ -43,33 +45,48 @@ public static boolean isValidWorldName(@Nullable String worldName) { * @return The resulting name status. */ @NotNull - public static NameStatus checkName(@Nullable String worldName) { - if (BLACKLIST_NAMES.contains(worldName)) { - return NameStatus.BLACKLISTED; - } - if (worldName == null || !WORLD_NAME_PATTERN.matcher(worldName).matches()) { - return NameStatus.INVALID_CHARS; - } - return NameStatus.VALID; + public NameStatus checkName(@Nullable String worldName) { + return Option.of(worldName).map(name -> { + if (name.isEmpty()) { + return NameStatus.EMPTY; + } + if (BLACKLIST_NAMES.contains(name)) { + return NameStatus.BLACKLISTED; + } + if (!WORLD_NAME_PATTERN.matcher(name).matches()) { + return NameStatus.INVALID_CHARS; + } + return NameStatus.VALID; + }).getOrElse(NameStatus.EMPTY); } /** - * Checks if a world name has a valid world folder. + * Check if a world name has a world folder directory. It may not contain valid world data. + * + * @param worldName The world name to check on. + * @return True if the folder exists, else false. + */ + public boolean hasWorldFolder(@Nullable String worldName) { + return checkFolder(worldName) != FolderStatus.DOES_NOT_EXIST; + } + + /** + * Checks if a world name has a valid world folder with basic world data. * * @param worldName The world name to check on. * @return True if check result is valid, else false. */ - public static boolean isValidWorldFolder(@Nullable String worldName) { + public boolean isValidWorldFolder(@Nullable String worldName) { return checkFolder(worldName) == FolderStatus.VALID; } /** - * Checks if a world folder is valid. + * Checks if a world folder is valid with basic world data. * * @param worldFolder The world folder to check on. * @return True if check result is valid, else false. */ - public static boolean isValidWorldFolder(@Nullable File worldFolder) { + public boolean isValidWorldFolder(@Nullable File worldFolder) { return checkFolder(worldFolder) == FolderStatus.VALID; } @@ -80,7 +97,7 @@ public static boolean isValidWorldFolder(@Nullable File worldFolder) { * @return The resulting folder status. */ @NotNull - public static FolderStatus checkFolder(@Nullable String worldName) { + public FolderStatus checkFolder(@Nullable String worldName) { if (worldName == null) { return FolderStatus.DOES_NOT_EXIST; } @@ -95,7 +112,7 @@ public static FolderStatus checkFolder(@Nullable String worldName) { * @return The resulting folder status. */ @NotNull - public static FolderStatus checkFolder(@Nullable File worldFolder) { + public FolderStatus checkFolder(@Nullable File worldFolder) { if (worldFolder == null || !worldFolder.exists() || !worldFolder.isDirectory()) { return FolderStatus.DOES_NOT_EXIST; } @@ -112,7 +129,7 @@ public static FolderStatus checkFolder(@Nullable File worldFolder) { * @param worldFolder The File that may be a world. * @return True if it looks like a world, else false. */ - private static boolean folderHasDat(@NotNull File worldFolder) { + private boolean folderHasDat(@NotNull File worldFolder) { File[] files = worldFolder.listFiles((file, name) -> name.toLowerCase().endsWith(".dat")); return files != null && files.length > 0; } @@ -131,6 +148,11 @@ public enum NameStatus { */ INVALID_CHARS, + /** + * Name string that is null or length 0. + */ + EMPTY, + /** * Name not valid as it is deemed blacklisted. */ diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldPurger.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldPurger.java new file mode 100644 index 000000000..a9432a96a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/WorldPurger.java @@ -0,0 +1,242 @@ +package org.mvplugins.multiverse.core.world.helpers; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Ambient; +import org.bukkit.entity.Animals; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Ghast; +import org.bukkit.entity.Golem; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Phantom; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Slime; +import org.bukkit.entity.Squid; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.world.MultiverseWorld; + +/** + * Used to remove animals from worlds that don't belong there. + * + * @deprecated this class needs a refactor + * @since 5.0 + */ +@Deprecated +@Service +public final class WorldPurger { + + /** + * Synchronizes the given worlds with their settings. + * + * @param worlds A list of {@link LoadedMultiverseWorld} + */ + public void purgeWorlds(@Nullable List worlds) { + if (worlds == null || worlds.isEmpty()) { + return; + } + for (LoadedMultiverseWorld world : worlds) { + this.purgeWorld(world); + } + } + + /** + * Convenience method for {@link #purgeWorld(LoadedMultiverseWorld, java.util.List, boolean, boolean)} that takes + * the settings from the world-config. + * + * @param world The {@link LoadedMultiverseWorld}. + */ + public void purgeWorld(@Nullable LoadedMultiverseWorld world) { + if (world == null) { + return; + } + ArrayList allMobs = new ArrayList<>(world.getSpawningAnimalsExceptions()); + allMobs.addAll(world.getSpawningMonstersExceptions()); + purgeWorld(world, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters()); + } + + /** + * Clear all animals/monsters that do not belong to a world according to the config. + * + * @param world The {@link LoadedMultiverseWorld}. + * @param thingsToKill A {@link List} of animals/monsters to be killed. + * @param negateAnimals Whether the monsters in the list should be negated. + * @param negateMonsters Whether the animals in the list should be negated. + */ + public void purgeWorld( + LoadedMultiverseWorld world, + List thingsToKill, + boolean negateAnimals, + boolean negateMonsters) { + purgeWorld(world, thingsToKill, negateAnimals, negateMonsters, null); + } + + /** + * Clear all animals/monsters that do not belong to a world according to the config. + * + * @param world The {@link LoadedMultiverseWorld}. + * @param thingsToKill A {@link List} of animals/monsters to be killed. + * @param negateAnimals Whether the monsters in the list should be negated. + * @param negateMonsters Whether the animals in the list should be negated. + * @param sender The {@link CommandSender} that initiated the action. He will/should be notified. + */ + public void purgeWorld( + @Nullable LoadedMultiverseWorld world, + @NotNull List thingsToKill, + boolean negateAnimals, + boolean negateMonsters, + CommandSender sender) { + if (world == null) { + return; + } + World bukkitWorld = world.getBukkitWorld().getOrNull(); + if (bukkitWorld == null) { + return; + } + int projectilesKilled = 0; + int entitiesKilled = 0; + boolean specifiedAll = thingsToKill.contains("ALL"); + boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll; + boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll; + List worldEntities = bukkitWorld.getEntities(); + List livingEntities = new ArrayList(worldEntities.size()); + List projectiles = new ArrayList(worldEntities.size()); + for (final Entity e : worldEntities) { + if (e instanceof Projectile p) { + if (p.getShooter() != null) { + projectiles.add((Projectile) e); + } + } else if (e instanceof LivingEntity) { + livingEntities.add((LivingEntity) e); + } + } + for (final LivingEntity e : livingEntities) { + if (killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters)) { + final Iterator it = projectiles.iterator(); + while (it.hasNext()) { + final Projectile p = it.next(); + if (Objects.equals(p.getShooter(), e)) { + p.remove(); + it.remove(); + projectilesKilled++; + } + } + e.remove(); + entitiesKilled++; + } + } + if (sender != null) { + sender.sendMessage(entitiesKilled + " entities purged from the world '" + world.getName() + "' along with " + + projectilesKilled + " projectiles that belonged to them."); + } + } + + /** + * Determines whether the specified creature should be killed and automatically reads the params from a world object. + * + * @param world The world. + * @param entity The creature. + * @return {@code true} if the creature should be killed, otherwise {@code false}. + */ + public boolean shouldWeKillThisCreature(@NotNull MultiverseWorld world, @NotNull Entity entity) { + ArrayList allMobs = new ArrayList<>(world.getSpawningAnimalsExceptions()); + allMobs.addAll(world.getSpawningMonstersExceptions()); + return this.shouldWeKillThisCreature(entity, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters()); + } + + /** + * Determines whether the specified creature should be killed. + * + * @param entity The creature. + * @param thingsToKill A {@link List} of animals/monsters to be killed. + * @param negateAnimals Whether the monsters in the list should be negated. + * @param negateMonsters Whether the animals in the list should be negated. + * @return {@code true} if the creature should be killed, otherwise {@code false}. + */ + public boolean shouldWeKillThisCreature( + Entity entity, + List thingsToKill, + boolean negateAnimals, + boolean negateMonsters) { + boolean specifiedAll = thingsToKill.contains("ALL"); + boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll; + boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll; + return this.killDecision( + entity, + thingsToKill, + negateAnimals, + negateMonsters, + specifiedAnimals, + specifiedMonsters); + } + + private boolean killDecision( + Entity entity, + List thingsToKill, + boolean negateAnimals, + boolean negateMonsters, + boolean specifiedAnimals, + boolean specifiedMonsters) { + boolean negate = false; + boolean specified = false; + if (entity instanceof Golem + || entity instanceof Squid + || entity instanceof Animals + || entity instanceof Ambient) { + // it's an animal + if (specifiedAnimals && !negateAnimals) { + Logging.finest("Removing an entity because I was told to remove all animals in world %s: %s", + entity.getWorld().getName(), entity); + return true; + } + if (specifiedAnimals) { + specified = true; + } + negate = negateAnimals; + } else if (entity instanceof Monster + || entity instanceof Ghast + || entity instanceof Slime + || entity instanceof Phantom) { + // it's a monster + if (specifiedMonsters && !negateMonsters) { + Logging.finest("Removing an entity because I was told to remove all monsters in world %s: %s", + entity.getWorld().getName(), entity); + return true; + } + if (specifiedMonsters) { + specified = true; + } + negate = negateMonsters; + } + for (String s : thingsToKill) { + EntityType type = EntityType.fromName(s); + if (type != null && type.equals(entity.getType())) { + specified = true; + if (!negate) { + Logging.finest( + "Removing an entity because it WAS specified and we are NOT negating in world %s: %s", + entity.getWorld().getName(), entity); + return true; + } + break; + } + } + if (!specified && negate) { + Logging.finest("Removing an entity because it was NOT specified and we ARE negating in world %s: %s", + entity.getWorld().getName(), entity); + return true; + } + + return false; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/helpers/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/helpers/package-info.java new file mode 100644 index 000000000..777d759b3 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/helpers/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.helpers; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/location/NullLocation.java b/src/main/java/org/mvplugins/multiverse/core/world/location/NullLocation.java new file mode 100644 index 000000000..f5a303681 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/location/NullLocation.java @@ -0,0 +1,70 @@ +package org.mvplugins.multiverse.core.world.location; + +import java.util.Collections; +import java.util.Map; + +import org.bukkit.Location; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; + +/** + * Null-location. + */ +@SerializableAs("MVNullLocation (It's a bug if you see this in your config file)") +public final class NullLocation extends SpawnLocation { + private static final NullLocation INSTANCE = new NullLocation(); + + /** + * Get the default null location instance. + * + * @return The instance. + */ + public static NullLocation get() { + return INSTANCE; + } + + private NullLocation() { + super(0, -1, 0); + } + + @Override + public @NotNull Location clone() { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull Map serialize() { + return Collections.emptyMap(); + } + + /** + * Let Bukkit be able to deserialize this. + * + * @param args The map. + * @return The deserialized object. + */ + public static NullLocation deserialize(Map args) { + return new NullLocation(); + } + + @Override + public @NotNull Vector toVector() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return -1; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof NullLocation; + } + + @Override + public String toString() { + return "Location{null}"; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnLocation.java b/src/main/java/org/mvplugins/multiverse/core/world/location/SpawnLocation.java similarity index 50% rename from src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnLocation.java rename to src/main/java/org/mvplugins/multiverse/core/world/location/SpawnLocation.java index a09391c17..e6e3a8894 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/configuration/SpawnLocation.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/location/SpawnLocation.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.configuration; +package org.mvplugins.multiverse.core.world.location; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -11,6 +11,7 @@ import org.bukkit.block.Block; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.SerializableAs; +import org.jetbrains.annotations.NotNull; /** * Just like a regular {@link Location}, however {@code world} is usually {@code null} @@ -20,14 +21,35 @@ public class SpawnLocation extends Location implements ConfigurationSerializable { private Reference worldRef; + /** + * Constructs a new Location with the given coordinates. + * + * @param x The x-coordinate of this new location + * @param y The y-coordinate of this new location + * @param z The z-coordinate of this new location + */ public SpawnLocation(double x, double y, double z) { super(null, x, y, z); } + /** + * Constructs a new Location with the given coordinates and direction. + * + * @param x The x-coordinate of this new location + * @param y The y-coordinate of this new location + * @param z The z-coordinate of this new location + * @param yaw The absolute rotation on the x-plane, in degrees + * @param pitch The absolute rotation on the y-plane, in degrees + */ public SpawnLocation(double x, double y, double z, float yaw, float pitch) { super(null, x, y, z, yaw, pitch); } + /** + * Constructs a new Location from an existing Location. + * + * @param loc The location to clone. + */ public SpawnLocation(Location loc) { this(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); } @@ -52,38 +74,43 @@ public void setWorld(World world) { * {@inheritDoc} */ @Override - public Chunk getChunk() { - if ((this.worldRef != null) && (this.worldRef.get() != null)) - return this.worldRef.get().getChunkAt(this); - return null; + public @NotNull Chunk getChunk() { + World world = this.worldRef != null ? this.worldRef.get() : null; + if (world != null) { + return world.getChunkAt(this); + } + throw new IllegalStateException("World is null"); } /** * {@inheritDoc} */ @Override - public Block getBlock() { - if ((this.worldRef != null) && (this.worldRef.get() != null)) - return this.worldRef.get().getBlockAt(this); - return null; + public @NotNull Block getBlock() { + World world = this.worldRef != null ? this.worldRef.get() : null; + if (world != null) { + return world.getBlockAt(this); + } + throw new IllegalStateException("World is null"); } /** * {@inheritDoc} */ @Override - public Map serialize() { - Map serialized = new HashMap(5); // SUPPRESS CHECKSTYLE: MagicNumberCheck - serialized.put("x", this.getX()); - serialized.put("y", this.getY()); - serialized.put("z", this.getZ()); - serialized.put("pitch", this.getPitch()); - serialized.put("yaw", this.getYaw()); - return serialized; + public @NotNull Map serialize() { + var map = new HashMap(); + map.put("x", getX()); + map.put("y", getY()); + map.put("z", getZ()); + map.put("pitch", getPitch()); + map.put("yaw", getYaw()); + return map; } /** * Let Bukkit be able to deserialize this. + * * @param args The map. * @return The deserialized object. */ @@ -95,4 +122,18 @@ public static SpawnLocation deserialize(Map args) { float yaw = ((Number) args.get("yaw")).floatValue(); return new SpawnLocation(x, y, z, yaw, pitch); } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Location{world=" + getWorld() + + ",x=" + this.getX() + + ",y=" + this.getY() + + ",z=" + this.getZ() + + ",pitch=" + this.getPitch() + + ",yaw=" + this.getYaw() + + '}'; + } } diff --git a/src/main/java/org/mvplugins/multiverse/core/world/location/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/location/package-info.java new file mode 100644 index 000000000..04d7bad9f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/location/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.location; \ No newline at end of file diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/CloneWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/CloneWorldOptions.java new file mode 100644 index 000000000..f5fba1f5b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/CloneWorldOptions.java @@ -0,0 +1,118 @@ +package org.mvplugins.multiverse.core.world.options; + +import org.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + +/** + * Options for customizing the cloning of a world. + */ +public final class CloneWorldOptions implements KeepWorldSettingsOptions { + + /** + * Creates a new {@link CloneWorldOptions} instance with the given world. + * + * @param world The world to clone. + * @param newWorldName The name of the new world. + * @return A new {@link CloneWorldOptions} instance. + */ + public static CloneWorldOptions fromTo(LoadedMultiverseWorld world, String newWorldName) { + return new CloneWorldOptions(world, newWorldName); + } + + private final LoadedMultiverseWorld world; + private final String newWorldName; + private boolean keepGameRule = true; + private boolean keepWorldConfig = true; + + private boolean keepWorldBorder = true; + + CloneWorldOptions(LoadedMultiverseWorld world, String newWorldName) { + this.world = world; + this.newWorldName = newWorldName; + } + + /** + * Gets the world to clone. + * + * @return The world to clone. + */ + public LoadedMultiverseWorld world() { + return world; + } + + /** + * Gets the name of the new world. + * + * @return The name of the new world. + */ + public String newWorldName() { + return newWorldName; + } + + /** + * Sets whether to keep the game rule of the world during cloning. + * + * @param keepGameRuleInput Whether to keep the game rule of the world during cloning. + * @return This {@link CloneWorldOptions} instance. + */ + @Override + public @NotNull CloneWorldOptions keepGameRule(boolean keepGameRuleInput) { + this.keepGameRule = keepGameRuleInput; + return this; + } + + /** + * Gets whether to keep the game rule of the world during cloning. + * + * @return Whether to keep the game rule of the world during cloning. + */ + @Override + public boolean keepGameRule() { + return keepGameRule; + } + + /** + * Sets whether to keep the world config of the world during cloning. + * + * @param keepWorldConfigInput Whether to keep the world config of the world. + * @return This {@link CloneWorldOptions} instance. + */ + @Override + public @NotNull CloneWorldOptions keepWorldConfig(boolean keepWorldConfigInput) { + this.keepWorldConfig = keepWorldConfigInput; + return this; + } + + /** + * Gets whether to keep the world config of the world during cloning. + * + * @return Whether to keep the world config of the world during cloning. + */ + @Override + public boolean keepWorldConfig() { + return keepWorldConfig; + } + + + /** + * Sets whether to keep the world border of the world during cloning. + * + * @param keepWorldBorderInput Whether to keep the world border of the world. + * @return This {@link CloneWorldOptions} instance. + */ + @Override + public @NotNull CloneWorldOptions keepWorldBorder(boolean keepWorldBorderInput) { + this.keepWorldBorder = keepWorldBorderInput; + return this; + } + + /** + * Gets whether to keep the world border of the world during cloning. + * + * @return Whether to keep the world border of the world during cloning. + */ + @Override + public boolean keepWorldBorder() { + return keepWorldBorder; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java new file mode 100644 index 000000000..6b6302a2e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/CreateWorldOptions.java @@ -0,0 +1,215 @@ +package org.mvplugins.multiverse.core.world.options; + +import java.util.Random; + +import co.aikar.commands.ACFUtil; +import org.bukkit.World; +import org.bukkit.WorldType; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Options for customizing the creation of a new world. + */ +public final class CreateWorldOptions { + + /** + * Creates a new {@link CreateWorldOptions} instance with the given world name. + * + * @param worldName The name of the world to create. + * @return A new {@link CreateWorldOptions} instance. + */ + public static @NotNull CreateWorldOptions worldName(@NotNull String worldName) { + return new CreateWorldOptions(worldName); + } + + private final String worldName; + private Biome biome; + private World.Environment environment = World.Environment.NORMAL; + private boolean generateStructures = true; + private String generator = null; + private long seed; + private boolean useSpawnAdjust = true; + private WorldType worldType = WorldType.NORMAL; + + /** + * Creates a new {@link CreateWorldOptions} instance with the given world name. + * + * @param worldName The name of the world to create. + */ + CreateWorldOptions(@NotNull String worldName) { + this.worldName = worldName; + this.seed = ACFUtil.RANDOM.nextLong(); + } + + /** + * Gets the name of the world to create. + * + * @return The name of the world to create. + */ + public @NotNull String worldName() { + return worldName; + } + + /** + * Sets the single biome used for this world. This may be null, in which case the biome from the generator will be used. + * If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @param biome The biome used for this world + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions biome(@Nullable Biome biome) { + this.biome = biome; + return this; + } + + /** + * Gets the single biome used for this world. This may be null, in which case the biome from the generator will be used. + * If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @return The biome used for this world + */ + public @NotNull Biome biome() { + return biome; + } + + /** + * Sets the environment of the world to create. + * + * @param environmentInput The environment of the world to create. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions environment(@NotNull World.Environment environmentInput) { + this.environment = environmentInput; + return this; + } + + /** + * Gets the environment of the world to create. + * + * @return The environment of the world to create. + */ + public @NotNull World.Environment environment() { + return environment; + } + + /** + * Sets whether structures such as NPC villages should be generated. + * + * @param generateStructuresInput Whether structures such as NPC villages should be generated. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions generateStructures(boolean generateStructuresInput) { + this.generateStructures = generateStructuresInput; + return this; + } + + /** + * Gets whether structures such as NPC villages should be generated. + * + * @return Whether structures such as NPC villages should be generated. + */ + public boolean generateStructures() { + return generateStructures; + } + + /** + * Sets the custom generator plugin and its parameters. + * + * @param generatorInput The custom generator plugin and its parameters. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions generator(@Nullable String generatorInput) { + this.generator = generatorInput; + return this; + } + + /** + * Gets the custom generator plugin and its parameters. + * + * @return The custom generator plugin and its parameters. + */ + public @Nullable String generator() { + return generator; + } + + /** + * Sets the seed of the world to create. If the seed is a number, it will be parsed as a long. Otherwise, it will be + * hashed. + * + * @param seedInput The seed of the world to create. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions seed(@Nullable String seedInput) { + if (seedInput == null) { + return this; + } + try { + this.seed = Long.parseLong(seedInput); + } catch (NumberFormatException numberformatexception) { + this.seed = seedInput.hashCode(); + } + return this; + } + + /** + * Sets the seed of the world to create. + * + * @param seedInput The seed of the world to create. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions seed(long seedInput) { + this.seed = seedInput; + return this; + } + + /** + * Gets the seed of the world to create. + * + * @return The seed of the world to create. + */ + public long seed() { + return seed; + } + + /** + * Sets whether multiverse will search for a safe spawn location. + * + * @param useSpawnAdjustInput Whether multiverse will search for a safe spawn location. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions useSpawnAdjust(boolean useSpawnAdjustInput) { + this.useSpawnAdjust = useSpawnAdjustInput; + return this; + } + + /** + * Gets whether multiverse will search for a safe spawn location. + * + * @return Whether multiverse will search for a safe spawn location. + */ + public boolean useSpawnAdjust() { + return useSpawnAdjust; + } + + /** + * Sets the world type. + * + * @param worldTypeInput The world type. + * @return This {@link CreateWorldOptions} instance. + */ + public @NotNull CreateWorldOptions worldType(@NotNull WorldType worldTypeInput) { + this.worldType = worldTypeInput; + return this; + } + + /** + * Gets the world type. + * + * @return The world type. + */ + public @NotNull WorldType worldType() { + return worldType; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/ImportWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/ImportWorldOptions.java new file mode 100644 index 000000000..7090b5c16 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/ImportWorldOptions.java @@ -0,0 +1,123 @@ +package org.mvplugins.multiverse.core.world.options; + +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Options for customizing the import of a new world. + */ +public final class ImportWorldOptions { + + /** + * Creates a new {@link ImportWorldOptions} instance with the given world name. + * + * @param worldName The name of the world to create. + * @return A new {@link ImportWorldOptions} instance. + */ + public static @NotNull ImportWorldOptions worldName(@NotNull String worldName) { + return new ImportWorldOptions(worldName); + } + + private final String worldName; + private Biome biome; + private World.Environment environment = World.Environment.NORMAL; + private String generator = null; + private boolean useSpawnAdjust = true; + + ImportWorldOptions(String worldName) { + this.worldName = worldName; + } + + /** + * Gets the name of the world to create. + * + * @return The name of the world to create. + */ + public @NotNull String worldName() { + return worldName; + } + + /** + * Sets the single biome used for this world. This may be null, in which case the biome from the generator will be used. + * If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @param biome The biome used for this world + * @return This {@link ImportWorldOptions} instance. + */ + public @NotNull ImportWorldOptions biome(@Nullable Biome biome) { + this.biome = biome; + return this; + } + + /** + * Gets the single biome used for this world. This may be null, in which case the biome from the generator will be used. + * If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @return The biome used for this world + */ + public @NotNull Biome biome() { + return biome; + } + + /** + * Sets the environment of the world to create. + * + * @param environmentInput The environment of the world to create. + * @return This {@link ImportWorldOptions} instance. + */ + public @NotNull ImportWorldOptions environment(@NotNull World.Environment environmentInput) { + this.environment = environmentInput; + return this; + } + + /** + * Gets the environment of the world to create. + * + * @return The environment of the world to create. + */ + public @NotNull World.Environment environment() { + return environment; + } + + /** + * Sets the custom generator plugin and its parameters. + * + * @param generatorInput The custom generator plugin and its parameters. + * @return This {@link ImportWorldOptions} instance. + */ + public @NotNull ImportWorldOptions generator(@Nullable String generatorInput) { + this.generator = generatorInput; + return this; + } + + /** + * Gets the custom generator plugin and its parameters. + * + * @return The custom generator plugin and its parameters. + */ + public @Nullable String generator() { + return generator; + } + + /** + * Sets whether multiverse will search for a safe spawn location. + * + * @param useSpawnAdjustInput Whether multiverse will search for a safe spawn location. + * @return This {@link ImportWorldOptions} instance. + */ + public @NotNull ImportWorldOptions useSpawnAdjust(boolean useSpawnAdjustInput) { + this.useSpawnAdjust = useSpawnAdjustInput; + return this; + } + + /** + * Gets whether multiverse will search for a safe spawn location. + * + * @return Whether multiverse will search for a safe spawn location. + */ + public boolean useSpawnAdjust() { + return useSpawnAdjust; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/KeepWorldSettingsOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/KeepWorldSettingsOptions.java new file mode 100644 index 000000000..8e501c7eb --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/KeepWorldSettingsOptions.java @@ -0,0 +1,54 @@ +package org.mvplugins.multiverse.core.world.options; + +import org.jetbrains.annotations.NotNull; + +/** + * Options for customizing the keeping of world settings. Used by clone and regen. + */ +public sealed interface KeepWorldSettingsOptions permits CloneWorldOptions, RegenWorldOptions { + + /** + * Sets whether to keep the game rule of the world. + * + * @param keepGameRuleInput Whether to keep the game rule of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepGameRule(boolean keepGameRuleInput); + + /** + * Gets whether to keep the game rule of the world. + * + * @return Whether to keep the game rule of the world. + */ + boolean keepGameRule(); + + /** + * Sets whether to keep the world config of the world. + * + * @param keepWorldConfigInput Whether to keep the world config of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepWorldConfig(boolean keepWorldConfigInput); + + /** + * Gets whether to keep the world config of the world. + * + * @return Whether to keep the world config of the world. + */ + boolean keepWorldConfig(); + + /** + * Sets whether to keep the world border of the world. + * + * @param keepWorldBorderInput Whether to keep the world border of the world. + * @return This {@link KeepWorldSettingsOptions} instance. + */ + @NotNull KeepWorldSettingsOptions keepWorldBorder(boolean keepWorldBorderInput); + + /** + * Gets whether to keep the world border of the world. + * + * @return Whether to keep the world border of the world. + */ + boolean keepWorldBorder(); +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/RegenWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/RegenWorldOptions.java new file mode 100644 index 000000000..a246d95e4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/RegenWorldOptions.java @@ -0,0 +1,214 @@ +package org.mvplugins.multiverse.core.world.options; + +import co.aikar.commands.ACFUtil; +import org.bukkit.block.Biome; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + +/** + * Options for customizing the regeneration of a world. + */ +public final class RegenWorldOptions implements KeepWorldSettingsOptions { + + private static final long UNINITIALIZED_SEED_VALUE = Long.MIN_VALUE; + + /** + * Creates a new {@link RegenWorldOptions} instance with the given world. + * + * @param world The world to regenerate. + * @return A new {@link RegenWorldOptions} instance. + */ + public static @NotNull RegenWorldOptions world(@NotNull LoadedMultiverseWorld world) { + return new RegenWorldOptions(world); + } + + private final LoadedMultiverseWorld world; + private Biome biome; + private boolean keepGameRule = true; + private boolean keepWorldConfig = true; + private boolean keepWorldBorder = true; + private boolean randomSeed = false; + private long seed = UNINITIALIZED_SEED_VALUE; + + RegenWorldOptions(@NotNull LoadedMultiverseWorld world) { + this.world = world; + } + + private boolean isSeedInitialized() { + return seed != UNINITIALIZED_SEED_VALUE; + } + + /** + * Gets the world to regenerate. + * + * @return The world to regenerate. + */ + public @NotNull LoadedMultiverseWorld world() { + return world; + } + + /** + * Sets the single biome used for this world. This may be null, in which case the biome from the generator will be + * used. If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @param biome The biome used for this world + * @return This {@link RegenWorldOptions} instance. + */ + public @NotNull RegenWorldOptions biome(@Nullable Biome biome) { + this.biome = biome; + return this; + } + + /** + * Gets the single biome used for this world. This may be null, in which case the biome from the generator will be + * used. If no generator is specified, the "natural" biome behaviour for this environment will be used. + * + * @return The biome used for this world + */ + public @NotNull Biome biome() { + return biome; + } + + /** + * Sets whether to keep the game rule of the world during regeneration. + * + * @param keepGameRuleInput Whether to keep the game rule of the world during regeneration. + * @return This {@link RegenWorldOptions} instance. + */ + @Override + public @NotNull RegenWorldOptions keepGameRule(boolean keepGameRuleInput) { + this.keepGameRule = keepGameRuleInput; + return this; + } + + /** + * Gets whether to keep the game rule of the world during regeneration. + * + * @return Whether to keep the game rule of the world during regeneration. + */ + @Override + public boolean keepGameRule() { + return keepGameRule; + } + + /** + * Sets whether to keep the world config of the world during regeneration. + * + * @param keepWorldConfigInput Whether to keep the world config of the world during regeneration. + * @return This {@link RegenWorldOptions} instance. + */ + @Override + public @NotNull RegenWorldOptions keepWorldConfig(boolean keepWorldConfigInput) { + this.keepWorldConfig = keepWorldConfigInput; + return this; + } + + /** + * Gets whether to keep the world config of the world during regeneration. + * + * @return Whether to keep the world config of the world during regeneration. + */ + @Override + public boolean keepWorldConfig() { + return keepWorldConfig; + } + + /** + * Sets whether to keep the world border of the world during regeneration. + * + * @param keepWorldBorderInput Whether to keep the world border of the world. + * @return This {@link RegenWorldOptions} instance. + */ + @Override + public @NotNull RegenWorldOptions keepWorldBorder(boolean keepWorldBorderInput) { + this.keepWorldBorder = keepWorldBorderInput; + return this; + } + + /** + * Gets whether to keep the world border of the world during regeneration. + * + * @return Whether to keep the world border of the world during regeneration. + */ + @Override + public boolean keepWorldBorder() { + return keepWorldBorder; + } + + /** + * Sets whether to use a random seed for the world to regenerate. Cannot be set to true when seed is set. + * + * @param randomSeedInput Whether to use a random seed for the world to regenerate. + * @return This {@link RegenWorldOptions} instance. + */ + public @NotNull RegenWorldOptions randomSeed(boolean randomSeedInput) { + if (randomSeedInput && isSeedInitialized()) { + throw new IllegalStateException("Cannot set randomSeed to true when seed is set"); + } + this.randomSeed = randomSeedInput; + return this; + } + + /** + * Gets whether to use a random seed for the world to regenerate. + * + * @return Whether to use a random seed for the world to regenerate. + */ + @SuppressWarnings("unused") + public boolean randomSeed() { + return randomSeed; + } + + /** + * Sets the seed for the world to regenerate. Random seed will be disabled. + * + * @param seedInput The seed for the world to regenerate. + * @return This {@link RegenWorldOptions} instance. + */ + public @NotNull RegenWorldOptions seed(@Nullable String seedInput) { + if (seedInput == null) { + this.seed = UNINITIALIZED_SEED_VALUE; + return this; + } + if (randomSeed) { + randomSeed(false); + } + this.seed = parseOrHashSeed(seedInput); + return this; + } + + private long parseOrHashSeed(String seedInput) { + try { + return Long.parseLong(seedInput); + } catch (NumberFormatException numberformatexception) { + return seedInput.hashCode(); + } + } + + /** + * Sets the seed for the world to regenerate. Random seed will be disabled. + * + * @param seedInput The seed for the world to regenerate. + * @return This {@link RegenWorldOptions} instance. + */ + public @NotNull RegenWorldOptions seed(long seedInput) { + this.seed = seedInput; + return this; + } + + /** + * Gets the seed for the world to regenerate. + * + * @return The seed for the world to regenerate. + */ + public long seed() { + if (randomSeed) { + return ACFUtil.RANDOM.nextLong(); + } else if (isSeedInitialized()) { + return seed; + } + return world.getSeed(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/UnloadWorldOptions.java b/src/main/java/org/mvplugins/multiverse/core/world/options/UnloadWorldOptions.java new file mode 100644 index 000000000..ebbc85ed4 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/UnloadWorldOptions.java @@ -0,0 +1,55 @@ +package org.mvplugins.multiverse.core.world.options; + +import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; + +/** + * Options for customizing the unloading of a world. + */ +public final class UnloadWorldOptions { + + /** + * Creates a new {@link UnloadWorldOptions} instance with the given world. + * + * @param world The world to unload. + * @return A new {@link UnloadWorldOptions} instance. + */ + public static UnloadWorldOptions world(LoadedMultiverseWorld world) { + return new UnloadWorldOptions(world); + } + + private final LoadedMultiverseWorld world; + private boolean saveBukkitWorld = true; + + UnloadWorldOptions(LoadedMultiverseWorld world) { + this.world = world; + } + + /** + * Gets the world to unload. + * + * @return The world to unload. + */ + public LoadedMultiverseWorld world() { + return world; + } + + /** + * Sets whether to save the bukkit world before unloading. + * + * @param saveBukkitWorldInput Whether to save the bukkit world before unloading. + * @return This {@link UnloadWorldOptions} instance. + */ + public UnloadWorldOptions saveBukkitWorld(boolean saveBukkitWorldInput) { + this.saveBukkitWorld = saveBukkitWorldInput; + return this; + } + + /** + * Gets whether to save the bukkit world before unloading. + * + * @return Whether to save the bukkit world before unloading. + */ + public boolean saveBukkitWorld() { + return saveBukkitWorld; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/options/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/options/package-info.java new file mode 100644 index 000000000..c6221aa15 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/options/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.options; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/package-info.java new file mode 100644 index 000000000..be040f95a --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world; diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/CloneFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/CloneFailureReason.java new file mode 100644 index 000000000..1098b7a63 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/CloneFailureReason.java @@ -0,0 +1,56 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world clone operation. + */ +public enum CloneFailureReason implements FailureReason { + /** + * The world name is invalid. + */ + INVALID_WORLDNAME(MVCorei18n.CLONEWORLD_INVALIDWORLDNAME), + + /** + * The target new world folder already exists. + */ + WORLD_EXIST_FOLDER(MVCorei18n.CLONEWORLD_WORLDEXISTFOLDER), + + /** + * The target new world is already exist but unloaded. + */ + WORLD_EXIST_UNLOADED(MVCorei18n.CLONEWORLD_WORLDEXISTUNLOADED), + + /** + * The target new world is already loaded. + */ + WORLD_EXIST_LOADED(MVCorei18n.CLONEWORLD_WORLDEXISTLOADED), + + /** + * Failed to copy the world folder contents. + */ + COPY_FAILED(MVCorei18n.CLONEWORLD_COPYFAILED), + + /** + * Failed to import the new world. + */ + IMPORT_FAILED(MVCorei18n.GENERIC_FAILURE); + + private final MessageKeyProvider message; + + CloneFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/CreateFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/CreateFailureReason.java new file mode 100644 index 000000000..025297f68 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/CreateFailureReason.java @@ -0,0 +1,51 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world creation operation. + */ +public enum CreateFailureReason implements FailureReason { + /** + * The world name is invalid. + */ + INVALID_WORLDNAME(MVCorei18n.CREATEWORLD_INVALIDWORLDNAME), + + /** + * The target new world folder already exists. + */ + WORLD_EXIST_FOLDER(MVCorei18n.CREATEWORLD_WORLDEXISTFOLDER), + + /** + * The target new world is already exist but unloaded. + */ + WORLD_EXIST_UNLOADED(MVCorei18n.CREATEWORLD_WORLDEXISTUNLOADED), + + /** + * The target new world is already exist and loaded. + */ + WORLD_EXIST_LOADED(MVCorei18n.CREATEWORLD_WORLDEXISTLOADED), + + /** + * Bukkit API failed to create the world. + */ + BUKKIT_CREATION_FAILED(MVCorei18n.CREATEWORLD_BUKKITCREATIONFAILED); + + private final MessageKeyProvider message; + + CreateFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/DeleteFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/DeleteFailureReason.java new file mode 100644 index 000000000..885421f83 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/DeleteFailureReason.java @@ -0,0 +1,57 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.event.MVWorldDeleteEvent; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world deletion operation. + */ +public enum DeleteFailureReason implements FailureReason { + /** + * The world does not exist. + */ + WORLD_NON_EXISTENT(MVCorei18n.DELETEWORLD_WORLDNONEXISTENT), + + /** + * The world could not be loaded. + */ + LOAD_FAILED(MVCorei18n.DELETEWORLD_LOADFAILED), + + /** + * The world could not be unloaded. + */ + WORLD_FOLDER_NOT_FOUND(MVCorei18n.DELETEWORLD_WORLDFOLDERNOTFOUND), + + /** + * The world could not be removed. + */ + REMOVE_FAILED(MVCorei18n.GENERIC_FAILURE), + + /** + * The world folder could not be deleted. + */ + FAILED_TO_DELETE_FOLDER(MVCorei18n.DELETEWORLD_FAILEDTODELETEFOLDER), + + /** + * The {@link MVWorldDeleteEvent} was cancelled. + */ + EVENT_CANCELLED(MVCorei18n.GENERIC_FAILURE); // todo: messaging + + private final MessageKeyProvider message; + + DeleteFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/ImportFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/ImportFailureReason.java new file mode 100644 index 000000000..21b19d152 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/ImportFailureReason.java @@ -0,0 +1,51 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world import operation. + */ +public enum ImportFailureReason implements FailureReason { + /** + * The world name is invalid. + */ + INVALID_WORLDNAME(MVCorei18n.IMPORTWORLD_INVALIDWORLDNAME), + + /** + * The world folder is invalid. + */ + WORLD_FOLDER_INVALID(MVCorei18n.IMPORTWORLD_WORLDFOLDERINVALID), + + /** + * The target world folder already exists. You should load it instead. + */ + WORLD_EXIST_UNLOADED(MVCorei18n.IMPORTWORLD_WORLDEXISTUNLOADED), + + /** + * The target world is already exist and loaded. + */ + WORLD_EXIST_LOADED(MVCorei18n.IMPORTWORLD_WORLDEXISTLOADED), + + /** + * Bukkit API failed to create the world. + */ + BUKKIT_CREATION_FAILED(MVCorei18n.IMPORTWORLD_BUKKITCREATIONFAILED); + + private final MessageKeyProvider message; + + ImportFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/LoadFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/LoadFailureReason.java new file mode 100644 index 000000000..e081c3426 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/LoadFailureReason.java @@ -0,0 +1,51 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world loading operation. + */ +public enum LoadFailureReason implements FailureReason { + /** + * Loading operation is underway. + */ + WORLD_ALREADY_LOADING(MVCorei18n.LOADWORLD_WORLDALREADYLOADING), + + /** + * The world does not exist. + */ + WORLD_NON_EXISTENT(MVCorei18n.LOADWORLD_WORLDNONEXISTENT), + + /** + * The world folder exists but is not known to Multiverse. + */ + WORLD_EXIST_FOLDER(MVCorei18n.LOADWORLD_WORLDEXISTFOLDER), + + /** + * The world is already loaded. + */ + WORLD_EXIST_LOADED(MVCorei18n.LOADWORLD_WORLDEXISTLOADED), + + /** + * Bukkit API failed to create the world. + */ + BUKKIT_CREATION_FAILED(MVCorei18n.LOADWORLD_BUKKITCREATIONFAILED); + + private final MessageKeyProvider message; + + LoadFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/RegenFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/RegenFailureReason.java new file mode 100644 index 000000000..3f9207e4f --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/RegenFailureReason.java @@ -0,0 +1,36 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world regeneration operation. + */ +public enum RegenFailureReason implements FailureReason { + /** + * The world does not exist. + */ + DELETE_FAILED(MVCorei18n.GENERIC_FAILURE), + + /** + * The new world could not be created. + */ + CREATE_FAILED(MVCorei18n.GENERIC_FAILURE); + + private final MessageKeyProvider message; + + RegenFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/RemoveFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/RemoveFailureReason.java new file mode 100644 index 000000000..dedeec798 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/RemoveFailureReason.java @@ -0,0 +1,36 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world removal operation. + */ +public enum RemoveFailureReason implements FailureReason { + /** + * The world does not exist. + */ + WORLD_NON_EXISTENT(MVCorei18n.REMOVEWORLD_WORLDNONEXISTENT), + + /** + * The world could not be unloaded. + */ + UNLOAD_FAILED(MVCorei18n.GENERIC_FAILURE); + + private final MessageKeyProvider message; + + RemoveFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/UnloadFailureReason.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/UnloadFailureReason.java new file mode 100644 index 000000000..86d4d01d6 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/UnloadFailureReason.java @@ -0,0 +1,46 @@ +package org.mvplugins.multiverse.core.world.reasons; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; + +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.utils.result.FailureReason; + +/** + * Result of a world unloading operation. + */ +public enum UnloadFailureReason implements FailureReason { + /** + * Unloading operation is underway. + */ + WORLD_ALREADY_UNLOADING(MVCorei18n.UNLOADWORLD_WORLDALREADYUNLOADING), + + /** + * The world does not exist. + */ + WORLD_NON_EXISTENT(MVCorei18n.UNLOADWORLD_WORLDNONEXISTENT), + + /** + * The world is already unloaded. + */ + WORLD_UNLOADED(MVCorei18n.UNLOADWORLD_WORLDUNLOADED), + + /** + * Bukkit API failed to unload the world. + */ + BUKKIT_UNLOAD_FAILED(MVCorei18n.UNLOADWORLD_BUKKITUNLOADFAILED); + + private final MessageKeyProvider message; + + UnloadFailureReason(MessageKeyProvider message) { + this.message = message; + } + + /** + * {@inheritDoc} + */ + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/world/reasons/package-info.java b/src/main/java/org/mvplugins/multiverse/core/world/reasons/package-info.java new file mode 100644 index 000000000..16452d663 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/world/reasons/package-info.java @@ -0,0 +1 @@ +package org.mvplugins.multiverse.core.world.reasons; diff --git a/src/main/resources/META-INF/services/org.mvplugins.multiverse.external.glassfish.hk2.extension.ServiceLocatorGenerator b/src/main/resources/META-INF/services/org.mvplugins.multiverse.external.glassfish.hk2.extension.ServiceLocatorGenerator new file mode 100644 index 000000000..4b2da3330 --- /dev/null +++ b/src/main/resources/META-INF/services/org.mvplugins.multiverse.external.glassfish.hk2.extension.ServiceLocatorGenerator @@ -0,0 +1 @@ +org.mvplugins.multiverse.external.jvnet.hk2.external.generator.ServiceLocatorGeneratorImpl diff --git a/src/main/resources/multiverse-core_en.properties b/src/main/resources/multiverse-core_en.properties new file mode 100644 index 000000000..9297e2df1 --- /dev/null +++ b/src/main/resources/multiverse-core_en.properties @@ -0,0 +1,233 @@ +# configuration +mv-core.config.save.failed=Unable to save Multiverse-Core config.yml. Your changes will be temporary! +mv-core.config.node.notfound=Node not found in config: {node} + +# /mv check +mv-core.check.description=Checks if a player can teleport themselves to a destination. +mv-core.check.player.description=Player to check destination on. +mv-core.check.destination.description=A destination location, e.g. a world name. +mv-core.check.haspermission=&a{player} can teleport to {destination} +mv-core.check.nopermission=&c{player} cannot teleport to {destination} +mv-core.check.location=The destination's location is: &f{location} + +# /mv clone +mv-core.clone.description=Clones a world. +mv-core.clone.world.description=The target world to clone. +mv-core.clone.newWorld.description=The new cloned world name. +mv-core.clone.cloning=Cloning world '{world}' to '{newworld}'... +mv-core.clone.success=&aWorld cloned to '{world}'! + +# /mv confirm +mv-core.confirm.description=Confirms dangerous commands before executing them. + +# /mv coordinates +mv-core.coordinates.description=Simply sends your coordinates +mv-core.coordinates.info.title=&b--- Location Information --- +mv-core.coordinates.info.world=&bWorld: &f{world} +mv-core.coordinates.info.alias=&bAlias: &f{alias} +mv-core.coordinates.info.worldScale=&bWorld Scale: &f{scale} +mv-core.coordinates.info.coordinates=&bCoordinates: &f{coordinates} +mv-core.coordinates.info.direction=&bDirection: &f{direction} + +# /mv create +mv-core.create.description=Creates a new world and loads it. +mv-core.create.name.description=New world name. +mv-core.create.environment.description=The world's environment. See: /mv environments +mv-core.create.flags.description=Additional world settings. See https://gg.gg/nn8bl for all possible flags. +mv-core.create.properties=Creating world {worldName} with the following properties: +mv-core.create.properties.environment=- Environment: &f{environment} +mv-core.create.properties.seed=- Seed: &f{seed} +mv-core.create.properties.worldtype=- World Type: &f{worldType} +mv-core.create.properties.adjustspawn=- Adjust Spawn: &f{adjustSpawn} +mv-core.create.properties.biome=- Biome: &f{biome} +mv-core.create.properties.generator=- Generator: &f{generator} +mv-core.create.properties.structures=- Structures: &f{structures} +mv-core.create.loading=Creating world... +mv-core.create.success=&aWorld '{world}' created! + +# /mv debug +mv-core.debug.info.description=Show the current debug level. +mv-core.debug.info.off=&fMultiverse Debug mode is &cOFF&f. +mv-core.debug.info.on=&fMultiverse Debug mode is at &alevel {level}&f. +mv-core.debug.change.description=Change debug level. +mv-core.debug.change.syntax=level +mv-core.debug.change.level.description=Debug level to set to. + +# /mv delete +mv-core.delete.description=Deletes a world on your server PERMANENTLY. +mv-core.delete.deleting=Deleting world '{world}'... +mv-core.delete.prompt=Are you sure you want to delete world '{world}'? +mv-core.delete.success=&aWorld '{world}' deleted! + +# /mv dumps +mv-core.dumps.description=Dumps version info to the console or paste services +mv-core.dumps.url.list={service} : {link} + +# /mv gamerule set +mv-core.gamerule.set.description=Changes a gamerule in one or more worlds. +mv-core.gamerule.set.gamerule.description=Gamerule to set. +mv-core.gamerule.set.value.description=Value of gamerule. +mv-core.gamerule.set.world.description=World to apply gamerule to, current player's world by default. +mv-core.gamerule.set.failed=Failed to set gamerule {gamerule} to {value} in {world}. &fIt should be a {type}. +mv-core.gamerule.set.success.single=&aSuccessfully set {gamerule} to {value} in {world}. +mv-core.gamerule.set.success.multiple=&aSuccessfully set {gamerule} to {value} in {count} worlds. + +# /mv gamerule reset +mv-core.gamerule.reset.description=Resets a gamerule in one or more worlds to it's default value. +mv-core.gamerule.reset.gamerule.description=Gamerule to reset. +mv-core.gamerule.reset.world.description=World to reset gamerule of, current player's world by default. +mv-core.gamerule.reset.failed=Failed to reset gamerule {gamerule} in {world}. +mv-core.gamerule.reset.success.single=&aSuccessfully reset {gamerule} in {world}. +mv-core.gamerule.reset.success.multiple=&aSuccessfully reset {gamerule} in {count} worlds. + +# /mv gamerule list +mv-core.gamerule.list.description=Lists gamerules for the specified world. +mv-core.gamerule.list.description.page=The page to view. +mv-core.gamerule.list.description.world=The world to list gamerules in. +mv-core.gamerule.list.title= --- Gamerules for {world} --- + +# /mv generators +mv-core.generators.description=Lists generators known to Multiverse +mv-core.generators.description.flags=Filter - only shows entries matching this. Page - the page to show +mv-core.generators.empty=&cNo Generator Plugins found. + +# /mv import +mv-core.import.description=Imports an existing world folder. +mv-core.import.name.description=Name of the world folder. +mv-core.import.env.description=The world's environment. See: /mv env +mv-core.import.other.description=Other world settings. See: https://gg.gg/nn8c2 +mv-core.import.importing=Starting import of world '{world}'... +mv-core.import.success=&aWorld '{world}' imported! + +# /mv load +mv-core.load.description=Loads a world. World must be already in worlds.yml, else please use /mv import. +mv-core.load.world.description=Name of world you want to load. +mv-core.load.loading=Loading world '{world}'... +mv-core.load.success=&aWorld '{world}' loaded! + +# /mv regen +mv-core.regen.description=Regenerates a world on your server. The previous state will be lost PERMANENTLY. +mv-core.regen.world.description=World that you want to regen. +mv-core.regen.other.description=Other world settings. See: http://gg.gg/nn8lk +mv-core.regen.regenerating=Regenerating world '{world}'... +mv-core.regen.prompt=Are you sure you want to regenerate world '{world}'? +mv-core.regen.success=&aWorld '{world}' regenerated! + +# /mv reload +mv-core.reload.description=Reloads config files for all Multiverse modules. +mv-core.reload.reloading=&6Reloading all Multiverse Plugin configs... +mv-core.reload.success=&aReload complete! + +# /mv remove +mv-core.remove.description=Unloads a world from Multiverse and removes it from worlds.yml. This does NOT delete the world folder. +mv-core.remove.world.description=World you want to remove from MV's knowledge. +mv-core.remove.success=&aWorld '{world}' removed! + +# /mv +mv-core.root.title=&a{name} version {version} +mv-core.root.help=&aSee &f/mv help&a for commands available. + +# /mv setspawn +mv-core.setspawn.description=Sets the spawn location of the specified world +mv-core.setspawn.location.description=Location of the new spawn +mv-core.setspawn.world.description=Target world to set spawn of (defaults to player's current world) + +# /mv spawn +mv-core.spawn.description=Teleports the specified player to the spawn of the world they are in +mv-core.spawn.player.description=The player +mv-core.spawn.success=Teleported {player} to '{world}' spawn! +mv-core.spawn.failed=Failed to teleport {player} to '{world}' spawn. {reason} + +# /mv tp +mv-core.teleport.description=Allows you to teleport to a location on your server! +mv-core.teleport.player.description=Target player to teleport. +mv-core.teleport.destination.description=Location, can be a world name. +mv-core.teleport.toomanyplayers=&cYou cannot teleport more than {count} players at once. +mv-core.teleport.success=Teleported {player} to {destination}. +mv-core.teleport.failed=Failed to teleport {player} to {destination}. {reason} + +# /mv unload +mv-core.unload.description=Unloads a world from Multiverse. This does NOT remove the world folder. This does NOT remove it from the config file. +mv-core.unload.world.description=Name of the world you want to unload. +mv-core.unload.unloading=Unloading world '{world}'... +mv-core.unload.success=&aWorld '{world}' unloaded! + +# /mv usage +mv-core.usage.description=Show Multiverse-Core command usage. + +# /mv who +# /mv whoall +mv-core.who.description=Lists the players in the world specified +mv-core.who.all.description=Lists the players in all worlds +mv-core.who.world.description=Name of the world you want to list players in +mv-core.who.flags.description=Filter - only shows entries matching this. Page - the page to show +mv-core.who.empty=&rEmpty + +# /mv version +mv-core.version.description=Displays version and authors +mv-core.version.mv=Multiverse Core Version &fv{version} +mv-core.version.authors=Multiverse Core Authors &f{authors} +mv-core.version.secretcode=Special Code: &fFRN002 + +# commands error +mv-core.commands.error.playersonly=&cThis command can only be used by players +mv-core.commands.error.multiverseworldonly=&cThis can only be used in multiverse worlds + +# entry check +mv-core.entrycheck.blacklisted='{world}' is blacklisted. +mv-core.entrycheck.notenoughmoney=you do not have enough money to pay entry fee. You are required to pay {amount}. +mv-core.entrycheck.cannotpayentryfee=you do not have the ability to pay entry fee. +mv-core.entrycheck.exceedplayerlimit=the world has reached its player limit. +mv-core.entrycheck.noworldaccess=you do not have permissions to access the world. + +# teleport failure reason +mv-core.teleportfailurereason.null.destination=Destination is invalid! +mv-core.teleportfailurereason.null.location=Location is invalid! +mv-core.teleportfailurereason.unsafe.location=Location is unsafe! Use `--unsafe` to override. +mv-core.teleportfailurereason.teleport.failed=Something rejected the teleport. +mv-core.teleportfailurereason.teleport.failed.exception=An error occurred during the teleport. See console for more details. +mv-core.teleportfailurereason.event.cancelled=The teleport was cancelled by another plugin. + +# world manager result +mv-core.cloneworld.invalidworldname=World '{world}' contains invalid characters! +mv-core.cloneworld.worldexistfolder=World '{world}' exists in server folders! You need to delete it first before cloning. +mv-core.cloneworld.worldexistunloaded=World '{world}' already exists and it's unloaded! You need to delete it first before cloning. +mv-core.cloneworld.worldexistloaded=World '{world}' already exists! You need to delete it first before cloning. +mv-core.cloneworld.copyfailed=Failed to copy world to '{world}': {error}\n&fSee console for more details. + +mv-core.createworld.invalidworldname=World '{world}' contains invalid characters! +mv-core.createworld.worldexistfolder=World '{world}' already exists in server folders!&f Type '&a/mv import {world} &f' if you wish to import it. +mv-core.createworld.worldexistunloaded=World '{world}' already exists, but it's not loaded!&f Type '&a/mv load {world}&f' if you wish to load it. +mv-core.createworld.worldexistloaded=World '{world}' already exists! +mv-core.createworld.bukkitcreationfailed=Bukkit failed to create world '{world}': {error}\n&fSee console for more details. + +mv-core.deleteworld.worldnonexistent=World '{world}' not found! +mv-core.deleteworld.loadfailed=Unable to load world '{world}', does the world folder exist?&f You can run '&a/mv remove {world}&f' to remove it from Multiverse, or attempt to load again with '&a/mv load {world}&f'. +mv-core.deleteworld.worldfoldernotfound=World '{world}' folder not found! +mv-core.deleteworld.failedtodeletefolder=Failed to delete world folder '{world}': {error}\n&fSee console for more details. + +mv-core.importworld.invalidworldname=World '{world}' contains invalid characters! +mv-core.importworld.worldexistunloaded=World '{world}' already exists, but it's not loaded!&f Type '&a/mv load {world}&f' if you wish to load it. +mv-core.importworld.worldexistloaded=World '{world}' already exists! +mv-core.importworld.worldfolderinvalid=World '{world}' folder contents does not seem to be a valid world! +mv-core.importworld.bukkitcreationfailed=Bukkit failed to import world '{world}': {error}\n&fSee console for more details. + +mv-core.loadworld.worldalreadyloading=World '{world}' is already loading! Please wait... +mv-core.loadworld.worldnonexistent=World '{world}' not found! Use '&a/mv create {world} &f' to create it. +mv-core.loadworld.worldexistfolder=World '{world}' exists in server folders, but it's not known to Multiverse!&f Type '&a/mv import {world} &f' if you wish to import it. +mv-core.loadworld.worldexistloaded=World '{world}' is already loaded! +mv-core.loadworld.bukkitcreationfailed=Bukkit failed to load world '{world}': {error}\n&fSee console for more details. + +mv-core.removeworld.worldnonexistent=World '{world}' not found! + +mv-core.unloadworld.worldalreadyunloading=World '{world}' is already unloading! Please wait... +mv-core.unloadworld.worldnonexistent=World '{world}' does not exist! +mv-core.unloadworld.worldunloaded=World '{world}' is already unloaded! +mv-core.unloadworld.bukkitunloadfailed=Bukkit failed to unload world '{world}': {error}\n&fSee console for more details. + +# generic +mv-core.generic.success=Success! +mv-core.generic.failure=Failed! +mv-core.generic.error=Error! +mv-core.generic.null=Null! +mv-core.generic.you=you diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ed7346e85..f4bc2d692 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,187 +1,7 @@ name: Multiverse-Core -main: com.onarandombox.MultiverseCore.MultiverseCore -authors: ['dumptruckman', 'Rigby', 'fernferret', 'lithium3141', 'main--'] +main: org.mvplugins.multiverse.core.MultiverseCore +authors: ['dumptruckman', 'Rigby', 'fernferret', 'lithium3141', 'main--', 'benwoo1110', 'Zax71'] website: 'https://dev.bukkit.org/projects/multiverse-core' -softdepend: ['Vault'] +softdepend: ['Vault', 'PlaceholderAPI'] api-version: 1.13 version: ${version} -commands: - mv: - description: Generic Multiverse Command - usage: / - mvcreate: - description: World create command - aliases: [mvc] - usage: | - / - / creative normal -- Creates a world called 'creative' with a NORMAL environment. - / hellworld nether -- Creates a world called 'hellworld' with a NETHER environment. - mvimport: - description: World import command - aliases: [mvim] - usage: | - / [-g generator[:id]] [-n] - / creative normal -- Imports an existing world called 'creative' with a NORMAL environment. - / hellworld nether -- Imports an existing world called 'hellworld' with a NETHER environment. - mvremove: - description: Remove world from multiverse command - usage: | - / - mvdelete: - description: Delete world from server folders command - usage: | - / - mvunload: - description: World unload command - usage: | - / - mvmodify: - description: Modify the settings of an existing world - aliases: [mvm] - usage: | - / [world] - / set pvp false creative -- Turns off PVP in the 'creative' world. - mvmset: - description: Modify the settings of an existing world - usage: | - / [world] - / pvp false creative -- Turns off PVP in the 'creative' world. - mvmadd: - description: Modify the settings of an existing world - usage: | - / [world] - / sheep animals world_nether -- Add sheep to animal list for 'world_nether' world. - mvmremove: - description: Modify the settings of an existing world - aliases: [mvmdelete] - usage: | - / [world] - / sheep animals world_nether -- remove sheep from animal list for 'world_nether' world. - mvmclear: - description: Modify the settings of an existing world - usage: | - / [world] - / animals world_nether -- Empties animal list for 'world_nether' world. - mvtp: - description: Command to teleport between Worlds - usage: | - / [player] - Example: / creative - Teleports you to the relevant location in the 'creative' world. - mvlist: - description: Print list of loaded Worlds - aliases: [mvl] - usage: | - / [page] - Example: / NETHER - Shows all NETHER Worlds. - Example: / NORMAL - Shows all NORMAL Worlds. - mvsetspawn: - description: Set the spawn area for a particular world - aliases: [mvsets, mvss] - usage: / -- Sets the spawn area of the current world to your location. - mvspawn: - description: Teleport to the spawn area - aliases: [mvs] - usage: / [player] -- Teleports you to the spawn area of your current world. - mvcoord: - description: Display World, Coordinates, Direction & Compression for a world. - aliases: [mvco] - usage: | - / [world] - / -- Shows the relevant coordinates in your current world. - / creative -- Shows the relevant coordinates if you were in the 'creative' world. - mvwho: - description: Display online users per world. - aliases: [mvw] - usage: | - / [world|--all] - / -- Shows who is online in each world. - / creative -- Shows who is online in the 'creative' world. - mvreload: - description: Reload Configuration files. - aliases: [mvr] - usage: / - mvpurge: - description: Purge the targetted world of creatures. - usage: | - / [WORLD|all] [all|animals|monsters|MOBNAME] - / -- Purges the players world of all creatures. - / creative all -- Purges the creative world of all Creatures. - / creative creeper -- Purges the creative world of all CREEPERS. - mvconfirm: - description: Confirms sensitive decisions like deleting a world. - usage: | - / - mvinfo: - description: Gets world info. - aliases: [mvi] - usage: | - / [world] [page] - mvenv: - description: Tells the user all possible environment types. - usage: | - / - mvversion: - description: Prints out version info. - aliases: [mvv, mvver] - usage: | - / [-b|-h|-p] [--include-plugin-list] - mvhelp: - description: Displays the Multiverse Help. - aliases: [mvsearch, mvh] - usage: | - / [filter] [page #] - mvdebug: - description: Turns on debugging. - usage: | - / [1|2|3|off|silent] - mvgenerators: - description: Displays all found world generators. - aliases: [mvgens] - usage: | - / - mvload: - description: Loads a world into Multiverse. - usage: | - / - mvregen: - description: Regenerates a world Multiverse already knows about. - usage: | - / [-s seed] - mvscript: - description: Runs a script from the Multiverse scripts directory. - usage: | - /