343 lines
14 KiB
Kotlin
343 lines
14 KiB
Kotlin
package rs09.worker
|
|
|
|
import api.sendMessage
|
|
import com.google.protobuf.Message
|
|
import core.game.node.entity.player.Player
|
|
import core.game.system.communication.ClanEntry
|
|
import core.game.system.communication.ClanRank
|
|
import core.game.system.communication.ClanRepository
|
|
import core.game.system.communication.CommunicationInfo
|
|
import core.net.packet.PacketRepository
|
|
import core.net.packet.context.ContactContext
|
|
import core.net.packet.context.MessageContext
|
|
import core.net.packet.out.CommunicationMessage
|
|
import core.net.packet.out.ContactPackets
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.GlobalScope
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.withContext
|
|
import proto.management.ClanJoinNotification
|
|
import proto.management.ClanLeaveNotification
|
|
import proto.management.ClanMessage
|
|
import proto.management.FriendUpdate
|
|
import proto.management.JoinClanRequest
|
|
import proto.management.LeaveClanRequest
|
|
import proto.management.PlayerStatusUpdate
|
|
import proto.management.PrivateMessage
|
|
import proto.management.RequestClanInfo
|
|
import proto.management.RequestContactInfo
|
|
import proto.management.SendClanInfo
|
|
import proto.management.SendClanInfo.ClanMember
|
|
import proto.management.SendContactInfo
|
|
import proto.management.SendContactInfo.Contact
|
|
import rs09.game.system.SystemLogger
|
|
import rs09.game.world.GameWorld
|
|
import rs09.game.world.repository.Repository
|
|
import java.util.Deque
|
|
import java.util.LinkedList
|
|
import java.util.concurrent.BlockingDeque
|
|
import java.util.concurrent.LinkedBlockingDeque
|
|
import kotlin.reflect.jvm.internal.impl.descriptors.Visibilities.Private
|
|
|
|
/**
|
|
* Processes management-related events e.g clan messages, etc.
|
|
*/
|
|
object ManagementEvents {
|
|
private var isRunning: Boolean = true
|
|
private val eventQueue: BlockingDeque<Message> = LinkedBlockingDeque()
|
|
private val waitingOnClanInfo = HashMap<String, Deque<Message>>()
|
|
private val hasRequestedClanInfo = HashMap<String, Boolean>()
|
|
|
|
val job = GlobalScope.launch {
|
|
while (isRunning) {
|
|
val event = withContext(Dispatchers.IO) { eventQueue.take() }
|
|
try {
|
|
handleEvent(event)
|
|
handleLoggingFor(event)
|
|
} catch (ignored: Exception) {}
|
|
}
|
|
}
|
|
|
|
private fun handleLoggingFor(event: Message) {
|
|
when (event) {
|
|
is PlayerStatusUpdate -> SystemLogger.logMS("${event.username} -(WLD)> ${event.world}")
|
|
is RequestContactInfo -> SystemLogger.logMS("${event.username} -> RQ CONTACT INFO")
|
|
is SendContactInfo -> SystemLogger.logMS("${event.username} <- SND CONTACT INFO")
|
|
is PrivateMessage -> SystemLogger.logMS("[PM] ${event.sender}->${event.receiver}: ${event.message}")
|
|
is ClanMessage -> SystemLogger.logMS("[CM:${event.clanName}] ${event.sender}: ${event.message}")
|
|
is JoinClanRequest -> SystemLogger.logMS("${event.username} +CL ${event.clanName}")
|
|
is LeaveClanRequest -> SystemLogger.logMS("${event.username} -CL ${event.clanName}")
|
|
is RequestClanInfo -> SystemLogger.logMS("REQUEST CLAN INFO: ${event.clanOwner}")
|
|
is SendClanInfo -> SystemLogger.logMS("RECEIVE CLAN INFO: ${event.clanOwner}->${event.clanName}")
|
|
is ClanJoinNotification -> SystemLogger.logMS("${event.username} JOINED CLAN ${event.clanName}")
|
|
is ClanLeaveNotification -> SystemLogger.logMS("${event.username} LEFT CLAN ${event.clanName}")
|
|
}
|
|
}
|
|
|
|
@JvmStatic fun publish(event: Message) {
|
|
eventQueue.offer(event)
|
|
}
|
|
|
|
private fun handleEvent(event: Message) {
|
|
when (event) {
|
|
|
|
is PlayerStatusUpdate -> {
|
|
val notifiablePlayers = if (event.notifyFriendsOnly) {
|
|
GameWorld.accountStorage.getOnlineFriends(event.username)
|
|
} else {
|
|
Repository.playerNames.keys.toList()
|
|
}.filter { Repository.getPlayerByName(it)?.communication?.contacts?.containsKey(event.username) == true }
|
|
|
|
for (playerName in notifiablePlayers) {
|
|
val p = Repository.getPlayerByName(playerName) ?: continue
|
|
p.communication.contacts[event.username]?.worldId = event.world
|
|
PacketRepository.send(ContactPackets::class.java, ContactContext(p, event.username, event.world))
|
|
}
|
|
}
|
|
|
|
is RequestContactInfo -> {
|
|
val response = SendContactInfo.newBuilder()
|
|
response.username = event.username
|
|
val info = GameWorld.accountStorage.getAccountInfo(event.username)
|
|
val contacts = CommunicationInfo.parseContacts(info.contacts)
|
|
|
|
for ((username, contact) in contacts) {
|
|
val online = Repository.getPlayerByName(username) != null
|
|
val cbuild = Contact.newBuilder()
|
|
cbuild.username = username
|
|
cbuild.world = if (online) GameWorld.settings!!.worldId else 0
|
|
cbuild.rank = contact.rank.ordinal
|
|
response.addContacts(cbuild)
|
|
}
|
|
|
|
val blocked = info.blocked.split(",")
|
|
for (user in blocked) response.addBlocked(user)
|
|
|
|
publish(response.build())
|
|
}
|
|
|
|
is SendContactInfo -> {
|
|
val p = Repository.getPlayerByName(event.username) ?: return
|
|
|
|
PacketRepository.send(
|
|
ContactPackets::class.java,
|
|
ContactContext(p, ContactContext.UPDATE_STATE_TYPE)
|
|
)
|
|
|
|
p.communication.contacts.clear()
|
|
p.communication.blocked.clear()
|
|
|
|
for (contact in event.contactsList) {
|
|
val c = core.game.system.communication.Contact(contact.username)
|
|
p.communication.contacts[contact.username] = c
|
|
c.worldId = contact.world
|
|
c.rank = ClanRank.values()[contact.rank]
|
|
PacketRepository.send(
|
|
ContactPackets::class.java,
|
|
ContactContext(p, contact.username, contact.world)
|
|
)
|
|
}
|
|
|
|
for (blocked in event.blockedList) {
|
|
p.communication.blocked.add(blocked)
|
|
}
|
|
|
|
PacketRepository.send(
|
|
ContactPackets::class.java,
|
|
ContactContext(p, ContactContext.IGNORE_LIST_TYPE)
|
|
)
|
|
|
|
}
|
|
|
|
is FriendUpdate -> {
|
|
val remove = event.type == FriendUpdate.Type.REMOVE
|
|
val f = Repository.getPlayerByName(event.friend)
|
|
val p = Repository.getPlayerByName(event.username)
|
|
val world = if (f != null) GameWorld.settings!!.worldId else 0
|
|
}
|
|
|
|
is PrivateMessage -> {
|
|
val sender = Repository.getPlayerByName(event.sender)
|
|
val receiver = Repository.getPlayerByName(event.receiver)
|
|
|
|
if (sender != null) {
|
|
PacketRepository.send(
|
|
CommunicationMessage::class.java,
|
|
MessageContext(sender, event.receiver, event.rank, MessageContext.SEND_MESSAGE, event.message)
|
|
)
|
|
}
|
|
|
|
if (receiver != null) {
|
|
PacketRepository.send(
|
|
CommunicationMessage::class.java,
|
|
MessageContext(receiver, event.sender, event.rank, MessageContext.RECIEVE_MESSAGE, event.message)
|
|
)
|
|
}
|
|
}
|
|
|
|
is JoinClanRequest -> {
|
|
val p = Repository.getPlayerByName(event.username) ?: return
|
|
|
|
if (shouldWaitForClanInfo(event.clanName)) {
|
|
queueUntilClanInfo(event.clanName, event)
|
|
return
|
|
}
|
|
|
|
val clan = ClanRepository.get(event.clanName)
|
|
|
|
if (clan == null) {
|
|
sendMessage(p, "The channel you tried to join does not exist.:clan:")
|
|
} else {
|
|
clan.enter(p)
|
|
p.communication.clan = clan
|
|
}
|
|
}
|
|
|
|
is ClanJoinNotification -> {
|
|
if (event.world == GameWorld.settings!!.worldId) return
|
|
|
|
if (shouldWaitForClanInfo(event.clanName)) {
|
|
queueUntilClanInfo(event.clanName, event)
|
|
return
|
|
}
|
|
|
|
val clan = ClanRepository.get(event.clanName)
|
|
val entry = ClanEntry(event.username, event.world)
|
|
clan.players.add(entry)
|
|
clan.update()
|
|
}
|
|
|
|
is LeaveClanRequest -> {
|
|
val p = Repository.getPlayerByName(event.username) ?: return
|
|
|
|
if (shouldWaitForClanInfo(event.clanName)) {
|
|
queueUntilClanInfo(event.clanName, event)
|
|
return
|
|
}
|
|
|
|
val clan = ClanRepository.get(event.clanName)
|
|
|
|
if (clan == null) {
|
|
sendMessage(p, "Error leaving clan. Please relog.")
|
|
} else {
|
|
clan.leave(p, true)
|
|
}
|
|
}
|
|
|
|
is ClanLeaveNotification -> {
|
|
if (shouldWaitForClanInfo(event.clanName)) {
|
|
queueUntilClanInfo(event.clanName, event)
|
|
return
|
|
}
|
|
|
|
val clan = ClanRepository.get(event.clanName)
|
|
val entry = clan.players.firstOrNull { it.name.equals(event.username) } ?: return
|
|
clan.players.remove(entry)
|
|
clan.update()
|
|
}
|
|
|
|
is RequestClanInfo -> {
|
|
val clan = ClanRepository.get(event.clanOwner)
|
|
val response = SendClanInfo.newBuilder()
|
|
|
|
if (clan == null) {
|
|
response.hasInfo = false
|
|
response.clanOwner = event.clanOwner
|
|
} else {
|
|
response.hasInfo = true
|
|
response.clanName = clan.name
|
|
response.clanOwner = event.clanOwner
|
|
|
|
for (member in clan.players) {
|
|
val cmBuilder = ClanMember.newBuilder()
|
|
cmBuilder.username = member.name
|
|
cmBuilder.world = member.worldId
|
|
cmBuilder.rank = (clan.ranks[member.name] ?: ClanRank.NONE).ordinal
|
|
response.addMembers(cmBuilder)
|
|
}
|
|
}
|
|
|
|
publish(response.build())
|
|
}
|
|
|
|
is SendClanInfo -> {
|
|
if (event.hasInfo) {
|
|
val clan = ClanRepository.getClans().getOrPut(event.clanOwner) { ClanRepository(event.clanOwner) }
|
|
clan.name = event.clanName
|
|
clan.joinRequirement = ClanRank.values()[event.joinRequirement]
|
|
clan.kickRequirement = ClanRank.values()[event.kickRequirement]
|
|
clan.messageRequirement = ClanRank.values()[event.messageRequirement]
|
|
clan.lootRequirement = ClanRank.values()[event.lootRequirement]
|
|
|
|
for (member in event.membersList) {
|
|
val entry = ClanEntry(member.username, member.world)
|
|
clan.ranks[member.username] = ClanRank.values()[member.rank]
|
|
if (member.world == GameWorld.settings!!.worldId) {
|
|
val p = Repository.getPlayerByName(member.username)
|
|
entry.player = p
|
|
p?.communication?.clan = clan
|
|
}
|
|
clan.players.add(entry)
|
|
}
|
|
|
|
clan.update()
|
|
} else {
|
|
val info = GameWorld.accountStorage.getAccountInfo(event.clanOwner)
|
|
if (info.clanName.isNotEmpty()) {
|
|
val reqs = CommunicationInfo.parseClanRequirements(info.clanReqs)
|
|
val c = ClanRepository(event.clanOwner)
|
|
val contacts = CommunicationInfo.parseContacts(info.contacts)
|
|
c.name = info.clanName
|
|
c.joinRequirement = reqs[0]
|
|
c.messageRequirement = reqs[1]
|
|
c.kickRequirement = reqs[2]
|
|
c.lootRequirement = reqs[3]
|
|
for ((username, contact) in contacts) {
|
|
c.ranks[username] = contact.rank
|
|
}
|
|
ClanRepository.getClans()[event.clanOwner] = c
|
|
}
|
|
}
|
|
|
|
val queuedEvents = waitingOnClanInfo[event.clanOwner] ?: return
|
|
while (queuedEvents.peek() != null) {
|
|
publish(queuedEvents.pop())
|
|
}
|
|
}
|
|
|
|
is ClanMessage -> {
|
|
if (shouldWaitForClanInfo(event.clanName)) {
|
|
queueUntilClanInfo(event.clanName, event)
|
|
return
|
|
}
|
|
|
|
val clan = ClanRepository.get(event.clanName)
|
|
|
|
for (member in clan.players.filter { it.player != null }) {
|
|
PacketRepository.send(
|
|
CommunicationMessage::class.java,
|
|
MessageContext(member.player, event.sender, event.rank, MessageContext.CLAN_MESSAGE, event.message)
|
|
)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
private fun queueUntilClanInfo(clanName: String, message: Message) {
|
|
val queue = waitingOnClanInfo.getOrPut(clanName) {LinkedList()}
|
|
queue.offer(message)
|
|
|
|
if (hasRequestedClanInfo[clanName] == null) {
|
|
val request = RequestClanInfo.newBuilder()
|
|
request.clanOwner = clanName
|
|
request.world = GameWorld.settings!!.worldId
|
|
publish(request.build())
|
|
hasRequestedClanInfo[clanName] = true
|
|
}
|
|
}
|
|
|
|
private fun shouldWaitForClanInfo(clanName: String): Boolean {
|
|
return ClanRepository.get(clanName) == null && hasRequestedClanInfo[clanName] == null
|
|
}
|
|
} |