From 1b2fc74c65b094cdc2da6b5bd44c204dd751f3ea Mon Sep 17 00:00:00 2001 From: Moritz Ruth Date: Sat, 20 Jun 2020 17:28:09 +0200 Subject: [PATCH] Add map voting --- build.gradle.kts | 1 - .../moritzruth/spigot_ttt/CommandManager.kt | 4 +- .../de/moritzruth/spigot_ttt/Settings.kt | 1 + .../de/moritzruth/spigot_ttt/TTTPlugin.kt | 9 +- .../spigot_ttt/game/AbortCommand.kt | 9 +- .../moritzruth/spigot_ttt/game/GameManager.kt | 39 ++-- .../spigot_ttt/game/GeneralGameListener.kt | 2 +- .../moritzruth/spigot_ttt/game/InfoCommand.kt | 2 +- .../spigot_ttt/game/ReviveCommand.kt | 6 +- .../spigot_ttt/game/StartCommand.kt | 7 + .../game/items/impl/weapons/guns/Gun.kt | 2 +- .../spigot_ttt/game/players/PlayerManager.kt | 6 +- .../spigot_ttt/game/worlds/MapVoting.kt | 181 ++++++++++++++++++ .../spigot_ttt/game/worlds/TTTWorld.kt | 58 ++++++ .../spigot_ttt/game/worlds/VotingCommand.kt | 45 +++++ .../spigot_ttt/game/worlds/WorldManager.kt | 9 + .../spigot_ttt/utils/CreateTabCompleter.kt | 4 +- .../moritzruth/spigot_ttt/worlds/TTTWorld.kt | 73 ------- .../spigot_ttt/worlds/WorldCommand.kt | 29 --- .../spigot_ttt/worlds/WorldManager.kt | 24 --- src/main/resources/config.yml | 1 + src/main/resources/plugin.yml | 18 +- 22 files changed, 353 insertions(+), 177 deletions(-) create mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/MapVoting.kt create mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/TTTWorld.kt create mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/VotingCommand.kt create mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/WorldManager.kt delete mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/worlds/TTTWorld.kt delete mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldCommand.kt delete mode 100644 src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldManager.kt diff --git a/build.gradle.kts b/build.gradle.kts index 711f991..cafe112 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,6 @@ repositories { dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7") implementation("commons-codec:commons-codec:1.14") compileOnly("com.comphenix.protocol", "ProtocolLib", "4.5.0") compileOnly("org.spigotmc", "spigot-api", "1.15.2-R0.1-SNAPSHOT") diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/CommandManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/CommandManager.kt index accaa4b..464d8bd 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/CommandManager.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/CommandManager.kt @@ -5,7 +5,7 @@ import de.moritzruth.spigot_ttt.game.InfoCommand import de.moritzruth.spigot_ttt.game.ReviveCommand import de.moritzruth.spigot_ttt.game.StartCommand import de.moritzruth.spigot_ttt.game.items.AddItemSpawnCommand -import de.moritzruth.spigot_ttt.worlds.WorldCommand +import de.moritzruth.spigot_ttt.game.worlds.VotingCommand object CommandManager { fun initializeCommands() { @@ -16,6 +16,6 @@ object CommandManager { ResourcepackCommand() ReloadTTTConfigCommand() InfoCommand() - WorldCommand() + VotingCommand() } } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/Settings.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/Settings.kt index 7d3ae7b..8675201 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/Settings.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/Settings.kt @@ -13,6 +13,7 @@ object Settings { val preparingPhaseDuration get() = plugin.config.getInt("duration.preparing", 20) val combatPhaseDuration get() = plugin.config.getInt("duration.combat", 480) // 8 minutes val overPhaseDuration get() = plugin.config.getInt("duration.over", 10) + val mapVotingDuration get() = plugin.config.getInt("duration.map-voting", 10) val initialCredits get() = plugin.config.getInt("initial-credits", 2) val creditsPerKill get() = plugin.config.getInt("credits-per-kill", 1) } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/TTTPlugin.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/TTTPlugin.kt index 645937e..53d9aeb 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/TTTPlugin.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/TTTPlugin.kt @@ -1,7 +1,8 @@ package de.moritzruth.spigot_ttt import de.moritzruth.spigot_ttt.game.GameManager -import de.moritzruth.spigot_ttt.worlds.WorldManager +import de.moritzruth.spigot_ttt.game.worlds.MapVoting +import de.moritzruth.spigot_ttt.game.worlds.WorldManager import org.bukkit.ChatColor import org.bukkit.plugin.java.JavaPlugin @@ -13,8 +14,8 @@ class TTTPlugin: JavaPlugin() { override fun onEnable() { saveDefaultConfig() - WorldManager.removeNeglectedWorlds() - + WorldManager.initialize() + MapVoting.registerListener() CommandManager.initializeCommands() GameManager.initialize() @@ -25,7 +26,7 @@ class TTTPlugin: JavaPlugin() { } override fun onDisable() { - GameManager.resetWorld() + GameManager.reset() } fun broadcast(message: String, withPrefix: Boolean = true) { diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/AbortCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/AbortCommand.kt index c50b451..706aacf 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/AbortCommand.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/AbortCommand.kt @@ -18,10 +18,11 @@ class AbortCommand: CommandExecutor { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { if (GameManager.phase === null) { - sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Spiel.") - } else { - GameManager.abortGame(true) - } + val tttWorld = GameManager.tttWorld + if (tttWorld == null) + sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Spiel.") + else tttWorld.unload() + } else GameManager.abortGame(true) return true } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/GameManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/GameManager.kt index 80eacaa..410adb3 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/GameManager.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/GameManager.kt @@ -12,11 +12,15 @@ import de.moritzruth.spigot_ttt.game.items.shop.Shop import de.moritzruth.spigot_ttt.game.items.shop.ShopListener import de.moritzruth.spigot_ttt.game.players.PlayerManager import de.moritzruth.spigot_ttt.game.players.Role +import de.moritzruth.spigot_ttt.game.worlds.TTTWorld import de.moritzruth.spigot_ttt.plugin import de.moritzruth.spigot_ttt.utils.call import de.moritzruth.spigot_ttt.utils.heartsToHealth import de.moritzruth.spigot_ttt.utils.teleportToWorldSpawn -import org.bukkit.* +import org.bukkit.GameRule +import org.bukkit.Material +import org.bukkit.Sound +import org.bukkit.SoundCategory import org.bukkit.block.Block import kotlin.random.Random @@ -29,9 +33,13 @@ object GameManager { ScoreboardHelper.forEveryScoreboard { it.updateEverything(); it.showCorrectSidebarScoreboard() } } - val world = plugin.server.getWorld("world")!! + var tttWorld: TTTWorld? = null + set(value) { + field = value + if (value != null) adjustWorld() + } - val destroyedBlocks = mutableMapOf() + val world get() = tttWorld?.world ?: throw IllegalStateException("The world was not set or is not loaded") private val listeners = ItemManager.listeners .plus(GeneralGameListener) @@ -43,8 +51,6 @@ object GameManager { .plus(GeneralGameListener.packetListener) fun initialize() { - adjustWorld() - listeners.forEach { plugin.server.pluginManager.registerEvents(it, plugin) } packetListeners.forEach { ProtocolLibrary.getProtocolManager().addPacketListener(it) } } @@ -64,7 +70,7 @@ object GameManager { GameEndEvent(false).call() phase = null - resetWorld() + reset() PlayerManager.resetAfterGame() } @@ -92,21 +98,11 @@ object GameManager { } } - fun resetWorld() { + fun reset() { CorpseManager.destroyAll() ItemManager.reset() - - destroyedBlocks.forEach { (location, material) -> - world.getBlockAt(location).type = material - } - destroyedBlocks.clear() - - world.run { - setStorm(false) - time = 0 - setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false) - setGameRule(GameRule.DO_WEATHER_CYCLE, false) - } + tttWorld?.unload() + tttWorld = null } fun abortGame(broadcast: Boolean = false) { @@ -115,8 +111,8 @@ object GameManager { GameEndEvent(true).call() phase = null Timers.cancelCurrentTask() - resetWorld() PlayerManager.resetAfterGame() + reset() if (broadcast) { GameMessenger.aborted() @@ -179,9 +175,8 @@ object GameManager { } } - fun destroyBlock(block: Block) { + fun destroyBlockIfAllowed(block: Block) { if (phase != null && block.type.toString().contains("glass_pane", true)) { - destroyedBlocks[block.location] = block.type block.type = Material.AIR world.playSound( block.location, diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/GeneralGameListener.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/GeneralGameListener.kt index 94ad4d1..b7ec9a7 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/GeneralGameListener.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/GeneralGameListener.kt @@ -122,7 +122,7 @@ object GeneralGameListener : Listener { @EventHandler fun onPlayerInteract(event: PlayerInteractEvent) { if (event.player.inventory.itemInMainHand.type == Material.AIR && event.action == Action.LEFT_CLICK_BLOCK) { - GameManager.destroyBlock(event.clickedBlock!!) + GameManager.destroyBlockIfAllowed(event.clickedBlock!!) } } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/InfoCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/InfoCommand.kt index 53ce92a..2ecb3d8 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/InfoCommand.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/InfoCommand.kt @@ -14,7 +14,7 @@ class InfoCommand: CommandExecutor { init { val command = plugin.getCommand("info")!! command.tabCompleter = createTabCompleter { _, index -> - if (index == 1) PlayerManager.tttPlayers.map { it.player.name } + if (index == 0) PlayerManager.tttPlayers.map { it.player.name } else null } command.setExecutor(this) diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/ReviveCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/ReviveCommand.kt index 0b7eaa3..295ed81 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/ReviveCommand.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/ReviveCommand.kt @@ -23,14 +23,14 @@ class ReviveCommand: CommandExecutor { .map { it.player.name } if (sender is Player) when (index) { - 1 -> getPlayers().filter { it != sender.name }.run { + 0 -> getPlayers().filter { it != sender.name }.run { if (TTTPlayer.of(sender)?.alive == false) plus("here") else this } - 2 -> listOf("here") + 1 -> listOf("here") else -> null } else when (index) { - 1 -> getPlayers() + 0 -> getPlayers() else -> null } } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/StartCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/StartCommand.kt index 890096f..87cbd35 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/StartCommand.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/StartCommand.kt @@ -18,6 +18,13 @@ class StartCommand: CommandExecutor { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { if (GameManager.phase === null) { + if (GameManager.tttWorld == null) { + sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Bitte starte zuerst das Map-Voting mit " + + "${ChatColor.WHITE}/voting") + + return true + } + try { GameManager.startPreparingPhase() } catch (e: PlayerManager.NotEnoughPlayersException) { diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/items/impl/weapons/guns/Gun.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/items/impl/weapons/guns/Gun.kt index e7e1a82..be1e811 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/items/impl/weapons/guns/Gun.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/items/impl/weapons/guns/Gun.kt @@ -98,7 +98,7 @@ abstract class Gun( if (rayTraceResult !== null) { val hitBlock = rayTraceResult.hitBlock - if (hitBlock != null) GameManager.destroyBlock(hitBlock) + if (hitBlock != null) GameManager.destroyBlockIfAllowed(hitBlock) val entity = rayTraceResult.hitEntity diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/PlayerManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/PlayerManager.kt index 8c03436..83deadc 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/PlayerManager.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/PlayerManager.kt @@ -20,7 +20,11 @@ import kotlin.random.Random object PlayerManager { val tttPlayers = mutableListOf() - private fun getAvailablePlayers() = plugin.server.onlinePlayers.filter { it.gameMode === GameMode.SURVIVAL } + fun isAvailable(player: Player): Boolean { + return player.gameMode === GameMode.SURVIVAL + } + + fun getAvailablePlayers() = plugin.server.onlinePlayers.filter { isAvailable(it) } private fun getStillLivingRoleGroups() = getStillLivingRoles().map { it.group }.toSet() private fun getStillLivingRoles() = tttPlayers.filter { it.alive || SecondChance.getInstance(it)?.preventRoundEnd == true diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/MapVoting.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/MapVoting.kt new file mode 100644 index 0000000..1439daa --- /dev/null +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/MapVoting.kt @@ -0,0 +1,181 @@ +package de.moritzruth.spigot_ttt.game.worlds + +import de.moritzruth.spigot_ttt.Settings +import de.moritzruth.spigot_ttt.TTTPlugin +import de.moritzruth.spigot_ttt.game.GameManager +import de.moritzruth.spigot_ttt.game.players.PlayerManager +import de.moritzruth.spigot_ttt.plugin +import de.moritzruth.spigot_ttt.utils.* +import org.bukkit.ChatColor +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.inventory.InventoryClickEvent +import org.bukkit.event.inventory.InventoryType +import org.bukkit.event.player.PlayerDropItemEvent +import org.bukkit.event.player.PlayerGameModeChangeEvent +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.scheduler.BukkitTask +import java.util.* +import kotlin.math.max + +class MapVoting private constructor() { + private var secondsRemaining = Settings.mapVotingDuration + private val maps = WorldManager.tttWorlds.toList() + private var timerTask: BukkitTask + + private val inventory = plugin.server.createInventory( + null, + InventoryType.CHEST, + "${ChatColor.BLUE}${ChatColor.BOLD}Map-Voting" + ) + + private val votes = mutableMapOf() + + private fun createMapItemStack(map: TTTWorld): ItemStack { + val config = map.config + val iconMaterialString = config.getString("icon") ?: "GRASS_BLOCK" + val votesForThisMap = votes.values.count { it === map } + + return ItemStack(Material.valueOf(iconMaterialString), max(1, votesForThisMap)).applyMeta { + setDisplayName("${config.getString("title")}${ChatColor.RESET} ${ChatColor.GRAY}($votesForThisMap)") + lore = listOf("").plus(config.getStringList("description").map { "${ChatColor.RESET}$it" }) + hideInfo() + } + } + + init { + maps.forEachIndexed { index, map -> inventory.setItem(index, createMapItemStack(map)) } + + timerTask = plugin.server.scheduler.runTaskTimer(plugin, fun() { + if (secondsRemaining == 0) { + stop() + val votedMaps = votes.values + val winnerMap = + if (votedMaps.count() == 0) maps.random() + else { + val mapsSortedByVotes = votedMaps.sortedBy { votedMap -> votedMaps.count { it === votedMap } } + mapsSortedByVotes[0] + } + + plugin.broadcast("${ChatColor.GREEN}Ausgewählte Map: " + + winnerMap.config.getString("title")) + + if (winnerMap.world != null) winnerMap.unload() + winnerMap.load() + GameManager.tttWorld = winnerMap + plugin.server.onlinePlayers.forEach { + it.teleport(winnerMap.world!!.spawnLocation) + } + } else { + inventory.setItem(26, ItemStack(Material.CLOCK, secondsRemaining).applyMeta { + setDisplayName("${ChatColor.GREEN}Verbleibende Zeit: ${ChatColor.WHITE}${secondsRemaining}s") + }) + secondsRemaining -= 1 + } + }, 0, secondsToTicks(1).toLong()) + + PlayerManager.getAvailablePlayers().forEach { + giveVoteItem(it) + it.sendMessage("${TTTPlugin.prefix}${ChatColor.GREEN}Das Map-Voting wurde gestartet.") + } + } + + fun vote(player: Player, map: TTTWorld) { + votes[player.uniqueId] = map + inventory.setItem(maps.indexOf(map), createMapItemStack(map)) + } + + fun cancel() { + PlayerManager.getAvailablePlayers().forEach { + it.sendMessage("${TTTPlugin.prefix}${ChatColor.RED}Das Map-Voting wurde abgebrochen.") + } + stop() + } + + private fun stop() { + timerTask.cancel() + current = null + PlayerManager.getAvailablePlayers().forEach { removeVoteItem(it) } + plugin.server.onlinePlayers.forEach { if (it.openInventory.topInventory === inventory) it.closeInventory() } + } + + companion object { + var current: MapVoting? = null; private set + + private val voteItem = ItemStack(Material.PAPER).applyMeta { + setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Map-Voting") + hideInfo() + } + + private fun giveVoteItem(player: Player) { + player.inventory.setItem(8, voteItem) + } + + private fun removeVoteItem(player: Player) { + player.inventory.clear(8) + } + + fun start(): MapVoting? { + if (current != null) throw IllegalStateException("There is already a map voting in progress") + return MapVoting().also { current = it } + } + + private val listener = object : Listener { + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + if (event.item?.type == Material.PAPER && event.action.isRightClick) { + val voting = current ?: return + event.player.openInventory(voting.inventory) + } + } + + @EventHandler + fun onInventoryClick(event: InventoryClickEvent) { + val whoClicked = event.whoClicked + if (whoClicked !is Player) return + + val voting = current ?: return + if (event.clickedInventory != voting.inventory) return + event.isCancelled = true + + if (event.click.isLeftClick) { + val map = voting.maps.getOrNull(event.slot) ?: return + voting.vote(whoClicked, map) + } + } + + @EventHandler + fun onPlayerDropItem(event: PlayerDropItemEvent) { + if (current != null && event.itemDrop.itemStack.type == Material.PAPER) { + event.isCancelled = true + } + } + + @EventHandler + fun onPlayerGameModeChange(event: PlayerGameModeChangeEvent) { + nextTick { + if (current != null) { + if (PlayerManager.isAvailable(event.player)) giveVoteItem(event.player) + else removeVoteItem(event.player) + } + } + } + + @EventHandler + fun onPlayerJoinEvent(event: PlayerJoinEvent) { + if (PlayerManager.isAvailable(event.player)) { + if (current == null) removeVoteItem(event.player) + else giveVoteItem(event.player) + } + } + } + + fun registerListener() { + plugin.server.pluginManager.registerEvents(listener, plugin) + } + } +} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/TTTWorld.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/TTTWorld.kt new file mode 100644 index 0000000..372ea1a --- /dev/null +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/TTTWorld.kt @@ -0,0 +1,58 @@ +package de.moritzruth.spigot_ttt.game.worlds + +import de.moritzruth.spigot_ttt.plugin +import de.moritzruth.spigot_ttt.utils.ConfigurationFile +import org.bukkit.World +import org.bukkit.WorldCreator +import java.io.File +import java.io.FileNotFoundException + +class TTTWorld(private val sourceWorldDir: File) { + init { + if (!sourceWorldDir.exists()) throw FileNotFoundException() + } + + private val name: String = sourceWorldDir.name + val config = ConfigurationFile(sourceWorldDir.resolve("config.yml")) + + private val actualWorldName = "${WORLD_PREFIX}${name}" + private val worldDir = plugin.server.worldContainer.resolve("./$actualWorldName") + var world: World? = plugin.server.getWorld(actualWorldName); private set + + init { + if (world != null) unloadWorld() + if (worldDir.exists()) worldDir.deleteRecursively() + } + + fun load() { + if (world != null) throw IllegalStateException("The world is already loaded") + + sourceWorldDir.copyRecursively(worldDir) + loadWorld() + } + + private fun loadWorld() { + world = plugin.server.getWorld(actualWorldName) + ?: plugin.server.createWorld(WorldCreator.name(actualWorldName)) + } + + fun unload() { + if (world == null) throw IllegalStateException("The world is not loaded") + unloadWorld() + worldDir.deleteRecursively() + } + + private fun unloadWorld() { + world!!.players.forEach { it.teleport(plugin.server.getWorld("world")!!.spawnLocation) } + plugin.server.unloadWorld(actualWorldName, false) + world = null + } + + companion object { + fun createForSourceWorlds(): Set = + WORLDS_DIR.listFiles(File::isDirectory)!!.map { TTTWorld(it) }.toSet() + + const val WORLD_PREFIX = "tempworld_" + private val WORLDS_DIR = plugin.dataFolder.resolve("./worlds").also { it.mkdirs() } + } +} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/VotingCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/VotingCommand.kt new file mode 100644 index 0000000..0d0ce05 --- /dev/null +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/VotingCommand.kt @@ -0,0 +1,45 @@ +package de.moritzruth.spigot_ttt.game.worlds + +import de.moritzruth.spigot_ttt.COMMAND_RESPONSE_PREFIX +import de.moritzruth.spigot_ttt.game.GameManager +import de.moritzruth.spigot_ttt.game.GamePhase +import de.moritzruth.spigot_ttt.plugin +import de.moritzruth.spigot_ttt.utils.EmptyTabCompleter +import org.bukkit.ChatColor +import org.bukkit.command.Command +import org.bukkit.command.CommandExecutor +import org.bukkit.command.CommandSender + +class VotingCommand: CommandExecutor { + init { + val command = plugin.getCommand("voting")!! + command.setExecutor(this) + command.tabCompleter = EmptyTabCompleter + } + + override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { + if (args.count() == 1 && args[0].equals("cancel", true)) { + val voting = MapVoting.current + if (voting == null) { + sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Zurzeit läuft kein Map-Voting.") + } else { + voting.cancel() + } + + return true + } + + if (MapVoting.current == null) { + if (GameManager.phase == null || GameManager.phase == GamePhase.OVER) { + MapVoting.start() + } else { + sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Du kannst das Map-Voting nicht starten, " + + "während das Spiel läuft.") + } + } else { + sender.sendMessage("$COMMAND_RESPONSE_PREFIX${ChatColor.RED}Es läuft bereits ein Map-Voting.") + } + + return true + } +} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/WorldManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/WorldManager.kt new file mode 100644 index 0000000..b195597 --- /dev/null +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/worlds/WorldManager.kt @@ -0,0 +1,9 @@ +package de.moritzruth.spigot_ttt.game.worlds + +object WorldManager { + lateinit var tttWorlds: Set + + fun initialize() { + tttWorlds = TTTWorld.createForSourceWorlds() + } +} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/utils/CreateTabCompleter.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/utils/CreateTabCompleter.kt index 462b464..8b44cf2 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/utils/CreateTabCompleter.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/utils/CreateTabCompleter.kt @@ -8,10 +8,10 @@ fun createTabCompleter(fn: (sender: CommandSender, index: Int) -> List?) fun createTabCompleter(fn: (sender: CommandSender, index: Int, args: List) -> List?) = TabCompleter { sender, _, _, args -> - val index = args.count() + val index = args.count() - 1 val completions = - if (index == 0) emptyList() + if (index < 0) emptyList() else fn(sender, index, args.toList()) ?: emptyList() completions.filter { it.startsWith(args.last(), true) } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/TTTWorld.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/TTTWorld.kt deleted file mode 100644 index 3f2aea5..0000000 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/TTTWorld.kt +++ /dev/null @@ -1,73 +0,0 @@ -package de.moritzruth.spigot_ttt.worlds - -import de.moritzruth.spigot_ttt.plugin -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import org.bukkit.WorldCreator - -class TTTWorld(val sourceWorld: WorldManager.SourceWorld) { - var state: State = State.NOT_COPIED; private set - enum class State { - NOT_COPIED, - COPYING, - COPIED, - LOADED, - UNLOADING - } - - val id = WorldManager.tttWorlds.count() - val actualWorldName = "${WORLD_PREFIX}${id}" - private val worldDir = plugin.server.worldContainer.resolve("./$actualWorldName") - - init { - WorldManager.tttWorlds.add(this) - } - - suspend fun copy() { - if (state != State.NOT_COPIED) throw IllegalStateException("The world was already copied") - state = State.COPYING - - coroutineScope { - launch(Dispatchers.IO) { - sourceWorld.dir.copyRecursively(worldDir) - } - } - } - - fun load() { - if (state != State.COPIED) throw IllegalStateException("The world was not copied yet or already loaded") - plugin.server.createWorld(WorldCreator.name(actualWorldName)) - } - - suspend fun save() { - if (state != State.LOADED) throw IllegalStateException("The world must be loaded") - - coroutineScope { - launch(Dispatchers.IO) { - val tempWorldDir = WorldManager.worldsDir.resolve("./${sourceWorld.name}_$id") - worldDir.copyRecursively(tempWorldDir) - sourceWorld.dir.deleteRecursively() - tempWorldDir.renameTo(sourceWorld.dir) - state = State.NOT_COPIED - } - } - } - - suspend fun unloadAndRemove() { - if (state != State.LOADED) throw IllegalStateException("The world must be loaded") - state = State.UNLOADING - plugin.server.unloadWorld(actualWorldName, false) - - coroutineScope { - launch(Dispatchers.IO) { - worldDir.deleteRecursively() - state = State.NOT_COPIED - } - } - } - - companion object { - const val WORLD_PREFIX = "tempworld_" - } -} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldCommand.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldCommand.kt deleted file mode 100644 index 9851f79..0000000 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldCommand.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.moritzruth.spigot_ttt.worlds - -import de.moritzruth.spigot_ttt.plugin -import de.moritzruth.spigot_ttt.utils.createTabCompleter -import org.bukkit.command.Command -import org.bukkit.command.CommandExecutor -import org.bukkit.command.CommandSender - -class WorldCommand: CommandExecutor { - init { - val command = plugin.getCommand("world")!! - command.setExecutor(this) - command.tabCompleter = createTabCompleter { _, index, args -> - return@createTabCompleter when(index) { - 0 -> listOf("load", "save", "join", "list") - 1 -> when(args[0].toLowerCase()) { - "load" -> WorldManager.sourceWorlds.map { it.name } - "save", "join" -> WorldManager.tttWorlds.map { it.id.toString() } - else -> null - } - else -> null - } - } - } - - override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array): Boolean { - TODO("Not yet implemented") - } -} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldManager.kt deleted file mode 100644 index 75fac20..0000000 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/worlds/WorldManager.kt +++ /dev/null @@ -1,24 +0,0 @@ -package de.moritzruth.spigot_ttt.worlds - -import de.moritzruth.spigot_ttt.plugin -import de.moritzruth.spigot_ttt.utils.ConfigurationFile -import java.io.File - -object WorldManager { - data class SourceWorld(val dir: File) { - val name: String = dir.name - val config = ConfigurationFile(dir.resolve("config.yml")) - } - - val worldsDir = plugin.dataFolder.resolve("./worlds").also { it.mkdirs() } - val sourceWorlds = worldsDir.listFiles(File::isDirectory)!!.map { SourceWorld(it) } - - val tttWorlds = mutableSetOf() - - fun removeNeglectedWorlds() { - plugin.server.worldContainer.listFiles { file -> - file.isDirectory && file.name.startsWith(TTTWorld.WORLD_PREFIX) && - tttWorlds.find { it.actualWorldName == file.name } != null - }!!.forEach { it.deleteRecursively() } - } -} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3f63521..e2e8158 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -12,6 +12,7 @@ duration: preparing: 20 combat: 480 over: 10 + map-voting: 10 initial-credits: 2 credits-per-kill: 1 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 0d53983..4c5270b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -7,15 +7,20 @@ depend: - ProtocolLib commands: + voting: + usage: /voting ['cancel'] + permission: ttt.voting + description: Start (or cancel) the map voting + start: usage: /start permission: ttt.start - description: Starts the TTT game + description: Start the TTT game abort: usage: /abort permission: ttt.abort - description: Aborts the TTT game + description: Go back to the lobby world and abort the TTT game, if it is running additemspawn: usage: /additemspawn @@ -23,12 +28,12 @@ commands: description: Add an item spawn revive: - usage: /revive [Player] ['here'] + usage: /revive [player] ['here'] permission: ttt.revive description: Revive yourself or another player at the world spawn or at your location info: - usage: /info [Player] + usage: /info [player] permission: ttt.info description: Show information about all players or a specific player @@ -45,8 +50,3 @@ commands: permission: ttt.reload aliases: - rt - - world: - usage: /world load OR /world <'join'|'save'> OR /world list - description: Perform world operations - permission: ttt.world