commit 5501b75d952b73aac89274310ca067e32e7f2a1f Author: bizink Date: Thu Jun 12 00:11:46 2025 +1000 Portal module diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5f737e --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..14252fa --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,50 @@ +plugins { + kotlin("jvm") version "2.2.0-Beta2" + id("com.gradleup.shadow") version "8.3.0" + id("xyz.jpenilla.run-paper") version "2.3.1" +} + +group = "com.pobnellion" +version = "2.0-SNAPSHOT" + +repositories { + mavenCentral() + maven("https://repo.papermc.io/repository/maven-public/") { + name = "papermc-repo" + } + maven("https://oss.sonatype.org/content/groups/public/") { + name = "sonatype" + } +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:1.21.5-R0.1-SNAPSHOT") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +tasks { + runServer { + // Configure the Minecraft version for our task. + // This is the only required configuration besides applying the plugin. + // Your plugin's jar (or shadowJar if present) will be used automatically. + minecraftVersion("1.21") + } +} + +val targetJavaVersion = 21 +kotlin { + jvmToolchain(targetJavaVersion) +} + +tasks.build { + dependsOn("shadowJar") +} + +tasks.processResources { + val props = mapOf("version" to version) + inputs.properties(props) + filteringCharset = "UTF-8" + filesMatching("plugin.yml") { + expand(props) + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e69de29 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0d8ab51 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..b357a34 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "pobutils" diff --git a/src/main/kotlin/com/pobnellion/pobutils/Pobutils.kt b/src/main/kotlin/com/pobnellion/pobutils/Pobutils.kt new file mode 100644 index 0000000..46da7d9 --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/Pobutils.kt @@ -0,0 +1,77 @@ +package com.pobnellion.pobutils + +import com.pobnellion.pobutils.modules.CmdModule +import com.pobnellion.pobutils.modules.ModuleBase +import com.pobnellion.pobutils.modules.portals.Portals +import org.bukkit.plugin.java.JavaPlugin + + +class Pobutils : JavaPlugin() { + companion object { + val availableModules = mutableMapOf() + val enabledModules : MutableMap = mutableMapOf() + fun getDisabledModuleNames() = availableModules.keys.filter { name -> !enabledModules.containsKey(name) } + fun isEnabled(moduleName: String) : Boolean = enabledModules.containsKey(moduleName) + } + + override fun onEnable() { + loadDefaultConfig() + + CmdModule.register(this) + + registerModule(Portals(this)) + + logger.info("Registered ${availableModules.size} modules: [${availableModules.keys.joinToString()}]") + + // Enable modules + for ((name, module) in availableModules) { + if (config.getBoolean("modules.${name}")) { + module.onEnable() + enabledModules[name] = module + } + } + + logger.info("Loaded ${enabledModules.size} modules: [${enabledModules.keys.joinToString()}]") + } + + override fun onDisable() { + // Plugin shutdown logic + } + + private fun registerModule(module: ModuleBase) { + availableModules[module.name] = module + module.register() + } + + private fun loadDefaultConfig() { + saveResource("config.yml", false) + + config.addDefault("modules.noJoinMessage", false) + config.addDefault("modules.sit", false) + config.addDefault("modules.spawn", false) + config.addDefault("modules.portals", false) + config.addDefault("modules.warp", false) + config.addDefault("modules.hub", false) + config.addDefault("modules.disableTNT", false) + config.addDefault("modules.tabList", false) + config.addDefault("modules.formatChat", false) + config.addDefault("modules.disableTrample", false) + config.addDefault("modules.snowballDamage", false) + config.addDefault("data.spawn.location", "") + config.addDefault("data.spawn.spawnOnJoin", false) + config.addDefault("data.spawn.spawnOnDeath", false) + + // config.addDefault("data.spawn.spawnOnFirstJoin", false); + config.addDefault("data.portals", "") + config.addDefault("data.warps", "") + config.addDefault("data.formatChat.serverAlias", "?") + config.addDefault("data.formatChat.messageFormat", " : ") + config.addDefault("data.formatChat.formatMessageText", true) + + config.addDefault("settings.snowballDamage.damageExceptions", ArrayList()) + config.addDefault("settings.snowballDamage.snowmenDontHitEachother", true) + + config.options().copyDefaults(true) + saveConfig() + } +} diff --git a/src/main/kotlin/com/pobnellion/pobutils/modules/CmdModule.kt b/src/main/kotlin/com/pobnellion/pobutils/modules/CmdModule.kt new file mode 100644 index 0000000..e120163 --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/modules/CmdModule.kt @@ -0,0 +1,96 @@ +package com.pobnellion.pobutils.modules + +import com.mojang.brigadier.Command +import com.mojang.brigadier.arguments.StringArgumentType +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.pobnellion.pobutils.Pobutils +import io.papermc.paper.command.brigadier.CommandSourceStack +import io.papermc.paper.command.brigadier.Commands +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.TextComponent +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.plugin.java.JavaPlugin + +@Suppress("UnstableApiUsage") +object CmdModule { + fun register(plugin: JavaPlugin) { + val command = Commands.literal("module") + .requires { source -> source.sender.hasPermission("pobutils.admin") } + .then(handleAction("enable", plugin)) + .then(handleAction("disable", plugin)) + .then(handleAction("reload", plugin)) + .build() + + plugin.lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS) {commands -> + commands.registrar().register(command) + } + } + + private fun handleAction(action: String, plugin: JavaPlugin) : LiteralArgumentBuilder { + return Commands.literal(action) + .then(Commands.argument("module", StringArgumentType.word()) + .suggests { ctx, builder -> + when (action) { + "enable" -> Pobutils.getDisabledModuleNames().forEach { module -> builder.suggest(module) } + "disable" -> Pobutils.enabledModules.keys.forEach { module -> builder.suggest(module) } + "reload" -> Pobutils.enabledModules.keys.forEach { module -> builder.suggest(module) } + } + + return@suggests builder.buildFuture() + } + .executes { ctx -> + val moduleName = StringArgumentType.getString(ctx, "module") + + val module = Pobutils.availableModules[moduleName] + + if (module == null) { + ctx.source.sender.sendMessage(Component.text("No module named '$moduleName'", NamedTextColor.RED)) + return@executes Command.SINGLE_SUCCESS + } + + val message : TextComponent + when (action) { + "enable" -> { + if (Pobutils.isEnabled(moduleName)) { + message = Component.text("Module '$moduleName' is already enabled", NamedTextColor.RED) + } + else { + Pobutils.enabledModules[moduleName] = module + module.onEnable() + plugin.config.set("modules.$moduleName", true) + plugin.saveConfig() + message = Component.text("Module '$moduleName' enabled", NamedTextColor.YELLOW) + } + } + "disable" -> { + if (!Pobutils.isEnabled(moduleName)) { + message = Component.text("Module '$moduleName' is already disabled", NamedTextColor.RED) + } + else { + Pobutils.enabledModules.remove(moduleName) + module.onDisable() + plugin.config.set("modules.$moduleName", false) + plugin.saveConfig() + message = Component.text("Module '$moduleName' disabled", NamedTextColor.YELLOW) + } + } + "reload" -> { + if (!Pobutils.isEnabled(moduleName)) { + message = Component.text("Module '$moduleName' is not currently enabled", NamedTextColor.RED) + } + else { + module.reload() + message = Component.text("Module '$moduleName' reloaded", NamedTextColor.YELLOW) + } + } + else -> message = Component.text("Unknown argument '$action'", NamedTextColor.RED) + } + + plugin.server.onlinePlayers.forEach { player -> player.updateCommands() } + ctx.source.sender.sendMessage(message) + + return@executes Command.SINGLE_SUCCESS + }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/pobutils/modules/ModuleBase.kt b/src/main/kotlin/com/pobnellion/pobutils/modules/ModuleBase.kt new file mode 100644 index 0000000..15e8d0c --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/modules/ModuleBase.kt @@ -0,0 +1,11 @@ +package com.pobnellion.pobutils.modules + +import org.bukkit.plugin.java.JavaPlugin + +abstract class ModuleBase(val plugin: JavaPlugin) { + abstract val name : String + abstract fun register() + abstract fun reload() + abstract fun onDisable() + abstract fun onEnable() +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/pobutils/modules/portals/CmdPortal.kt b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/CmdPortal.kt new file mode 100644 index 0000000..e46edae --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/CmdPortal.kt @@ -0,0 +1,115 @@ +package com.pobnellion.pobutils.modules.portals + +import com.mojang.brigadier.Command +import com.mojang.brigadier.arguments.StringArgumentType +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.pobnellion.pobutils.Pobutils +import io.papermc.paper.command.brigadier.CommandSourceStack +import io.papermc.paper.command.brigadier.Commands +import io.papermc.paper.command.brigadier.argument.ArgumentTypes +import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import org.bukkit.entity.Player +import org.bukkit.plugin.java.JavaPlugin + +@Suppress("UnstableApiUsage") +object CmdPortal { + val serverList: MutableList = mutableListOf() + + fun register(plugin: JavaPlugin, portals: Portals) { + val command = Commands.literal("portal") + .requires { source -> + Pobutils.isEnabled("portals") && + source.sender.hasPermission("pobutils.admin") && + source.sender is Player + } + .then(addOrUpdatePortal("add", portals)) + .then(addOrUpdatePortal("update", portals)) + .then(removePortal(portals)) + .then(listPortals(portals)) + .build() + + plugin.lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS) { commands -> + commands.registrar().register(command) + } + } + + private fun addOrUpdatePortal(action: String, portals: Portals) : LiteralArgumentBuilder { + var portalSubCommand = Commands.argument("name", StringArgumentType.word()) + + if (action == "update") + portalSubCommand = portalSubCommand.suggests { ctx, builder -> + portals.portals.forEach { portal -> builder.suggest(portal.name) } + return@suggests builder.buildFuture() + } + + portalSubCommand.then(Commands.argument("location1", ArgumentTypes.blockPosition()) + .then(Commands.argument("location2", ArgumentTypes.blockPosition()) + .then(Commands.argument("destinationServer", StringArgumentType.word()) + .suggests { ctx, builder -> + serverList.forEach { serverName -> builder.suggest(serverName) } + return@suggests builder.buildFuture() + } + .executes { ctx -> + val name = StringArgumentType.getString(ctx, "name") + val destServer = StringArgumentType.getString(ctx, "destinationServer") + val location1 = ctx.getArgument("location1", BlockPositionResolver::class.java).resolve(ctx.source) + val location2 = ctx.getArgument("location2", BlockPositionResolver::class.java).resolve(ctx.source) + + if (!serverList.contains(destServer)) { + ctx.source.sender.sendMessage(Component.text("No server named $destServer", NamedTextColor.RED)) + return@executes Command.SINGLE_SUCCESS + } + + when (action) { + "add" if portals.exists(name) -> ctx.source.sender.sendMessage(Component.text("A portal called $name already exists!", NamedTextColor.RED)) + "update" if !portals.exists(name) -> ctx.source.sender.sendMessage(Component.text("No portal named $name!", NamedTextColor.RED)) + else -> { + portals.addOrUpdate(name, location1, location2, destServer) + val actionVerb = if (action == "add") "Added" else "Updated" + ctx.source.sender.sendMessage(Component.text("$actionVerb portal '$name'", NamedTextColor.YELLOW)) + } + } + + return@executes Command.SINGLE_SUCCESS + }))) + + return Commands + .literal(action) + .then(portalSubCommand) + } + + private fun removePortal(portals: Portals) : LiteralArgumentBuilder { + return Commands.literal("remove") + .then(Commands.argument("portal", StringArgumentType.word()) + .suggests { ctx, builder -> + portals.portals.forEach { portal -> builder.suggest(portal.name) } + return@suggests builder.buildFuture() + } + .executes { ctx -> + val name = StringArgumentType.getString(ctx, "portal") + + if (!portals.exists(name)) + ctx.source.sender.sendMessage(Component.text("No portal named $name!", NamedTextColor.RED)) + else + portals.remove(name) + + return@executes Command.SINGLE_SUCCESS + }) + } + + private fun listPortals(portals: Portals) : LiteralArgumentBuilder { + return Commands.literal("list") + .executes { ctx -> + ctx.source.sender.sendMessage(Component.text("There are ${portals.portals.count()} portals:", NamedTextColor.YELLOW)) + + portals.portals.forEach { portal -> + ctx.source.sender.sendMessage(Component.text(portal.toString(), NamedTextColor.YELLOW)) + } + + return@executes Command.SINGLE_SUCCESS + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portal.kt b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portal.kt new file mode 100644 index 0000000..f0f1406 --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portal.kt @@ -0,0 +1,58 @@ +package com.pobnellion.pobutils.modules.portals + +import org.bukkit.Location +import kotlin.math.max +import kotlin.math.min + +class Portal { + + val name: String + val destServer: String + private val x1: Int + private val y1: Int + private val z1: Int + private val x2: Int + private val y2: Int + private val z2: Int + + constructor(name: String, destServer: String, x1: Int, y1: Int, z1: Int, x2: Int, y2: Int, z2: Int) { + this.name = name + this.destServer = destServer + this.x1 = min(x1, x2) + this.y1 = min(y1, y2) + this.z1 = min(z1, z2) + this.x2 = max(x1, x2) + this.y2 = max(y1, y2) + this.z2 = max(z1, z2) + } + + companion object { + fun deserialize(name: String, serializedData: String) : Portal { + val data = serializedData.split(" ") + + return Portal( + name, + data[6], + data[0].toInt(), + data[1].toInt(), + data[2].toInt(), + data[3].toInt(), + data[4].toInt(), + data[5].toInt()) + } + } + + fun serialize(): String { + return "$x1 $y1 $z1 $x2 $y2 $z2 $destServer" + } + + fun contains(location: Location) : Boolean { + return location.blockX >= x1 && location.blockX <= x2 && + location.blockY >= y1 && location.blockY <= y2 && + location.blockZ >= z1 && location.blockZ <= z2 + } + + override fun toString() : String { + return "[$name] ($x1 $y1 $z1) ($x2 $y2 $z2) dest: $destServer" + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portals.kt b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portals.kt new file mode 100644 index 0000000..e6fdac4 --- /dev/null +++ b/src/main/kotlin/com/pobnellion/pobutils/modules/portals/Portals.kt @@ -0,0 +1,164 @@ +package com.pobnellion.pobutils.modules.portals + +import com.google.common.io.ByteStreams +import com.pobnellion.pobutils.Pobutils +import com.pobnellion.pobutils.modules.ModuleBase +import io.papermc.paper.math.BlockPosition +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.NamespacedKey +import org.bukkit.attribute.Attribute +import org.bukkit.attribute.AttributeModifier +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerMoveEvent +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.plugin.java.JavaPlugin +import org.bukkit.plugin.messaging.PluginMessageListener + + +@Suppress("UnstableApiUsage") +class Portals(plugin: JavaPlugin) : ModuleBase(plugin), Listener, PluginMessageListener { + val portals: MutableList = mutableListOf() + val portalCooldowns: MutableMap = mutableMapOf() + override val name: String = "portals" + + override fun register() { + Bukkit.getPluginManager().registerEvents(this, plugin) + plugin.server.pluginManager.registerEvents(this, plugin) + + CmdPortal.register(plugin, this) + } + + override fun reload() { + onDisable() + onEnable() + } + + override fun onEnable() { + loadConfig() + + plugin.server.messenger.registerOutgoingPluginChannel(plugin, "BungeeCord") + plugin.server.messenger.registerIncomingPluginChannel(plugin, "BungeeCord", this) + + // use a player to trigger server list refresh (otherwise refresh will happen on player join) + if (plugin.server.onlinePlayers.count() > 0) + refreshServerList(plugin.server.onlinePlayers.first()) + } + + override fun onDisable() { + portals.clear() + CmdPortal.serverList.clear() + + plugin.server.messenger.unregisterOutgoingPluginChannel(plugin) + plugin.server.messenger.unregisterIncomingPluginChannel(plugin) + } + + private fun loadConfig() { + plugin.reloadConfig() + val config = plugin.config.getConfigurationSection("data.portals") + + if (config != null) + portals.addAll(config.getKeys(false) + .map { name -> Portal.deserialize(name, config.get(name) as String) }) + + plugin.logger.info("Loaded ${portals.count()} portals") + } + + private fun updateConfig() { + val portalsConfig = portals.associateBy( + { portal -> portal.name }, + { portal -> portal.serialize() }) + + plugin.config.set("data.portals", portalsConfig) + plugin.saveConfig() + } + + fun addOrUpdate(name: String, p1: BlockPosition, p2: BlockPosition, destServer: String) { + portals.removeIf { portal -> portal.name == name } + val portal = Portal(name, destServer, p1.blockX(), p1.blockY(), p1.blockZ(), p2.blockX(), p2.blockY(), p2.blockZ()) + portals.add(portal) + updateConfig() + } + + fun remove(name: String) { + if (!portals.removeIf { portal -> portal.name == name }) + plugin.componentLogger.error("Failed to remove portal $name") + updateConfig() + } + + fun exists(name: String?) : Boolean { + if (name == null) + return false + + return portals.any { portal -> portal.name == name } + } + + private fun refreshServerList(player: Player) { + plugin.logger.info("Refreshing server list") + val message = ByteStreams.newDataOutput() + message.writeUTF("GetServers") + player.sendPluginMessage(plugin, "BungeeCord", message.toByteArray()) + } + + private fun usePortal(event: PlayerMoveEvent, portal: Portal) { + val stopModifier = AttributeModifier(NamespacedKey.minecraft("stop"), -1.0, AttributeModifier.Operation.ADD_SCALAR) + event.player.getAttribute(Attribute.MOVEMENT_SPEED)!!.addTransientModifier(stopModifier) + event.player.getAttribute(Attribute.JUMP_STRENGTH)!!.addTransientModifier(stopModifier) + portalCooldowns[event.player] = event.from + + val message = ByteStreams.newDataOutput() + message.writeUTF("Connect") + message.writeUTF(portal.destServer) + event.player.sendPluginMessage(plugin, "BungeeCord", message.toByteArray()) + } + + override fun onPluginMessageReceived(channel: String, player: Player, message: ByteArray) { + if (channel != "BungeeCord") + return + + val message = ByteStreams.newDataInput(message) + val subchannel = message.readUTF() + if (subchannel == "GetServers") { + val serverList = message.readUTF() + CmdPortal.serverList.clear() + CmdPortal.serverList.addAll(serverList.split(", ")) + } + } + + // Switch player server when they enter a portal + @EventHandler + fun onPlayerMove(event: PlayerMoveEvent) { + if (!Pobutils.isEnabled(this.name)) + return + + if (portalCooldowns.containsKey(event.player)) + return + + for (portal in portals) { + if (portal.contains(event.to)) { + usePortal(event, portal) + break + } + } + } + + // When they leave, reset player to where they were before they entered the portal + @EventHandler + fun onPlayerQuit(event: PlayerQuitEvent) { + if (portalCooldowns.containsKey(event.player)) { + event.player.teleport(portalCooldowns[event.player] as Location) + portalCooldowns.remove(event.player) + } + } + + // Update server list used for command completion + // Plugin message requires a player to send the event so we wait for a player + @EventHandler + fun onPlayerJoin(event: PlayerJoinEvent) { + if (Pobutils.isEnabled(this.name) && CmdPortal.serverList.isEmpty()) + plugin.server.scheduler.runTaskLaterAsynchronously(plugin, {_ -> refreshServerList(event.player) }, 10 ) + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..788472d --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,27 @@ +modules: + noJoinMessage: true + sit: true + spawn: false + portals: false + warp: false + hub: true + disableTNT: false + tabList: false + formatChat: true + disableTrample: false + snowballDamage: false +data: + spawn: + location: '' + spawnOnJoin: false + spawnOnDeath: false + portals: '' + warps: '' + formatChat: + serverAlias: '?' + messageFormat: ' : ' + formatMessageText: true +settings: + snowballDamage: + damageExceptions: [] + snowmenDontHitEachother: true \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..d24b28d --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,49 @@ +name: pobutils +version: '2.0-SNAPSHOT' +main: com.pobnellion.pobutils.Pobutils +api-version: '1.21' +prefix: PobUtils +authors: [ Bizink ] + +commands: + module: + description: configure a module + usage: /module + permission: pobutils.admin + + spawn: + description: Teleport to spawn + usage: /spawn + permission: pobutils.user + + setspawn: + description: Set spawn location + usage: /setspawn [location] + permission: pobutils.admin + + sit: + description: Sit down for a while buddy + usage: /sit + permission: pobutils.user + + portal: + description: add or remove a portal to another server + usage: /portal [x1] [y1] [z1] [x2] [y2] [z2] [destinationServer] + permission: pobutils.admin + + warp: + description: list or go to warps + usage: /warp [add|del|list|warpName] [warpName] [x] [y] [z] [yaw] [pitch] + permission: pobutils.user + + hub: + description: go to hub server + usage: /hub + permission: pobutils.user + +permissions: + pobutils.user: + default: true + + pobutils.admin: + default: op \ No newline at end of file