Removed independent management server
World server now handles its own management-server-related functionality, a new management server will be written in the future to facilitate cross-world event syncing World server now runs standalone, this requires the client to be configured to connect the world server port for the world list (world list was previously supplied by the management server on port 5555, which should be swapped to the world server port that is generally 43595 for world 1) work is in progress to update client launchers to make this as seamless as possible This should resolve issues where players sometimes get a login error claiming they are already logged in
This commit is contained in:
@@ -78,7 +78,7 @@ object Server {
|
||||
SystemLogger.logErr("Port " + (43594 + GameWorld.settings?.worldId!!) + " is already in use!")
|
||||
throw e
|
||||
}
|
||||
WorldCommunicator.connect()
|
||||
//WorldCommunicator.connect()
|
||||
SystemLogger.logInfo(GameWorld.settings?.name + " flags " + GameWorld.settings?.toString())
|
||||
SystemLogger.logInfo(GameWorld.settings?.name + " started in " + t.duration(false, "") + " milliseconds.")
|
||||
val scanner = Scanner(System.`in`)
|
||||
|
||||
@@ -132,6 +132,7 @@ class WorkForInteractionListener : InteractionListener, LoginListener {
|
||||
if(entity !is Player) return
|
||||
|
||||
val job = getAttribute(entity, "jobs:id", -1)
|
||||
if (job !in 0 until SlayingJob.values().size) return
|
||||
val ids = SlayingJob.values()[job].ids
|
||||
|
||||
if(event.npc.id in ids){
|
||||
|
||||
@@ -170,8 +170,6 @@ class TutorialMagicTutorDialogue(player: Player? = null) : DialoguePlugin(player
|
||||
"at any time, look for a signpost or use the Lumbridge Home Port Spell."
|
||||
)
|
||||
stage = 12
|
||||
if(WorldCommunicator.isEnabled())
|
||||
MSPacketRepository.sendInfoUpdate(player)
|
||||
TutorialStage.removeHintIcon(player)
|
||||
|
||||
player.unhook(TutorialKillReceiver)
|
||||
|
||||
@@ -16,7 +16,7 @@ interface InteractionListener : ContentInterface{
|
||||
val GROUNDITEM: Int
|
||||
get() = 3
|
||||
|
||||
fun on(id: Int, type: Int, vararg option: String,handler: (player: Player, node: Node) -> Boolean){
|
||||
fun on(id: Int, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
||||
InteractionListeners.add(id,type,option,handler)
|
||||
}
|
||||
fun on(ids: IntArray, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
|
||||
|
||||
@@ -10,12 +10,13 @@ import core.game.node.entity.skill.Skills
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.update.flag.context.ChatMessage
|
||||
import core.game.world.update.flag.player.ChatFlag
|
||||
import core.net.amsc.MSPacketRepository
|
||||
import core.net.amsc.WorldCommunicator
|
||||
import proto.management.ClanMessage
|
||||
import rs09.game.node.entity.skill.slayer.SlayerManager
|
||||
import rs09.game.world.GameWorld.Pulser
|
||||
import rs09.net.packet.`in`.QCPacketType
|
||||
import rs09.worker.ManagementEvents.publish
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
@@ -41,11 +42,11 @@ object QCRepository {
|
||||
|
||||
|
||||
if(forClan) {
|
||||
if (WorldCommunicator.isEnabled()) {
|
||||
MSPacketRepository.sendClanMessage(player, qcString)
|
||||
} else {
|
||||
player?.communication?.clan?.message(player, qcString)
|
||||
}
|
||||
val builder = ClanMessage.newBuilder()
|
||||
builder.sender = player!!.name
|
||||
builder.clanName = player.communication.clan.owner.lowercase(Locale.getDefault()).replace(" ", "_")
|
||||
builder.message = qcString
|
||||
publish(builder.build())
|
||||
} else {
|
||||
val ctx = ChatMessage(player, qcString, 0, qcString.length)
|
||||
ctx.isQuickChat = true
|
||||
|
||||
@@ -55,7 +55,6 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
Repository.LOGGED_IN_PLAYERS.remove(player.username)
|
||||
Repository.lobbyPlayers.remove(player)
|
||||
Repository.playerNames.remove(player.name)
|
||||
MSPacketRepository.sendPlayerRemoval(player.name)
|
||||
flag(AuthResponse.ErrorLoadingProfile)
|
||||
}
|
||||
//Repository.getPlayerNames().put(player.getName(), player);
|
||||
@@ -63,16 +62,6 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
override fun pulse(): Boolean {
|
||||
try {
|
||||
if (details.session.isActive) {
|
||||
val p = Repository.getPlayerByName(player.name)
|
||||
if (p != null) {
|
||||
p.clear()
|
||||
Repository.playerNames.remove(p.name)
|
||||
Repository.lobbyPlayers.remove(p)
|
||||
Repository.removePlayer(p)
|
||||
}
|
||||
if (!Repository.players.contains(player)) {
|
||||
Repository.addPlayer(player)
|
||||
}
|
||||
loginListeners.forEach(Consumer { listener: LoginListener -> listener.login(player) }) //Run our login hooks
|
||||
parser.runContentHooks() //Run our saved-content-parsing hooks
|
||||
player.details.session.setObject(player)
|
||||
@@ -83,7 +72,6 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
player.monitor.log(player.details.macAddress, PlayerMonitor.ADDRESS_LOG)
|
||||
} else {
|
||||
Repository.playerNames.remove(player.name)
|
||||
MSPacketRepository.sendPlayerRemoval(player.name)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
@@ -91,7 +79,6 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
Repository.LOGGED_IN_PLAYERS.remove(player.username)
|
||||
Repository.lobbyPlayers.remove(player)
|
||||
Repository.playerNames.remove(player.name)
|
||||
MSPacketRepository.sendPlayerRemoval(player.name)
|
||||
flag(AuthResponse.ErrorLoadingProfile)
|
||||
}
|
||||
return true
|
||||
@@ -112,15 +99,9 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
player.updateSceneGraph(true)
|
||||
player.configManager.init()
|
||||
LoginConfiguration.configureGameWorld(player)
|
||||
Repository.playerNames[player.name] = player
|
||||
GameWorld.Pulser.submit(object : Pulse(1) {
|
||||
override fun pulse(): Boolean {
|
||||
if (!Repository.players.contains(player)) {
|
||||
Repository.addPlayer(player)
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (!Repository.players.contains(player)) {
|
||||
Repository.addPlayer(player)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,18 +109,12 @@ class LoginParser(val details: PlayerDetails, private val type: LoginType) {
|
||||
* @return `True` if the request is valid.
|
||||
*/
|
||||
private fun validateRequest(): Boolean {
|
||||
if (WorldCommunicator.getState() == ManagementServerState.CONNECTING) {
|
||||
return flag(AuthResponse.LoginServerOffline)
|
||||
}
|
||||
if (!details.session.isActive) {
|
||||
return false
|
||||
}
|
||||
if (SystemManager.isUpdating()) {
|
||||
return flag(AuthResponse.Updating)
|
||||
}
|
||||
if (Repository.getPlayerByName(details.username).also { gamePlayer = it } != null && gamePlayer!!.session.isActive) {
|
||||
return flag(AuthResponse.AlreadyOnline)
|
||||
}
|
||||
return if (details.isBanned) {
|
||||
flag(AuthResponse.AccountDisabled)
|
||||
} else true
|
||||
|
||||
@@ -114,4 +114,8 @@ object SystemLogger {
|
||||
{
|
||||
SystemLogger.logInfo("[SHUTDOWN] $message")
|
||||
}
|
||||
|
||||
fun logMS(s: String) {
|
||||
if(s.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[ MS] $s")}")
|
||||
}
|
||||
}
|
||||
@@ -135,12 +135,14 @@ object Repository {
|
||||
fun addPlayer(player: Player){
|
||||
players.add(player)
|
||||
uid_map[player.details.usernameHashcode] = player
|
||||
playerNames[player.name] = player
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removePlayer(player: Player){
|
||||
players.remove(player)
|
||||
uid_map.remove(player.details.usernameHashcode)
|
||||
playerNames.remove(player.name)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import core.game.system.communication.CommunicationInfo
|
||||
import core.net.IoReadEvent
|
||||
import core.net.IoSession
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.net.packet.`in`.Login
|
||||
import java.nio.ByteBuffer
|
||||
@@ -17,25 +18,21 @@ import java.nio.ByteBuffer
|
||||
class LoginReadEvent(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) {
|
||||
override fun read(session: IoSession, buffer: ByteBuffer) {
|
||||
val (response, info) = Login.decodeFromBuffer(buffer)
|
||||
|
||||
if(response != AuthResponse.Success || info == null) {
|
||||
session.write(response)
|
||||
return
|
||||
}
|
||||
|
||||
val (authResponse, accountInfo) = GameWorld.authenticator.checkLogin(info.username, info.password)
|
||||
|
||||
if(authResponse != AuthResponse.Success || accountInfo == null) {
|
||||
session.write(authResponse)
|
||||
return
|
||||
}
|
||||
|
||||
val details = PlayerDetails(info.username)
|
||||
details.accountInfo = accountInfo
|
||||
details.communication.parse(accountInfo)
|
||||
session.clientInfo = ClientInfo(info.displayMode, info.windowMode, info.screenWidth, info.screenHeight)
|
||||
session.isaacPair = info.isaacPair
|
||||
|
||||
Login.proceedWith(session, details, info.opcode)
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,24 @@ package rs09.net.packet.`in`
|
||||
import core.cache.crypto.ISAACCipher
|
||||
import core.cache.crypto.ISAACPair
|
||||
import core.cache.misc.buffer.ByteBufferUtils
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.PlayerDetails
|
||||
import core.game.node.entity.player.info.UIDInfo
|
||||
import core.game.node.entity.player.info.login.LoginType
|
||||
import core.net.Constants
|
||||
import core.net.IoSession
|
||||
import core.net.amsc.WorldCommunicator
|
||||
import core.tools.StringUtils
|
||||
import proto.management.JoinClanRequest
|
||||
import proto.management.PlayerStatusUpdate
|
||||
import proto.management.RequestContactInfo
|
||||
import rs09.ServerConstants
|
||||
import rs09.auth.AuthResponse
|
||||
import rs09.game.node.entity.player.info.login.LoginParser
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.worker.ManagementEvents
|
||||
import rs09.worker.ManagementEvents.publish
|
||||
import java.math.BigInteger
|
||||
import java.nio.BufferUnderflowException
|
||||
import java.nio.ByteBuffer
|
||||
@@ -68,6 +74,11 @@ object Login {
|
||||
info.isaacPair = produceISAACPairFrom(decryptedBuffer)
|
||||
info.username = StringUtils.longToString(decryptedBuffer.long)
|
||||
info.password = ByteBufferUtils.getString(decryptedBuffer)
|
||||
|
||||
if (Repository.getPlayerByName(info.username) != null) {
|
||||
return Pair(AuthResponse.AlreadyOnline, info)
|
||||
}
|
||||
|
||||
return Pair(AuthResponse.Success, info)
|
||||
} catch (e: Exception) {
|
||||
SystemLogger.logErr("Exception encountered during login packet parsing! See stack trace below.")
|
||||
@@ -109,6 +120,31 @@ object Login {
|
||||
Repository.LOGGED_IN_PLAYERS.add(details.username)
|
||||
details.session = session
|
||||
details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED"))
|
||||
WorldCommunicator.register(LoginParser(details, LoginType.fromType(opcode)))
|
||||
val player = Player(details)
|
||||
if (!Repository.players.contains(player)) {
|
||||
Repository.addPlayer(player)
|
||||
}
|
||||
LoginParser(details, LoginType.fromType(opcode)).initialize(player, opcode == RECONNECT_LOGIN_OP)
|
||||
sendMSEvents(details)
|
||||
}
|
||||
|
||||
private fun sendMSEvents(details: PlayerDetails) {
|
||||
val statusEvent = PlayerStatusUpdate.newBuilder()
|
||||
statusEvent.username = details.username
|
||||
statusEvent.world = GameWorld.settings!!.worldId
|
||||
statusEvent.notifyFriendsOnly = false
|
||||
publish(statusEvent.build())
|
||||
|
||||
val contactEvent = RequestContactInfo.newBuilder()
|
||||
contactEvent.username = details.username
|
||||
contactEvent.world = GameWorld.settings!!.worldId
|
||||
publish(contactEvent.build())
|
||||
|
||||
if (!details.communication.currentClan.isNullOrEmpty() && details.communication.clan == null) {
|
||||
val clanEvent = JoinClanRequest.newBuilder()
|
||||
clanEvent.username = details.username
|
||||
clanEvent.clanName = details.communication.currentClan
|
||||
publish(clanEvent.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ interface AccountStorageProvider {
|
||||
fun store(info: UserAccountInfo)
|
||||
fun update(info: UserAccountInfo)
|
||||
fun remove(info: UserAccountInfo)
|
||||
fun getOnlineFriends(username: String) : List<String>
|
||||
}
|
||||
|
||||
@@ -24,4 +24,8 @@ class InMemoryStorageProvider : AccountStorageProvider {
|
||||
override fun remove(info: UserAccountInfo) {
|
||||
storage.remove(info.username)
|
||||
}
|
||||
|
||||
override fun getOnlineFriends(username: String): List<String> {
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package rs09.storage
|
||||
|
||||
import core.game.system.communication.CommunicationInfo
|
||||
import rs09.auth.UserAccountInfo
|
||||
import rs09.game.system.SystemLogger
|
||||
import rs09.game.world.repository.Repository
|
||||
import java.lang.Long.max
|
||||
import java.sql.*
|
||||
|
||||
@@ -155,6 +157,24 @@ class SQLStorageProvider : AccountStorageProvider {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOnlineFriends(username: String): List<String> {
|
||||
val friends = ArrayList<String>()
|
||||
var fTokens = ""
|
||||
val conn = getConnection()
|
||||
conn.use {
|
||||
val friendsQuery = it.prepareStatement(GET_ALL_FRIENDS_QUERY)
|
||||
friendsQuery.setString(1, username)
|
||||
val f = friendsQuery.executeQuery()
|
||||
if (f.next()) {
|
||||
fTokens = f.getString(1)
|
||||
}
|
||||
}
|
||||
val contacts = CommunicationInfo.parseContacts(fTokens)
|
||||
for ((friendName, _) in contacts) if (Repository.getPlayerByName(friendName) != null) friends.add(friendName)
|
||||
|
||||
return friends
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val usernameQuery = "SELECT username FROM members WHERE username = ?;"
|
||||
private const val removeInfoQuery = "DELETE FROM members WHERE username = ?;"
|
||||
@@ -227,5 +247,7 @@ class SQLStorageProvider : AccountStorageProvider {
|
||||
15 to "lastLogin",
|
||||
16 to "online"
|
||||
)
|
||||
|
||||
private val GET_ALL_FRIENDS_QUERY = "SELECT contacts FROM players WHERE username = ?;"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
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 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user