11package dev.slne.surf.cloud.core.common.player
22
3+ import com.github.benmanes.caffeine.cache.Caffeine
4+ import com.sksamuel.aedile.core.asCache
35import dev.slne.surf.cloud.api.common.event.player.connection.CloudPlayerConnectToNetworkEvent
46import dev.slne.surf.cloud.api.common.event.player.connection.CloudPlayerDisconnectFromNetworkEvent
57import dev.slne.surf.cloud.api.common.player.CloudPlayer
@@ -8,33 +10,33 @@ import dev.slne.surf.cloud.api.common.player.task.PrePlayerJoinTask
810import dev.slne.surf.cloud.api.common.server.UserList
911import dev.slne.surf.cloud.api.common.server.UserListImpl
1012import dev.slne.surf.cloud.api.common.util.TimeLogger
11- import dev.slne.surf.cloud.api.common.util.mutableObject2ObjectMapOf
12- import dev.slne.surf.cloud.api.common.util.synchronize
13+ import dev.slne.surf.cloud.api.common.util.currentValues
1314import dev.slne.surf.cloud.core.common.spring.CloudLifecycleAware
1415import dev.slne.surf.surfapi.core.api.util.logger
15- import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
16- import kotlinx.coroutines.CompletableDeferred
17- import kotlinx.coroutines.sync.Mutex
18- import kotlinx.coroutines.sync.withLock
1916import org.jetbrains.annotations.MustBeInvokedByOverriders
2017import org.springframework.core.annotation.Order
2118import org.springframework.stereotype.Component
19+ import java.io.Serial
2220import java.net.Inet4Address
2321import java.util.*
2422
2523abstract class CloudPlayerManagerImpl <P : CommonCloudPlayerImpl > : CloudPlayerManager {
2624 private val log = logger()
27- protected val players = mutableObject2ObjectMapOf<UUID , P >().synchronize()
28- protected val creatingPlayers =
29- mutableObject2ObjectMapOf<UUID , CompletableDeferred <P ?>>().synchronize()
30- protected val createMutex = Mutex ()
25+
26+ protected val playerCache = Caffeine .newBuilder()
27+ .asCache<UUID , P >()
28+
29+ // protected val players = mutableObject2ObjectMapOf<UUID, P>().synchronize()
30+ // protected val creatingPlayers =
31+ // mutableObject2ObjectMapOf<UUID, CompletableDeferred<P?>>().synchronize()
32+ // protected val createMutex = Mutex()
3133
3234 override fun getPlayer (uuid : UUID ? ): P ? {
33- return players[ uuid]
35+ return uuid?. let { playerCache.getOrNull(it) }
3436 }
3537
3638 override fun getPlayer (name : String ): CloudPlayer ? =
37- players.values .find { it.name.equals(name, ignoreCase = true ) }
39+ playerCache.currentValues() .find { it.name.equals(name, ignoreCase = true ) }
3840
3941 abstract suspend fun createPlayer (
4042 uuid : UUID ,
@@ -53,18 +55,12 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
5355 abstract fun getProxyServerUid (player : P ): Long?
5456 abstract fun getServerUid (player : P ): Long?
5557
56- private fun addPlayer (player : P ) {
57- players[player.uuid] = player
58- }
59-
6058 override fun getOnlinePlayers (): UserList {
61- return UserListImpl .of(players.values )
59+ return UserListImpl .of(playerCache.currentValues() )
6260 }
6361
6462 protected inline fun forEachPlayer (action : (P ) -> Unit ) {
65- val tempPlayers = Object2ObjectArrayMap (players)
66- tempPlayers.values.forEach(action)
67- tempPlayers.clear()
63+ playerCache.currentValues().forEach(action)
6864 }
6965
7066 /* *
@@ -83,29 +79,58 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
8379 serverUid : Long ,
8480 runPreJoinTasks : Boolean
8581 ): PrePlayerJoinTask .Result {
86- val (player, preJoinResult, created) = getOrCreatePlayerAtomically(
87- uuid,
88- name,
89- proxy,
90- ip,
91- serverUid,
92- runPreJoinTasks
93- )
94-
95- if (player == null ) {
96- return preJoinResult
97- }
98-
99- if (! created) {
82+ val existing = playerCache.getOrNull(uuid)
83+ if (existing != null ) {
10084 if (proxy) {
101- updateProxyServer(player , serverUid)
85+ updateProxyServer(existing , serverUid)
10286 } else {
103- updateServer(player , serverUid)
87+ updateServer(existing , serverUid)
10488 }
105- onServerConnect(uuid, player, serverUid)
89+ onServerConnect(uuid, existing, serverUid)
90+ return PrePlayerJoinTask .Result .ALLOWED
10691 }
10792
108- return PrePlayerJoinTask .Result .ALLOWED
93+ return try {
94+ playerCache.get(uuid) {
95+ val newPlayer = createPlayer(uuid, name, proxy, ip, serverUid)
96+
97+ if (runPreJoinTasks) {
98+ val pre = preJoin(newPlayer)
99+ if (pre !is PrePlayerJoinTask .Result .ALLOWED ) throw PreJoinDenied (pre)
100+ }
101+ onNetworkConnect(uuid, newPlayer)
102+ onServerConnect(uuid, newPlayer, serverUid)
103+ newPlayer
104+ }
105+ PrePlayerJoinTask .Result .ALLOWED
106+ } catch (e: PreJoinDenied ) {
107+ e.result
108+ }
109+
110+
111+ // val (player, preJoinResult, created) = getOrCreatePlayerAtomically(
112+ // uuid,
113+ // name,
114+ // proxy,
115+ // ip,
116+ // serverUid,
117+ // runPreJoinTasks
118+ // )
119+ //
120+ // if (player == null) {
121+ // return preJoinResult
122+ // }
123+ //
124+ // if (!created) {
125+ // if (proxy) {
126+ // updateProxyServer(player, serverUid)
127+ // } else {
128+ // updateServer(player, serverUid)
129+ // }
130+ // onServerConnect(uuid, player, serverUid)
131+ // }
132+ //
133+ // return PrePlayerJoinTask.Result.ALLOWED
109134
110135
111136// val player = players[uuid]
@@ -167,45 +192,45 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
167192// return PrePlayerJoinTask.Result.ALLOWED
168193 }
169194
170- private suspend fun getOrCreatePlayerAtomically (
171- uuid : UUID ,
172- name : String ,
173- proxy : Boolean ,
174- ip : Inet4Address ,
175- serverUid : Long ,
176- runPreJoinTasks : Boolean
177- ): Triple <P ?, PrePlayerJoinTask .Result , Boolean > {
178- players[uuid]?.let { return Triple (it, PrePlayerJoinTask .Result .ALLOWED , false ) }
179-
180- var needsCreation = false
181- val creatingPlayer = createMutex.withLock {
182- creatingPlayers.computeIfAbsent(uuid) {
183- needsCreation = true
184- CompletableDeferred ()
185- }
186- }
187-
188- if (! creatingPlayer.isCompleted && needsCreation) {
189- val newPlayer = createPlayer(uuid, name, proxy, ip, serverUid)
190-
191- if (runPreJoinTasks) {
192- val preJoinResult = preJoin(newPlayer)
193- if (preJoinResult !is PrePlayerJoinTask .Result .ALLOWED ) {
194- creatingPlayer.complete(null )
195- return Triple (null , preJoinResult, true )
196- }
197- }
198-
199- addPlayer(newPlayer)
200- creatingPlayers.remove(uuid)
201-
202- onNetworkConnect(uuid, newPlayer)
203- onServerConnect(uuid, newPlayer, serverUid)
204- creatingPlayer.complete(newPlayer)
205- }
206-
207- return Triple (creatingPlayer.await(), PrePlayerJoinTask .Result .ALLOWED , true )
208- }
195+ // private suspend fun getOrCreatePlayerAtomically(
196+ // uuid: UUID,
197+ // name: String,
198+ // proxy: Boolean,
199+ // ip: Inet4Address,
200+ // serverUid: Long,
201+ // runPreJoinTasks: Boolean
202+ // ): Triple<P?, PrePlayerJoinTask.Result, Boolean> {
203+ // players[uuid]?.let { return Triple(it, PrePlayerJoinTask.Result.ALLOWED, false) }
204+ //
205+ // var needsCreation = false
206+ // val creatingPlayer = createMutex.withLock {
207+ // creatingPlayers.computeIfAbsent(uuid) {
208+ // needsCreation = true
209+ // CompletableDeferred()
210+ // }
211+ // }
212+ //
213+ // if (!creatingPlayer.isCompleted && needsCreation) {
214+ // val newPlayer = createPlayer(uuid, name, proxy, ip, serverUid)
215+ //
216+ // if (runPreJoinTasks) {
217+ // val preJoinResult = preJoin(newPlayer)
218+ // if (preJoinResult !is PrePlayerJoinTask.Result.ALLOWED) {
219+ // creatingPlayer.complete(null)
220+ // return Triple(null, preJoinResult, true)
221+ // }
222+ // }
223+ //
224+ // addPlayer(newPlayer)
225+ // creatingPlayers.remove(uuid)
226+ //
227+ // onNetworkConnect(uuid, newPlayer)
228+ // onServerConnect(uuid, newPlayer, serverUid)
229+ // creatingPlayer.complete(newPlayer)
230+ // }
231+ //
232+ // return Triple(creatingPlayer.await(), PrePlayerJoinTask.Result.ALLOWED, true)
233+ // }
209234
210235 protected open suspend fun preJoin (player : P ): PrePlayerJoinTask .Result {
211236 return PrePlayerJoinTask .Result .ALLOWED
@@ -220,20 +245,25 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
220245 * @param proxy A boolean indicating if the player was connected through a proxy.
221246 */
222247 suspend fun updateOrRemoveOnDisconnect (uuid : UUID , serverUid : Long , proxy : Boolean ) {
223- val player = players[uuid] ? : return
224- val oldProxy = getProxyServerUid(player)
225- val oldServer = getServerUid(player)
248+ val player = playerCache.getIfPresent(uuid)
249+ if (player != null ) {
250+ val oldProxy = getProxyServerUid(player)
251+ val oldServer = getServerUid(player)
252+ if (proxy) {
253+ removeProxyServer(player, serverUid)
254+ } else {
255+ removeServer(player, serverUid)
256+ }
257+ onServerDisconnect(uuid, player, serverUid)
226258
227- if (proxy) {
228- removeProxyServer(player, serverUid)
259+ if (! player.connected) {
260+ playerCache.invalidate(uuid)
261+ onNetworkDisconnect(uuid, player, oldProxy, oldServer)
262+ }
229263 } else {
230- removeServer(player, serverUid)
231- }
232- onServerDisconnect(uuid, player, serverUid)
233-
234- if (! player.connected) {
235- players.remove(uuid)
236- onNetworkDisconnect(uuid, player, oldProxy, oldServer)
264+ log.atWarning()
265+ .log(" Player with UUID $uuid not found during disconnect handling" )
266+ playerCache.invalidate(uuid)
237267 }
238268 }
239269
@@ -268,6 +298,14 @@ abstract class CloudPlayerManagerImpl<P : CommonCloudPlayerImpl> : CloudPlayerMa
268298 }
269299
270300 open fun terminate () {}
301+
302+
303+ private class PreJoinDenied (val result : PrePlayerJoinTask .Result ) : RuntimeException() {
304+ companion object {
305+ @Serial
306+ private const val serialVersionUID: Long = - 5043277924406776272L
307+ }
308+ }
271309}
272310
273311val playerManagerImpl get() = CloudPlayerManager .instance as CloudPlayerManagerImpl <out CommonCloudPlayerImpl >
0 commit comments