diff options
Diffstat (limited to 'src/main/kotlin/io/dico/parcels2/PlayerProfile.kt')
-rw-r--r-- | src/main/kotlin/io/dico/parcels2/PlayerProfile.kt | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt new file mode 100644 index 0000000..c5403cd --- /dev/null +++ b/src/main/kotlin/io/dico/parcels2/PlayerProfile.kt @@ -0,0 +1,287 @@ +@file:Suppress("unused", "UsePropertyAccessSyntax", "DEPRECATION") + +package io.dico.parcels2 + +import io.dico.parcels2.storage.Storage +import io.dico.parcels2.util.getPlayerNameOrDefault +import io.dico.parcels2.util.isValid +import io.dico.parcels2.util.uuid +import kotlinx.coroutines.experimental.Deferred +import kotlinx.coroutines.experimental.Unconfined +import kotlinx.coroutines.experimental.async +import org.bukkit.Bukkit +import org.bukkit.OfflinePlayer +import java.util.UUID + +interface PlayerProfile { + val uuid: UUID? get() = null + val name: String? + val notNullName: String + val isStar: Boolean get() = false + + fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean + + fun equals(other: PlayerProfile): Boolean + + override fun equals(other: Any?): Boolean + override fun hashCode(): Int + + val isFake: Boolean get() = this is Fake + val isReal: Boolean get() = this is Real + + companion object { + fun safe(uuid: UUID?, name: String?): PlayerProfile? { + if (uuid != null) return Real(uuid, name) + if (name != null) return invoke(name) + return null + } + + operator fun invoke(uuid: UUID?, name: String?): PlayerProfile { + return safe(uuid, name) ?: throw IllegalArgumentException("One of uuid and name must not be null") + } + + operator fun invoke(uuid: UUID): Real { + if (uuid == Star.uuid) return Star + return RealImpl(uuid, null) + } + + operator fun invoke(name: String): PlayerProfile { + if (name == Star.name) return Star + return Fake(name) + } + + operator fun invoke(player: OfflinePlayer): PlayerProfile { + return if (player.isValid) Real(player.uuid, player.name) else Fake(player.name) + } + + fun nameless(player: OfflinePlayer): Real { + if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid") + return RealImpl(player.uuid, null) + } + + fun byName(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile { + if (!allowReal) { + if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true") + return Fake(input) + } + + if (input == Star.name) return Star + + return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) } + ?: Unresolved(input) + } + } + + interface Real : PlayerProfile { + override val uuid: UUID + override val notNullName: String + get() = name ?: getPlayerNameOrDefault(uuid) + + val player: OfflinePlayer? get() = Bukkit.getOfflinePlayer(uuid).takeIf { it.isValid } + val playerUnchecked: OfflinePlayer get() = Bukkit.getOfflinePlayer(uuid) + + override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { + return uuid == player.uuid || (allowNameMatch && name?.let { it == player.name } == true) + } + + override fun equals(other: PlayerProfile): Boolean { + return other is Real && uuid == other.uuid + } + + companion object { + fun byName(name: String): PlayerProfile { + if (name == Star.name) return Star + return Unresolved(name) + } + + operator fun invoke(uuid: UUID, name: String?): Real { + if (name == Star.name || uuid == Star.uuid) return Star + return RealImpl(uuid, name) + } + + fun safe(uuid: UUID?, name: String?): Real? { + if (name == Star.name || uuid == Star.uuid) return Star + if (uuid == null) return null + return RealImpl(uuid, name) + } + + } + } + + object Star : BaseImpl(), Real { + override val name: String = "*" + override val uuid: UUID = UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1") + override val isStar: Boolean get() = true + + override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { + return true + } + } + + abstract class NameOnly(override val name: String) : BaseImpl() { + override val notNullName get() = name + + override fun matches(player: OfflinePlayer, allowNameMatch: Boolean): Boolean { + return allowNameMatch && player.name == name + } + } + + class Fake(name: String) : NameOnly(name) { + override fun equals(other: PlayerProfile): Boolean { + return other is Fake && other.name == name + } + } + + class Unresolved(name: String) : NameOnly(name) { + override fun equals(other: PlayerProfile): Boolean { + return other is Unresolved && name == other.name + } + + fun tryResolve(storage: Storage): Deferred<Real?> { + return async(Unconfined) { tryResolveSuspendedly(storage) } + } + + suspend fun tryResolveSuspendedly(storage: Storage): Real? { + return storage.getPlayerUuidForName(name).await()?.let { RealImpl(it, name) } + } + + fun resolve(uuid: UUID): Real { + return RealImpl(uuid, name) + } + + fun throwException(): Nothing { + throw IllegalArgumentException("A UUID for the player $name can not be found") + } + } + + abstract class BaseImpl : PlayerProfile { + override fun equals(other: Any?): Boolean { + return this === other || (other is PlayerProfile && equals(other)) + } + + override fun hashCode(): Int { + return uuid?.hashCode() ?: name!!.hashCode() + } + } + + private class RealImpl(override val uuid: UUID, override val name: String?) : BaseImpl(), Real + +} + + +/* + + +/** + * This class can represent: + * + * An existing player + * A fake player (with only a name) + * An existing player who must have its uuid resolved from the database (after checking against Bukkit OfflinePlayer) + * STAR profile, which matches everyone. This profile is considered a REAL player, because it can have an added status. + */ +class PlayerProfile2 private constructor(uuid: UUID?, + val name: String?, + val isReal: Boolean = uuid != null) { + private var _uuid: UUID? = uuid + val notNullName: String get() = name ?: getPlayerNameOrDefault(uuid!!) + + val uuid: UUID? get() = _uuid ?: if (isReal) throw IllegalStateException("This PlayerProfile must be resolved first") else null + + companion object { + // below uuid is just a randomly generated one (version 4). Hopefully no minecraft player will ever have it :) + val star = PlayerProfile(UUID.fromString("7d09c4c6-117d-4f36-9778-c4d24618cee1"), "*", true) + + fun nameless(player: OfflinePlayer): PlayerProfile { + if (!player.isValid) throw IllegalArgumentException("The given OfflinePlayer is not valid") + return PlayerProfile(player.uuid) + } + + fun fromNameAndUuid(name: String?, uuid: UUID?): PlayerProfile? { + if (name == null && uuid == null) return null + if (star.name == name && star._uuid == uuid) return star + return PlayerProfile(uuid, name) + } + + fun realPlayerByName(name: String): PlayerProfile { + return fromString(name, allowReal = true, allowFake = false) + } + + fun fromString(input: String, allowReal: Boolean = true, allowFake: Boolean = false): PlayerProfile { + if (!allowReal) { + if (!allowFake) throw IllegalArgumentException("at least one of allowReal and allowFake must be true") + return PlayerProfile(input) + } + + if (input == star.name) return star + + return Bukkit.getOfflinePlayer(input).takeIf { it.isValid }?.let { PlayerProfile(it) } + ?: PlayerProfile(null, input, !allowFake) + } + + operator fun invoke(name: String): PlayerProfile { + if (name == star.name) return star + return PlayerProfile(null, name) + } + + operator fun invoke(uuid: UUID): PlayerProfile { + if (uuid == star.uuid) return star + return PlayerProfile(uuid, null) + } + + operator fun invoke(player: OfflinePlayer): PlayerProfile { + // avoid UUID comparison against STAR + return if (player.isValid) PlayerProfile(player.uuid, player.name) else invoke(player.name) + } + } + + val isStar: Boolean get() = this === star || (name == star.name && _uuid == star._uuid) + val hasUUID: Boolean get() = _uuid != null + val mustBeResolved: Boolean get() = isReal && _uuid == null + + val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) } + + val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayerExact(name) } + val offlinePlayer: OfflinePlayer? get() = uuid?.let { Bukkit.getOfflinePlayer(it).takeIf { it.isValid } } + val offlinePlayerAllowingNameMatch: OfflinePlayer? + get() = offlinePlayer ?: Bukkit.getOfflinePlayer(name).takeIf { it.isValid } + + fun matches(player: OfflinePlayer, allowNameMatch: Boolean = false): Boolean { + if (isStar) return true + return uuid?.let { it == player.uniqueId } ?: false + || (allowNameMatch && name?.let { it == player.name } ?: false) + } + + fun equals(other: PlayerProfile): Boolean { + return if (_uuid != null) _uuid == other._uuid + else other._uuid == null && isReal == other.isReal && name == other.name + } + + override fun equals(other: Any?): Boolean { + return other is PlayerProfile && equals(other) + } + + override fun hashCode(): Int { + return _uuid?.hashCode() ?: name!!.hashCode() + } + + /** + * resolve the uuid of this player profile if [mustBeResolved], using specified [storage]. + * returns true if the PlayerProfile has a uuid after this call. + */ + suspend fun resolve(storage: Storage): Boolean { + if (mustBeResolved) { + val uuid = storage.getPlayerUuidForName(name!!).await() + _uuid = uuid + return uuid != null + } + return _uuid != null + } + + fun resolve(uuid: UUID) { + if (isReal && _uuid == null) { + _uuid = uuid + } + } +} +*/ |