@@ -20,6 +20,9 @@ import org.jetbrains.exposed.v1.datetime.datetime
2020import org.jetbrains.exposed.v1.jdbc.*
2121import org.jetbrains.exposed.v1.jdbc.transactions.transaction
2222import org.jetbrains.exposed.v1.json.json
23+ import java.util.concurrent.locks.ReentrantReadWriteLock
24+ import kotlin.concurrent.read
25+ import kotlin.concurrent.write
2326
2427object CasDataManager {
2528
@@ -29,17 +32,52 @@ object CasDataManager {
2932 val lastAccess = datetime(" last_access" )
3033 val data = json<SpamData >(" data" , { it.serialize() }, { it.deserialize() })
3134 }
32- val cacheById = hashMapOf<Int , SpamData >()
33- val cacheByIp = hashMapOf<String , SpamData >()
35+ private val cacheById = hashMapOf<Int , SpamDataHolder >()
36+ private val cacheByIp = hashMapOf<String , SpamDataHolder >()
37+ private val lock = ReentrantReadWriteLock ()
3438
3539 operator fun get (user : PlayerUser ): SpamData {
36- cacheByIp[user.addr]?.let {
37- return it
40+ return getHolder(user).spamData
41+ }
42+
43+ fun getHolder (user : PlayerUser ): SpamDataHolder {
44+ lock.read {
45+ cacheByIp[user.addr]?.let {
46+ return it
47+ }
48+ }
49+ lock.write {
50+ val created = SpamDataHolder (SpamData ())
51+ cacheById[user.dbId] = created
52+ cacheByIp[user.addr] = created
53+ return created
54+ }
55+ }
56+
57+ fun getCache (user : PlayerUser ): SpamData ? {
58+ return lock.read { cacheByIp[user.addr]?.spamData }
59+ }
60+
61+ fun purgeCache (deleteDb : Boolean ) {
62+ val time = System .currentTimeMillis()
63+ val toDel = mutableListOf<Any >()
64+ val handler = { map: MutableMap <out Any , SpamDataHolder > ->
65+ val iterator = map.iterator()
66+ for ((key, value) in iterator) {
67+ if (time - value.spamData.lastAccess > config.userDataExpiresAfter.toMillis()) {
68+ if (deleteDb)
69+ toDel.add(key)
70+ iterator.remove()
71+ }
72+ }
73+ }
74+ lock.write {
75+ handler(cacheById)
76+ handler(cacheByIp)
77+ }
78+ if (toDel.isNotEmpty()) {
79+ deleteExpiredAsync(keys = toDel)
3880 }
39- val created = SpamData ()
40- cacheById[user.dbId] = created
41- cacheByIp[user.addr] = created
42- return created
4381 }
4482
4583 init {
@@ -87,51 +125,55 @@ object CasDataManager {
87125 Bukkit .getOnlinePlayers().forEach { loadSpamData(it.user) }
88126 }
89127
90- fun loadSpamData (where : PlayerUser , async : Boolean = true ) {
128+ fun loadSpamData (where : PlayerUser ) {
91129 val dbId = where.dbId
92130 val addr = where.addr
93131
94- fun func () {
95- var spamData = latest(cacheById[dbId], cacheByIp[addr]) // Current cached
132+ StorageManager .coroutineScope.launch {
96133 with (ChatSpamTable ) {
97134 transaction(database) {
98135 selectAll().where { (ip eq addr) or (user eq dbId) }.orderBy(lastAccess, SortOrder .DESC )
99136 .limit(1 ).singleOrNull()?.let { row ->
100- spamData = latest(spamData, row[data])!! .also { sd ->
137+ val cached = lock.read { latest(cacheById[dbId], cacheByIp[addr]) }
138+ latest(cached, row[data]).also { holder ->
101139 val ip = row[ip]
102- cacheById[row[user]] = sd
103- cacheByIp[ip] = sd
104- Bukkit .getOnlinePlayers().filter { it.address!! .hostString == ip }.forEach { cacheById[it.user.dbId] = sd }
140+ lock.write {
141+ cacheById[row[user]] = holder
142+ cacheByIp[ip] = holder
143+ }
105144 }
106145 }
107146 }
108147 }
109- spamData?.let { sd ->
110- cacheById[dbId] = sd
111- cacheByIp[addr] = sd
112- Bukkit .getOnlinePlayers().filter { it.address!! .hostString == addr }.forEach { cacheById[it.user.dbId] = sd }
113- }
114- }
115-
116- if (async) {
117- StorageManager .coroutineScope.launch {
118- func()
119- }
120- } else {
121- func()
122148 }
123149 }
124150
125- private fun latest (o1 : SpamData ? , o2 : SpamData ? ): SpamData ? {
151+ private fun latest (o1 : SpamDataHolder ? , o2 : SpamDataHolder ? ): SpamDataHolder ? {
126152 if (o1 == null )
127153 return o2
128154 if (o2 == null )
129155 return null
130- return if (o1.lastAccess > o2.lastAccess) o1 else o2
156+ return if (o1.spamData.lastAccess > o2.spamData.lastAccess) {
157+ o2.spamData = o1.spamData
158+ o1
159+ } else {
160+ o1.spamData = o2.spamData
161+ o2
162+ }
163+ }
164+
165+ private fun latest (holder : SpamDataHolder ? , data : SpamData ): SpamDataHolder {
166+ if (holder == null )
167+ return SpamDataHolder (data)
168+ if (holder.spamData.lastAccess < data.lastAccess) {
169+ holder.spamData = data
170+ }
171+ return holder
131172 }
132173
133174 fun saveSpamDataNow (where : PlayerUser ) {
134- val spamData = latest(cacheById[where.dbId], cacheByIp[where.addr]) ? : return
175+ val holder = lock.read { latest(cacheById[where.dbId], cacheByIp[where.addr]) } ? : return
176+ val spamData = holder.spamData
135177 val lastAccessValue = kotlin.math.max(spamData.lastAccess, spamData.muteUntil).localDateTime
136178 with (ChatSpamTable ) {
137179 transaction(database) {
0 commit comments