package lila.ws import akka.actor.typed.Scheduler import java.util.concurrent.ConcurrentHashMap import scala.concurrent.duration._ import scala.concurrent.ExecutionContext import scala.jdk.CollectionConverters._ import ipc._ final class Users(implicit scheduler: Scheduler, ec: ExecutionContext) { private val users = new ConcurrentHashMap[User.ID, Set[Client]](32768) private val disconnects = ConcurrentHashMap.newKeySet[User.ID](2048) private def publish(msg: Any) = Bus.internal.publish("users", msg) scheduler.scheduleWithFixedDelay(7.seconds, 5.seconds) { () => publish(LilaIn.DisconnectUsers(disconnects.iterator.asScala.toSet)) disconnects.clear() } def connect(user: User, client: Client, silently: Boolean = false): Unit = users.compute( user.id, { case (_, null) => if (!disconnects.remove(user.id)) publish(LilaIn.ConnectUser(user, silently)) Set(client) case (_, clients) => clients + client } ) def disconnect(user: User, client: Client): Unit = users.computeIfPresent( user.id, (_, clients) => { val newClients = clients - client if (newClients.isEmpty) { disconnects add user.id null } else newClients } ) def tellOne(userId: User.ID, payload: ClientMsg): Unit = Option(users get userId) foreach { _ foreach { _ ! payload } } def tellMany(userIds: Iterable[User.ID], payload: ClientMsg): Unit = userIds foreach { tellOne(_, payload) } def kick(userId: User.ID): Unit = Option(users get userId) foreach { _ foreach { _ ! ClientCtrl.Disconnect } } def setTroll(userId: User.ID, v: IsTroll): Unit = Option(users get userId) foreach { _ foreach { _ ! ipc.SetTroll(v) } } def isOnline(userId: User.ID): Boolean = users containsKey userId def size = users.size }