Fix various minor bugs related to commands and implement portal publishing

This commit is contained in:
Gabriel Tofvesson 2021-11-01 23:15:09 +01:00
parent 68c6201d23
commit 5b129172ae
5 changed files with 90 additions and 30 deletions

View File

@ -124,6 +124,11 @@ spigot {
defaults = "op" defaults = "op"
} }
create("portals.modify.publish") {
description = "Allows changing the publicity state of portals"
defaults = "op"
}
create("portals.modify.*") { create("portals.modify.*") {
description = "Wildcard portal modification" description = "Wildcard portal modification"
defaults = "op" defaults = "op"
@ -132,7 +137,8 @@ spigot {
"portals.modify.edit" to true, "portals.modify.edit" to true,
"portals.modify.target" to true, "portals.modify.target" to true,
"portals.modify.allow" to true, "portals.modify.allow" to true,
"portals.modify.other" to true "portals.modify.other" to true,
"portals.modify.publish" to true
) )
} }

View File

@ -112,24 +112,32 @@ class MultiSortedList<E> constructor(
* Get all contiguous elements that match a comparison * Get all contiguous elements that match a comparison
* @return Iterable object of all matching elements, or null if none exist * @return Iterable object of all matching elements, or null if none exist
*/ */
fun getAll(comparator: Comparator<in E>, comparison: (E) -> Int): Iterable<E>? { fun getAll(comparator: Comparator<in E>, findBase: (E) -> Int, compare: (E) -> Int): Iterable<E>? {
var index = search(comparator, comparison) var index = search(comparator, findBase)
if (index < 0) return null if (index < 0) {
index = -(index + 1)
if (index >= size || compare(get(index, comparator)) != 0)
return null
}
// This should help with accessing entries in sequential collections (e.g. linked lists)
val iterator = (extraLists[comparator] ?: this).subList(index, size).iterator()
val result = LinkedList<E>() val result = LinkedList<E>()
while (iterator.hasNext()) {
var element: E val element = iterator.next()
do { if (compare(element) != 0)
element = get(index++, comparator)
if (comparison(element) != 0)
break break
result += element result += element
} while (index < size) }
return result return result
} }
fun getAll(comparator: Comparator<in E>, comparison: (E) -> Int) = getAll(comparator, comparison, comparison)
fun findValueOrNull(comparator: Comparator<in E>, comparison: (E) -> Int): E? { fun findValueOrNull(comparator: Comparator<in E>, comparison: (E) -> Int): E? {
val index = search(comparator, comparison) val index = search(comparator, comparison)

View File

@ -57,7 +57,8 @@ class PortalCommand(
permissionTpOther: Permission, permissionTpOther: Permission,
permissionInfo: Permission, permissionInfo: Permission,
permissionInfoOther: Permission, permissionInfoOther: Permission,
permissionEdit: Permission permissionEdit: Permission,
permissionPublish: Permission
): CommandExecutor, TabCompleter { ): CommandExecutor, TabCompleter {
// Arg parse node for targeting a portal owned by the sender // Arg parse node for targeting a portal owned by the sender
private val senderPortalParseNode: ArgNode<Portal> = private val senderPortalParseNode: ArgNode<Portal> =
@ -138,6 +139,10 @@ class PortalCommand(
.branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("pitch"), PARSE_NODE_DECIMAL)) // portals edit [name] pitch [number] .branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("pitch"), PARSE_NODE_DECIMAL)) // portals edit [name] pitch [number]
.branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("yaw"))) // portals edit [name] yaw .branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("yaw"))) // portals edit [name] yaw
.branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("pitch"))) // portals edit [name] pitch .branch(PermissionParseBranch(permissionEdit, false, constantParseNode("edit"), senderPortalParseNode, constantParseNode("pitch"))) // portals edit [name] pitch
.branch(PermissionParseBranch(permissionPublish, false, constantParseNode("publish"), senderPortalParseNode)) // portals publish [name]
.branch(PermissionParseBranch(permissionPublish, true, constantParseNode("publish"), PARSE_NODE_PLAYER, otherPortalParseNode)) // portals publish [player] [name]
.branch(PermissionParseBranch(permissionPublish, false, constantParseNode("unpublish"), senderPortalParseNode)) // portals unpublish [name]
.branch(PermissionParseBranch(permissionPublish, true, constantParseNode("unpublish"), PARSE_NODE_PLAYER, otherPortalParseNode)) // portals unpublish [player] [name]
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean { override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
when (val result = portalParse.getMatch(args, sender)) { when (val result = portalParse.getMatch(args, sender)) {
@ -263,7 +268,7 @@ class PortalCommand(
} }
"tp" -> { "tp" -> {
(result.match.last() as Portal).teleportPlayerTo(sender as Player) portalManager.teleportTo(sender as Player, result.match.last() as Portal)
null null
} }
@ -287,11 +292,11 @@ class PortalCommand(
"edit" -> { "edit" -> {
val isExplicit = result.match.last() is Double val isExplicit = result.match.last() is Double
val last = if (isExplicit) result.match.last() as Double else null val last = if (isExplicit) result.match.last() as Double else null
val portal = result.match[result.match.size - 3] as Portal val portal = result.match[1] as Portal
sender as Player sender as Player
when(result.match[result.match.lastIndex - (if (isExplicit) 1 else 0)] as String) { when(result.match[2] as String) {
"yaw" -> { "yaw" -> {
portal.yaw = if(isExplicit) last!!.toFloat() else sender.location.yaw portal.yaw = if(isExplicit) last!!.toFloat() else sender.location.yaw
RESULT_SUCCESS_EDIT_YAW.format(portal.yaw) RESULT_SUCCESS_EDIT_YAW.format(portal.yaw)
@ -304,6 +309,18 @@ class PortalCommand(
} }
} }
"publish", "unpublish" -> {
val publish = result.match.first() == "publish"
val portal = result.match.last() as Portal
if (portal.public == publish) {
"Nothing changed"
} else {
portal.public = publish
String.format(Locale.ROOT, "Portal \"%s\" is now %s", portal.name, if(publish) "public" else "private")
}
}
else -> RESULT_ERROR_UNKNOWN else -> RESULT_ERROR_UNKNOWN
} }

View File

@ -5,12 +5,14 @@ import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList import org.bukkit.event.HandlerList
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerLoginEvent
import org.bukkit.event.player.PlayerMoveEvent import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.event.player.PlayerQuitEvent import org.bukkit.event.player.PlayerQuitEvent
import org.bukkit.plugin.Plugin import org.bukkit.plugin.Plugin
import java.lang.Long.max
import java.util.* import java.util.*
import java.util.logging.Logger import java.util.logging.Logger
import kotlin.math.max
import kotlin.math.min
private const val PATH_DATA_PLAYERS = "players" private const val PATH_DATA_PLAYERS = "players"
private const val PATH_DATA_WORLDS = "worlds" private const val PATH_DATA_WORLDS = "worlds"
@ -281,17 +283,25 @@ class PortalManager(private val data: ConfigurationSection, private val config:
portals.getAll(COMPARATOR_PORTAL_OWNER_NAME) { owner.uniqueId.compareTo(it.owner.uniqueId) } portals.getAll(COMPARATOR_PORTAL_OWNER_NAME) { owner.uniqueId.compareTo(it.owner.uniqueId) }
fun getPortalsByPartialName(owner: OfflinePlayer, namePart: String) = fun getPortalsByPartialName(owner: OfflinePlayer, namePart: String) =
portals.getAll(COMPARATOR_PORTAL_OWNER_NAME) { portals.getAll(COMPARATOR_PORTAL_OWNER_NAME,
compareValues( {
it.owner::getUniqueId to owner::getUniqueId, compareValues(
{ it.name.substring(0, namePart.length.coerceAtMost(it.name.length)) } to { namePart } it.owner::getUniqueId to owner::getUniqueId,
) { it.name } to { namePart }
} )
},
{
compareValues(
it.owner::getUniqueId to owner::getUniqueId,
{ it.name.substring(0 until min(it.name.length, namePart.length)) } to { namePart }
)
},
)
fun getPortalsAt(location: Location) = fun getPortalsAt(location: Location) =
portals.getAll(COMPARATOR_PORTAL_LOCATION_OWNER, location.portalComparison(worlds::getIndex)) portals.getAll(COMPARATOR_PORTAL_LOCATION_OWNER, location.portalComparison(worlds::getIndex))
fun teleportPlayerTo(player: Player, portal: Portal) { fun enterPortal(player: Player, portal: Portal) {
val result = portal.enterPortal(player, this::getPortal) val result = portal.enterPortal(player, this::getPortal)
if (result is PortalResult.SUCCESS) if (result is PortalResult.SUCCESS)
triggerCooldown(player, result.link) triggerCooldown(player, result.link)
@ -300,6 +310,11 @@ class PortalManager(private val data: ConfigurationSection, private val config:
.warning("${player.name} failed to enter portal ${portal.name} (${portal.owner.playerName}; ${portal.world.name}; ${portal.x}, ${portal.y}, ${portal.z})") .warning("${player.name} failed to enter portal ${portal.name} (${portal.owner.playerName}; ${portal.world.name}; ${portal.x}, ${portal.y}, ${portal.z})")
} }
fun teleportTo(player: Player, portal: Portal) {
portal.teleportPlayerTo(player)
triggerCooldown(player, portal)
}
private fun popCooldowns(player: OfflinePlayer, moveTo: Location) { private fun popCooldowns(player: OfflinePlayer, moveTo: Location) {
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
while (cooldowns.isNotEmpty()) { while (cooldowns.isNotEmpty()) {
@ -323,6 +338,16 @@ class PortalManager(private val data: ConfigurationSection, private val config:
touchPortalCooldown[player.uniqueId] = portal touchPortalCooldown[player.uniqueId] = portal
} }
fun getAccessiblePortals(player: Player, location: Location = player.location) =
getPortalsAt(location)?.filter {
it.checkEnter(player, this::getPortal) is PortalResult.SUCCESS
}
fun getAccessiblePortal(player: Player, location: Location = player.location): Portal? {
val found = getAccessiblePortals(player, location)
return found?.firstOrNull { it.owner.uniqueId == player.uniqueId } ?: found?.firstOrNull()
}
@EventHandler @EventHandler
fun onPlayerMove(moveEvent: PlayerMoveEvent) { fun onPlayerMove(moveEvent: PlayerMoveEvent) {
val to = moveEvent.to val to = moveEvent.to
@ -331,15 +356,18 @@ class PortalManager(private val data: ConfigurationSection, private val config:
// If we're ignoring player movements for this player, just return immediately // If we're ignoring player movements for this player, just return immediately
if (isOnCooldown(moveEvent.player, to)) return if (isOnCooldown(moveEvent.player, to)) return
val found = getPortalsAt(to) enterPortal(moveEvent.player, getAccessiblePortal(moveEvent.player, to) ?: return)
}
}
val triggered = found?.firstOrNull { @EventHandler
it.owner.uniqueId == moveEvent.player.uniqueId && it.checkEnter(moveEvent.player, this::getPortal) is PortalResult.SUCCESS fun onPlayerJoin(loginEvent: PlayerLoginEvent) {
val portal = getAccessiblePortal(loginEvent.player)
if (portal != null) {
synchronized(touchPortalCooldown) {
triggerCooldown(loginEvent.player, portal)
} }
?: found?.firstOrNull { it.checkEnter(moveEvent.player, this::getPortal) is PortalResult.SUCCESS }
if (triggered != null)
teleportPlayerTo(moveEvent.player, triggered)
} }
} }

View File

@ -27,7 +27,8 @@ class PortalsPlugin: JavaPlugin() {
description.permissions.first { it.name == "portals.tp.other" }, description.permissions.first { it.name == "portals.tp.other" },
description.permissions.first { it.name == "portals.info" }, description.permissions.first { it.name == "portals.info" },
description.permissions.first { it.name == "portals.info.other" }, description.permissions.first { it.name == "portals.info.other" },
description.permissions.first { it.name == "portals.modify.edit" } description.permissions.first { it.name == "portals.modify.edit" },
description.permissions.first { it.name == "portals.modify.publish" }
) )
val pluginCommand = getCommand("portals")!! val pluginCommand = getCommand("portals")!!