summaryrefslogtreecommitdiff
path: root/src/main/kotlin/io/dico/parcels2/Privilege.kt
blob: ab9db336bb72d4346d54d8e3e70272387d85dfd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package io.dico.parcels2

import io.dico.parcels2.Privilege.*
import io.dico.parcels2.PrivilegeChangeResult.*
import io.dico.parcels2.util.ext.PERM_ADMIN_MANAGE
import io.dico.parcels2.util.ext.PERM_BAN_BYPASS
import io.dico.parcels2.util.ext.PERM_BUILD_ANYWHERE
import org.bukkit.OfflinePlayer
import org.bukkit.entity.Player

enum class Privilege(
    val number: Int,
    val transient: Boolean = false
) {
    BANNED(1),
    DEFAULT(2),
    CAN_BUILD(3),
    CAN_MANAGE(4),

    OWNER(-1, transient = true),
    ADMIN(-1, transient = true);

    fun isDistanceGrEq(other: Privilege): Boolean =
        when { // used for example when disallowBuild is called and CAN_MANAGE is the privilege.
            other > DEFAULT -> this >= other
            other == DEFAULT -> this == other
            else -> this <= other
        }

    fun isChangeInDirection(positiveDirection: Boolean, update: Privilege): Boolean =
        if (positiveDirection) update > this
        else update < this

    fun requireNonTransient(): Privilege {
        if (transient) {
            throw IllegalArgumentException("Transient privilege $this is invalid")
        }
        return this
    }

    /*
    fun canEnter() = this >= BANNED
    fun canBuild() = this >= CAN_BUILD
    fun canManage() = this >= CAN_MANAGE
    */

    companion object {
        fun getByNumber(id: Int) =
            when (id) {
                1 -> BANNED
                2 -> DEFAULT
                3 -> CAN_BUILD
                4 -> CAN_MANAGE
                else -> null
            }
    }
}

typealias PrivilegeKey = PlayerProfile.Real
typealias MutablePrivilegeMap = MutableMap<PrivilegeKey, Privilege>
typealias PrivilegeMap = Map<PrivilegeKey, Privilege>

@Suppress("FunctionName")
fun MutablePrivilegeMap(): MutablePrivilegeMap = hashMapOf()

interface PrivilegesMinimal {
    val map: PrivilegeMap
    var privilegeOfStar: Privilege

    fun getStoredPrivilege(key: PrivilegeKey): Privilege
    fun setStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean
}

interface Privileges : PrivilegesMinimal {
    val keyOfOwner: PlayerProfile.Real?

    fun privilege(player: OfflinePlayer, adminPerm: String): Privilege =
        if (player is Player && player.hasPermission(adminPerm)) ADMIN
        else {
            val key = player.privilegeKey
            if (key == keyOfOwner) OWNER
            else getStoredPrivilege(key)
        }

    fun changePrivilege(key: PrivilegeKey, positive: Boolean, update: Privilege): PrivilegeChangeResult =
        if (key != keyOfOwner) FAIL_OWNER
        else if (getStoredPrivilege(key).isChangeInDirection(positive, update)
            && setStoredPrivilege(key, update)
        ) SUCCESS
        else FAIL

    fun canManage(player: OfflinePlayer) = privilege(player, PERM_ADMIN_MANAGE) >= CAN_MANAGE
    fun allowManage(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, CAN_MANAGE)
    fun disallowManage(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, CAN_BUILD)

    fun canBuild(player: OfflinePlayer) = privilege(player, PERM_BUILD_ANYWHERE) >= CAN_BUILD
    fun allowBuild(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, CAN_BUILD)
    fun disallowBuild(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, DEFAULT)

    fun canEnter(player: OfflinePlayer) = privilege(player, PERM_BAN_BYPASS) >= DEFAULT
    fun ban(player: OfflinePlayer) = changePrivilege(player.privilegeKey, false, BANNED)
    fun unban(player: OfflinePlayer) = changePrivilege(player.privilegeKey, true, DEFAULT)

    /**
     * same as [canBuild] but doesn't perform a permission check for admin perms
     */
    fun canBuildFast(player: OfflinePlayer) = player.privilegeKey.let { if (it == keyOfOwner) OWNER else getStoredPrivilege(it)} >= CAN_BUILD
}

enum class PrivilegeChangeResult {
    SUCCESS, FAIL, FAIL_OWNER
}

val OfflinePlayer.privilegeKey: PrivilegeKey
    inline get() = PlayerProfile.nameless(this)

open class PrivilegesHolder(override var map: MutablePrivilegeMap = MutablePrivilegeMap()) : PrivilegesMinimal {
    override var privilegeOfStar: Privilege = DEFAULT
        set(value) = run { field = value.requireNonTransient() }

    override fun getStoredPrivilege(key: PrivilegeKey) =
        if (key.isStar) privilegeOfStar
        else map.getOrDefault(key, privilegeOfStar)

    override fun setStoredPrivilege(key: PrivilegeKey, privilege: Privilege): Boolean {
        privilege.requireNonTransient()

        if (key.isStar) {
            if (privilegeOfStar == privilege) return false
            privilegeOfStar = privilege
            return true
        }

        return if (privilege == DEFAULT) map.remove(key) != null
        else map.put(key, privilege) != privilege
    }
}

interface GlobalPrivileges : Privileges {
    override val keyOfOwner: PlayerProfile.Real
}

interface GlobalPrivilegesManager {
    operator fun get(owner: PlayerProfile.Real): GlobalPrivileges
    operator fun get(owner: OfflinePlayer): GlobalPrivileges = get(owner.privilegeKey)
}