summaryrefslogtreecommitdiff
path: root/src/main/kotlin/io/dico/parcels2/Parcel.kt
blob: b3eb7c906e541f93a81ae8a645106dbe74f2937a (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package io.dico.parcels2

import io.dico.parcels2.math.Vec2i
import io.dico.parcels2.util.getPlayerName
import io.dico.parcels2.util.hasBuildAnywhere
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import java.util.*

interface ParcelData {
    var owner: ParcelOwner?
    val added: Map<UUID, AddedStatus>

    fun getAddedStatus(uuid: UUID): AddedStatus
    fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean
    fun isBanned(uuid: UUID): Boolean
    fun isAllowed(uuid: UUID): Boolean
    fun canBuild(player: Player): Boolean

    var allowInteractInputs: Boolean
    var allowInteractInventory: Boolean

    fun isOwner(uuid: UUID): Boolean {
        return owner?.uuid == uuid
    }

    val infoString: String
        get() {
            TODO()
        }
}

/**
 * Parcel implementation of ParcelData will update the database when changes are made.
 * To change the data without updating the database, defer to the data delegate instance.
 *
 * This should be used for example in database query callbacks.
 * However, this implementation is intentionally not thread-safe.
 * Therefore, database query callbacks should schedule their updates using the bukkit scheduler.
 */
class Parcel(val world: ParcelWorld, val pos: Vec2i) : ParcelData {
    val id get() = "${pos.x}:${pos.z}"
    val homeLocation get() = world.generator.getHomeLocation(this)

    var data: ParcelData = ParcelDataHolder(); private set

    fun copyDataIgnoringDatabase(data: ParcelData) {
        this.data = data
    }

    fun copyData(data: ParcelData) {
        world.storage.setParcelData(this, data)
        this.data = data
    }

    override val added: Map<UUID, AddedStatus> get() = data.added
    override fun getAddedStatus(uuid: UUID) = data.getAddedStatus(uuid)
    override fun isBanned(uuid: UUID) = data.isBanned(uuid)
    override fun isAllowed(uuid: UUID) = data.isAllowed(uuid)
    override fun canBuild(player: Player) = data.canBuild(player)

    override var owner: ParcelOwner?
        get() = data.owner
        set(value) {
            if (data.owner != value) {
                world.storage.setParcelOwner(this, value)
                data.owner = value
            }
        }

    override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean {
        return data.setAddedStatus(uuid, status).also {
            if (it) world.storage.setParcelPlayerState(this, uuid, status.asBoolean)
        }
    }

    override var allowInteractInputs: Boolean
        get() = data.allowInteractInputs
        set(value) {
            if (data.allowInteractInputs == value) return
            world.storage.setParcelAllowsInteractInputs(this, value)
            data.allowInteractInputs = value
        }

    override var allowInteractInventory: Boolean
        get() = data.allowInteractInventory
        set(value) {
            if (data.allowInteractInventory == value) return
            world.storage.setParcelAllowsInteractInventory(this, value)
            data.allowInteractInventory = value
        }
}

class ParcelDataHolder : ParcelData {
    override var added = mutableMapOf<UUID, AddedStatus>()
    override var owner: ParcelOwner? = null

    override fun getAddedStatus(uuid: UUID): AddedStatus = added.getOrDefault(uuid, AddedStatus.DEFAULT)
    override fun setAddedStatus(uuid: UUID, status: AddedStatus): Boolean = status.takeIf { it != AddedStatus.DEFAULT }
        ?.let { added.put(uuid, it) != it }
        ?: added.remove(uuid) != null

    override fun isBanned(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.BANNED
    override fun isAllowed(uuid: UUID) = getAddedStatus(uuid) == AddedStatus.ALLOWED
    override fun canBuild(player: Player) = isAllowed(player.uniqueId)
        || owner?.matches(player, allowNameMatch = false) ?: false
        || player.hasBuildAnywhere

    override var allowInteractInputs = true
    override var allowInteractInventory = true
}

enum class AddedStatus {
    DEFAULT,
    ALLOWED,
    BANNED;

    val asBoolean
        get() = when (this) {
            DEFAULT -> null
            ALLOWED -> true
            BANNED -> false
        }
}

@Suppress("UsePropertyAccessSyntax")
class ParcelOwner(val uuid: UUID? = null,
                  name: String? = null) {

    companion object {
        fun create(uuid: UUID?, name: String?): ParcelOwner? {
            return uuid?.let { ParcelOwner(uuid, name) }
                ?: name?.let { ParcelOwner(uuid, name) }
        }
    }

    val name: String?

    init {
        uuid ?: name ?: throw IllegalArgumentException("uuid and/or name must be present")

        if (name != null) this.name = name
        else {
            val offlinePlayer = Bukkit.getOfflinePlayer(uuid).takeIf { it.isOnline() || it.hasPlayedBefore() }
            this.name = offlinePlayer?.name
        }
    }

    val playerName get() = getPlayerName(uuid, name)

    fun matches(player: Player, allowNameMatch: Boolean = false): Boolean {
        return uuid?.let { it == player.uniqueId } ?: false
            || (allowNameMatch && name?.let { it == player.name } ?: false)
    }

    val onlinePlayer: Player? get() = uuid?.let { Bukkit.getPlayer(uuid) }
    val onlinePlayerAllowingNameMatch: Player? get() = onlinePlayer ?: name?.let { Bukkit.getPlayer(name) }

    @Suppress("DEPRECATION")
    val offlinePlayer
        get() = (uuid?.let { Bukkit.getOfflinePlayer(it) } ?: Bukkit.getOfflinePlayer(name))
            ?.takeIf { it.isOnline() || it.hasPlayedBefore() }
}