mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
outfit checkpoint
This commit is contained in:
parent
57b3fd69ab
commit
402259e338
|
|
@ -12,7 +12,7 @@ import net.psforever.objects.serverobject.mount.Mountable
|
|||
import net.psforever.objects.serverobject.terminals.{ProximityUnit, Terminal}
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
|
||||
import net.psforever.packet.game.{AIDamage, ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarGrenadeStateMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BeginZoningMessage, BindPlayerMessage, BugReportMessage, ChangeAmmoMessage, ChangeFireModeMessage, ChangeFireStateMessage_Start, ChangeFireStateMessage_Stop, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, ChildObjectStateMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DeployRequestMessage, DismountVehicleCargoMsg, DismountVehicleMsg, DisplayedAwardMessage, DropItemMessage, DroppodLaunchRequestMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FavoritesRequest, FrameVehicleStateMessage, FriendsRequest, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, HitMessage, InvalidTerrainMessage, ItemTransactionMessage, LashMessage, LongRangeProjectileInfoMessage, LootItemMessage, MountVehicleCargoMsg, MountVehicleMsg, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, ProjectileStateMessage, ProximityTerminalUseMessage, ReleaseAvatarRequestMessage, ReloadMessage, RequestDestroyMessage, SetChatFilterMessage, SpawnRequestMessage, SplashHitMessage, SquadDefinitionActionMessage, SquadMembershipRequest, SquadWaypointRequest, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UplinkRequest, UseItemMessage, VehicleStateMessage, VehicleSubStateMessage, VoiceHostInfo, VoiceHostRequest, WarpgateRequest, WeaponDelayFireMessage, WeaponDryFireMessage, WeaponFireMessage, WeaponLazeTargetPositionMessage, ZipLineMessage}
|
||||
import net.psforever.services.{InterstellarClusterService => ICS}
|
||||
import net.psforever.services.CavernRotationService
|
||||
import net.psforever.services.CavernRotationService.SendCavernRotationUpdates
|
||||
|
|
@ -610,7 +610,14 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con
|
|||
case packet: HitHint =>
|
||||
logic.general.handleHitHint(packet)
|
||||
|
||||
case _: OutfitRequest => ()
|
||||
case packet: OutfitRequest =>
|
||||
logic.general.handleOutfitRequest(packet)
|
||||
|
||||
case packet: OutfitMembershipRequest =>
|
||||
logic.general.handleOutfitMembershipRequest(packet)
|
||||
|
||||
case packet: OutfitMembershipResponse =>
|
||||
logic.general.handleOutfitMembershipResponse(packet)
|
||||
|
||||
case pkt =>
|
||||
data.log.warn(s"Unhandled GamePacket $pkt")
|
||||
|
|
|
|||
|
|
@ -30,11 +30,13 @@ import net.psforever.objects.vehicles.Utility
|
|||
import net.psforever.objects.vital.Vitality
|
||||
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Unk0, Unk1}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitEvent, OutfitMemberEvent, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, OutfitRequestAction, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.RemoverActor
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.Success
|
||||
|
||||
|
|
@ -665,6 +667,19 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
val HitHint(_, _) = pkt
|
||||
}
|
||||
|
||||
def handleOutfitMembershipRequest(pkt: OutfitMembershipRequest): Unit = {}
|
||||
|
||||
def handleOutfitMembershipResponse(pkt: OutfitMembershipResponse): Unit = {}
|
||||
|
||||
def handleOutfitRequest(pkt: OutfitRequest): Unit = {
|
||||
pkt match {
|
||||
case OutfitRequest(_, OutfitRequestAction.Unk3(true)) =>
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Unk3(false)) =>
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
/* messages */
|
||||
|
||||
def handleRenewCharSavedTimer(): Unit = { /* */ }
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import net.psforever.actors.session.spectator.SpectatorMode
|
|||
import net.psforever.actors.session.support.{ChatFunctions, ChatOperations, SessionData}
|
||||
import net.psforever.objects.Session
|
||||
import net.psforever.packet.game.{ChatMsg, ServerType, SetChatFilterMessage}
|
||||
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
|
||||
import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel}
|
||||
import net.psforever.types.ChatMessageType
|
||||
import net.psforever.types.ChatMessageType.{CMT_TOGGLESPECTATORMODE, CMT_TOGGLE_GM}
|
||||
import net.psforever.util.Config
|
||||
|
|
@ -79,6 +79,9 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
case (CMT_SQUAD, _, _) =>
|
||||
ops.commandSquad(session, message, SquadChannel(sessionLogic.squad.squad_guid))
|
||||
|
||||
case (CMT_OUTFIT, _, _) =>
|
||||
ops.commandOutfit(session, message, OutfitChannel(sessionLogic.player.outfit_id))
|
||||
|
||||
case (CMT_WHO | CMT_WHO_CSR | CMT_WHO_CR | CMT_WHO_PLATOONLEADERS | CMT_WHO_SQUADLEADERS | CMT_WHO_TEAMS, _, _) =>
|
||||
ops.commandWho(session)
|
||||
|
||||
|
|
@ -100,7 +103,7 @@ class ChatLogic(val ops: ChatOperations, implicit val context: ActorContext) ext
|
|||
def handleIncomingMessage(message: ChatMsg, fromSession: Session): Unit = {
|
||||
import ChatMessageType._
|
||||
message.messageType match {
|
||||
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE =>
|
||||
case CMT_BROADCAST | CMT_SQUAD | CMT_PLATOON | CMT_COMMAND | CMT_NOTE | CMT_OUTFIT =>
|
||||
ops.commandIncomingSendAllIfOnline(session, message)
|
||||
|
||||
case CMT_OPEN =>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package net.psforever.actors.session.normal
|
|||
import akka.actor.typed.scaladsl.adapter._
|
||||
import akka.actor.{ActorContext, ActorRef, typed}
|
||||
import net.psforever.actors.session.{AvatarActor, SessionActor}
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData}
|
||||
import net.psforever.actors.session.support.{GeneralFunctions, GeneralOperations, SessionData, SessionOutfitHandlers}
|
||||
import net.psforever.objects.{Account, BoomerDeployable, BoomerTrigger, ConstructionItem, GlobalDefinitions, LivePlayerList, Player, SensorDeployable, ShieldGeneratorDeployable, SpecialEmp, TelepadDeployable, Tool, TrapDeployable, TurretDeployable, Vehicle}
|
||||
import net.psforever.objects.avatar.{Avatar, PlayerControl, SpecialCarry}
|
||||
import net.psforever.objects.ballistics.Projectile
|
||||
|
|
@ -37,13 +37,16 @@ import net.psforever.objects.vital.etc.SuicideReason
|
|||
import net.psforever.objects.vital.interaction.DamageInteraction
|
||||
import net.psforever.objects.zones.{ZoneProjectile, Zoning}
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Unk2}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, ActionResultMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestAction, CharacterRequestMessage, ChatMsg, CollisionIs, ConnectToWorldRequestMessage, CreateShortcutMessage, DeadState, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitEvent, OutfitMembershipRequest, OutfitMembershipRequestAction, OutfitMembershipResponse, OutfitRequest, OutfitRequestAction, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TerrainCondition, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.{AccountPersistenceService, RetrieveAccountData}
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.OutfitChannel
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.local.support.CaptureFlagManager
|
||||
import net.psforever.types.{CapacitorStateType, ChatMessageType, Cosmetic, ExoSuitType, ImplantType, PlanetSideEmpire, PlanetSideGUID, Vector3}
|
||||
import net.psforever.util.Config
|
||||
import net.psforever.zones.Zones.zones
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -796,6 +799,54 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
val HitHint(_, _) = pkt
|
||||
}
|
||||
|
||||
def handleOutfitMembershipRequest(pkt: OutfitMembershipRequest): Unit = {
|
||||
pkt match {
|
||||
case OutfitMembershipRequest(_, OutfitMembershipRequestAction.Form(_, outfitName)) =>
|
||||
if (player.outfit_id == 0) {
|
||||
SessionOutfitHandlers.HandleOutfitForm(outfitName, player, sessionLogic)
|
||||
}
|
||||
|
||||
case OutfitMembershipRequest(_, OutfitMembershipRequestAction.Invite(_, invitedName)) =>
|
||||
SessionOutfitHandlers.HandleOutfitInvite(zones, invitedName, player)
|
||||
|
||||
case OutfitMembershipRequest(_, OutfitMembershipRequestAction.Kick(memberId, _)) =>
|
||||
SessionOutfitHandlers.HandleOutfitKick(zones, memberId, player)
|
||||
|
||||
case OutfitMembershipRequest(_, OutfitMembershipRequestAction.SetRank(memberId, newRank, _)) =>
|
||||
SessionOutfitHandlers.HandleOutfitPromote(zones, memberId, newRank, player)
|
||||
|
||||
case OutfitMembershipRequest(_, OutfitMembershipRequestAction.AcceptInvite(_)) =>
|
||||
SessionOutfitHandlers.HandleOutfitInviteAccept(player, sessionLogic)
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
def handleOutfitMembershipResponse(pkt: OutfitMembershipResponse): Unit = {}
|
||||
|
||||
def handleOutfitRequest(pkt: OutfitRequest): Unit = {
|
||||
pkt match {
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Ranks(List(r1, r2, r3, r4, r5, r6, r7, r8))) =>
|
||||
// update db
|
||||
//sendResponse(OutfitEvent(6418, Unk2(OutfitInfo(player.outfit_name, 0, 0, 1, OutfitRankNames(r1.getOrElse(""), r2.getOrElse(""), r3.getOrElse(""), r4.getOrElse(""), r5.getOrElse(""), r6.getOrElse(""), r7.getOrElse(""), r8.getOrElse("")), "Welcome to the first PSForever Outfit!", 0, unk11=true, 0, 8888888, 0, 0, 0))))
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Motd(message)) =>
|
||||
// update db
|
||||
//sendResponse(OutfitEvent(6418, Unk2(OutfitInfo(player.outfit_name, 0, 0, 1, OutfitRankNames("", "", "", "", "", "", "", ""), message, 0, unk11=true, 0, 8888888, 0, 0, 0))))
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Unk3(true)) =>
|
||||
SessionOutfitHandlers.HandleViewOutfitWindow(zones, player, player.outfit_id)
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Unk3(false)) =>
|
||||
|
||||
case OutfitRequest(_, OutfitRequestAction.Unk4(true)) =>
|
||||
SessionOutfitHandlers.HandleGetOutfitList(player)
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
/* messages */
|
||||
|
||||
def handleRenewCharSavedTimer(): Unit = {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import net.psforever.objects.serverobject.doors.Door
|
|||
import net.psforever.objects.vehicles.Utility
|
||||
import net.psforever.objects.zones.ZoneProjectile
|
||||
import net.psforever.packet.PlanetSideGamePacket
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.packet.game.{ActionCancelMessage, AvatarFirstTimeEventMessage, AvatarImplantMessage, AvatarJumpMessage, BattleplanMessage, BindPlayerMessage, BugReportMessage, ChangeFireModeMessage, ChangeShortcutBankMessage, CharacterCreateRequestMessage, CharacterRequestMessage, ConnectToWorldRequestMessage, CreateShortcutMessage, DeployObjectMessage, DisplayedAwardMessage, DropItemMessage, EmoteMsg, FacilityBenefitShieldChargeRequestMessage, FriendsRequest, GenericAction, GenericActionMessage, GenericCollisionMsg, GenericObjectActionAtPositionMessage, GenericObjectActionMessage, GenericObjectStateMsg, HitHint, ImplantAction, InvalidTerrainMessage, LootItemMessage, MoveItemMessage, ObjectDetectedMessage, ObjectHeldMessage, OutfitMembershipRequest, OutfitMembershipResponse, OutfitRequest, PickupItemMessage, PlanetsideAttributeMessage, PlayerStateMessageUpstream, RequestDestroyMessage, TargetingImplantRequest, TradeMessage, UnuseItemMessage, UseItemMessage, VoiceHostInfo, VoiceHostRequest, ZipLineMessage}
|
||||
import net.psforever.services.account.AccountPersistenceService
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.types.{ExoSuitType, Vector3}
|
||||
|
|
@ -375,6 +375,12 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex
|
|||
|
||||
def handleHitHint(pkt: HitHint): Unit = { /* intentionally blank */ }
|
||||
|
||||
def handleOutfitMembershipRequest(pkt: OutfitMembershipRequest): Unit = {}
|
||||
|
||||
def handleOutfitMembershipResponse(pkt: OutfitMembershipResponse): Unit = {}
|
||||
|
||||
def handleOutfitRequest(pkt: OutfitRequest): Unit = {}
|
||||
|
||||
/* messages */
|
||||
|
||||
def handleRenewCharSavedTimer(): Unit = { /* intentionally blank */ }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import net.psforever.objects.LivePlayerList
|
|||
import net.psforever.objects.sourcing.PlayerSource
|
||||
import net.psforever.objects.zones.ZoneInfo
|
||||
import net.psforever.packet.game.SetChatFilterMessage
|
||||
import net.psforever.services.chat.{DefaultChannel, SquadChannel}
|
||||
import net.psforever.services.chat.{DefaultChannel, OutfitChannel, SquadChannel}
|
||||
import net.psforever.services.local.{LocalAction, LocalServiceMessage}
|
||||
import net.psforever.services.teamwork.{SquadResponse, SquadService, SquadServiceResponse}
|
||||
import net.psforever.types.ChatMessageType.CMT_QUIT
|
||||
|
|
@ -446,6 +446,14 @@ class ChatOperations(
|
|||
}
|
||||
}
|
||||
|
||||
def commandOutfit(session: Session, message: ChatMsg, toChannel: ChatChannel): Unit = {
|
||||
channels.foreach {
|
||||
case _/*channel*/: OutfitChannel =>
|
||||
commandSendToRecipient(session, message, toChannel)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
def commandWho(session: Session): Unit = {
|
||||
val players = session.zone.Players
|
||||
val popTR = players.count(_.faction == PlanetSideEmpire.TR)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ trait GeneralFunctions extends CommonSessionInterfacingFunctionality {
|
|||
def handleCanNotPutItemInSlot(msg: Containable.CanNotPutItemInSlot): Unit
|
||||
|
||||
def handleReceiveDefaultMessage(default: Any, sender: ActorRef): Unit
|
||||
|
||||
def handleOutfitMembershipRequest(pkt: OutfitMembershipRequest): Unit
|
||||
|
||||
def handleOutfitMembershipResponse(pkt: OutfitMembershipResponse): Unit
|
||||
|
||||
def handleOutfitRequest(pkt: OutfitRequest): Unit
|
||||
}
|
||||
|
||||
class GeneralOperations(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package net.psforever.actors.session.support
|
||||
|
||||
import net.psforever.objects.Player
|
||||
import scala.collection.mutable
|
||||
|
||||
case class OutfitInvite(
|
||||
sentTo: Player,
|
||||
sentFrom: Player,
|
||||
timestamp: Long = System.currentTimeMillis() / 1000
|
||||
)
|
||||
|
||||
object OutfitInviteManager {
|
||||
private val invites = mutable.Map[Long, OutfitInvite]()
|
||||
private val ExpirationSeconds = 320
|
||||
|
||||
def addOutfitInvite(invite: OutfitInvite): Boolean = {
|
||||
invites.get(invite.sentTo.CharId) match {
|
||||
case Some(existing) if (System.currentTimeMillis() / 1000 - existing.timestamp) < ExpirationSeconds =>
|
||||
false // Reject new invite (previous one is still valid)
|
||||
case _ =>
|
||||
invites(invite.sentTo.CharId) = invite
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def removeOutfitInvite(sentToId: Long): Unit = {
|
||||
invites.remove(sentToId)
|
||||
}
|
||||
|
||||
def getOutfitInvite(sentToId: Long): Option[OutfitInvite] = invites.get(sentToId)
|
||||
|
||||
def getAllOutfitInvites: List[OutfitInvite] = invites.values.toList
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,390 @@
|
|||
// Copyright (c) 2025 PSForever
|
||||
package net.psforever.actors.session.support
|
||||
|
||||
import io.getquill.{ActionReturning, EntityQuery, Insert, PostgresJAsyncContext, Query, Quoted, SnakeCase}
|
||||
import net.psforever.objects.avatar.PlayerControl
|
||||
import net.psforever.objects.zones.Zone
|
||||
import net.psforever.objects.Player
|
||||
import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Unk0, Unk1, Unk2}
|
||||
import net.psforever.packet.game.OutfitMembershipResponse.PacketType.CreateResponse
|
||||
import net.psforever.packet.game._
|
||||
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
|
||||
import net.psforever.services.chat.OutfitChannel
|
||||
import net.psforever.types.ChatMessageType
|
||||
import net.psforever.util.Config
|
||||
|
||||
import java.time.LocalDateTime
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
object SessionOutfitHandlers {
|
||||
|
||||
case class Avatar(id: Long, name: String, faction_id: Int, last_login: java.time.LocalDateTime)
|
||||
case class Outfit(id: Long, name: String, faction: Int, owner_id: Long, motd: Option[String], created: java.time.LocalDateTime,
|
||||
rank0: Option[String],
|
||||
rank1: Option[String],
|
||||
rank2: Option[String],
|
||||
rank3: Option[String],
|
||||
rank4: Option[String],
|
||||
rank5: Option[String],
|
||||
rank6: Option[String],
|
||||
rank7: Option[String])
|
||||
case class Outfitmember(id: Long, outfit_id: Long, avatar_id: Long, rank: Int)
|
||||
case class Outfitpoint(id: Long, outfit_id: Long, avatar_id: Long, points: Long)
|
||||
case class OutfitpointMv(outfit_id: Long, points: Long)
|
||||
|
||||
val ctx = new PostgresJAsyncContext(SnakeCase, Config.config.getConfig("database"))
|
||||
import ctx._
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
def HandleOutfitForm(outfitName: String, player: Player, session: SessionData): Unit = {
|
||||
val cleanedName = sanitizeOutfitName(outfitName)
|
||||
|
||||
cleanedName match {
|
||||
case Some(validName) =>
|
||||
ctx.run(findOutfitByName(validName)).flatMap {
|
||||
case existing if existing.nonEmpty =>
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "@OutfitErrorNameAlreadyTaken"))
|
||||
Future.successful(())
|
||||
|
||||
case _ =>
|
||||
createNewOutfit(validName, player.Faction.id, player.CharId).map { outfit =>
|
||||
val seconds: Long =
|
||||
outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000
|
||||
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitEvent(outfit.id, Unk2(
|
||||
OutfitInfo(
|
||||
outfit.name, 0, 0, 1,
|
||||
OutfitRankNames("", "", "", "", "", "", "", ""),
|
||||
"",
|
||||
14, unk11 = true, 0, seconds, 0, 0, 0))))
|
||||
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitMemberUpdate(outfit.id, player.CharId, 7, flag = true))
|
||||
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateSuccess"))
|
||||
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitMembershipResponse(CreateResponse, 0, 0, player.CharId, 0, "", "", flag = true))
|
||||
|
||||
player.outfit_id = outfit.id
|
||||
player.outfit_name = outfit.name
|
||||
|
||||
session.chat.JoinChannel(OutfitChannel(player.outfit_id))
|
||||
}
|
||||
.recover { case e =>
|
||||
e.printStackTrace()
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure"))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "@OutfitCreateFailure"))
|
||||
}
|
||||
}
|
||||
|
||||
def HandleOutfitInvite(zones: Seq[Zone], invitedName: String, sentFrom: Player): Unit = {
|
||||
findPlayerByNameForOutfitAction(zones, invitedName, sentFrom).foreach { invitedPlayer =>
|
||||
|
||||
PlayerControl.sendResponse(invitedPlayer.Zone, invitedPlayer.Name,
|
||||
OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Invite, 0, 0,
|
||||
sentFrom.CharId, sentFrom.CharId, sentFrom.Name, sentFrom.outfit_name, flag = false))
|
||||
|
||||
PlayerControl.sendResponse(sentFrom.Zone, sentFrom.Name,
|
||||
OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Invite, 0, 0,
|
||||
sentFrom.CharId, invitedPlayer.CharId, invitedPlayer.Name, sentFrom.outfit_name, flag = true))
|
||||
|
||||
val outfitInvite = OutfitInvite(invitedPlayer, sentFrom)
|
||||
OutfitInviteManager.addOutfitInvite(outfitInvite)
|
||||
}
|
||||
}
|
||||
|
||||
def HandleOutfitInviteAccept(invited: Player, session: SessionData): Unit = {
|
||||
OutfitInviteManager.getOutfitInvite(invited.CharId) match {
|
||||
case Some(outfitInvite) =>
|
||||
val outfitId = outfitInvite.sentFrom.outfit_id
|
||||
|
||||
(for {
|
||||
_ <- addMemberToOutfit(outfitId, invited.CharId)
|
||||
outfitOpt <- ctx.run(getOutfitById(outfitId)).map(_.headOption)
|
||||
memberCount <- ctx.run(getOutfitMemberCount(outfitId))
|
||||
points <- ctx.run(getOutfitPoints(outfitId)).map(_.headOption.map(_.points).getOrElse(0L))
|
||||
} yield (outfitOpt, memberCount, points))
|
||||
.map {
|
||||
case (Some(outfit), memberCount, points) =>
|
||||
|
||||
PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name,
|
||||
OutfitMembershipResponse(
|
||||
OutfitMembershipResponse.PacketType.Unk2, 0, 0,
|
||||
invited.CharId, outfitInvite.sentFrom.CharId, invited.Name, outfit.name, flag = false))
|
||||
|
||||
PlayerControl.sendResponse(invited.Zone, invited.Name,
|
||||
OutfitMembershipResponse(
|
||||
OutfitMembershipResponse.PacketType.Unk2, 0, 0,
|
||||
invited.CharId, outfitInvite.sentFrom.CharId, invited.Name, outfit.name, flag = true))
|
||||
|
||||
PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name,
|
||||
OutfitEvent(outfitId, OutfitEventAction.Unk5(memberCount)))
|
||||
|
||||
PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name,
|
||||
OutfitMemberEvent(outfitId, invited.CharId,
|
||||
OutfitMemberEventAction.Unk0(invited.Name, 0, 0, 0,
|
||||
OutfitMemberEventAction.PacketType.Padding, 0)))
|
||||
|
||||
val seconds: Long = outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000
|
||||
PlayerControl.sendResponse(invited.Zone, invited.Name,
|
||||
OutfitEvent(outfitId, Unk0(OutfitInfo(
|
||||
outfit.name, points, points, memberCount,
|
||||
OutfitRankNames("", "", "", "", "", "", "", ""),
|
||||
outfit.motd.getOrElse(""),
|
||||
14, unk11 = true, 0, seconds, 0, 0, 0))))
|
||||
|
||||
PlayerControl.sendResponse(invited.Zone, invited.Name,
|
||||
OutfitMemberUpdate(outfit.id, invited.CharId, 0, flag=true))
|
||||
|
||||
OutfitInviteManager.removeOutfitInvite(invited.CharId)
|
||||
|
||||
session.chat.JoinChannel(OutfitChannel(outfit.id))
|
||||
invited.outfit_id = outfit.id
|
||||
invited.outfit_name = outfit.name
|
||||
case (None, _, _) =>
|
||||
|
||||
PlayerControl.sendResponse(invited.Zone, invited.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit"))
|
||||
}
|
||||
.recover { case _ =>
|
||||
PlayerControl.sendResponse(invited.Zone, invited.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "Failed to join outfit"))
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
def HandleOutfitKick(zones: Seq[Zone], kickedId: Long, kickedBy: Player): Unit = {
|
||||
// if same id, player has left the outfit by their own choice
|
||||
if (kickedId == kickedBy.CharId) {
|
||||
// db stuff first
|
||||
PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1()))
|
||||
}
|
||||
else {
|
||||
// db stuff first
|
||||
// tell player they've been kicked (if online)
|
||||
findPlayerByIdForOutfitAction(zones, kickedId, kickedBy).foreach { kicked =>
|
||||
PlayerControl.sendResponse(kicked.Zone, kicked.Name, OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Kick, 0, 1, kickedBy.CharId, kicked.CharId, kicked.Name, kickedBy.Name, flag = false))
|
||||
kicked.Zone.AvatarEvents ! AvatarServiceMessage(kicked.Zone.id, AvatarAction.PlanetsideAttributeToAll(kicked.GUID, 39, 0))
|
||||
//kicked.Zone.AvatarEvents ! AvatarServiceMessage(kicked.Zone.id, AvatarAction.PlanetsideStringAttributeMessage(kicked.GUID, 0, ""))
|
||||
kicked.outfit_id = 0
|
||||
kicked.outfit_name = ""
|
||||
PlayerControl.sendResponse(kicked.Zone, kicked.Name, OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1()))
|
||||
|
||||
// move this out of foreach - db will provide kicked char details
|
||||
PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Kick, 0, 1, kickedBy.CharId, kicked.CharId, kicked.Name, "", flag = true))
|
||||
PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1()))
|
||||
// new number of outfit members?
|
||||
PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, OutfitEvent(kickedBy.outfit_id, OutfitEventAction.Unk5(34)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def HandleOutfitPromote(zones: Seq[Zone], promotedId: Long, newRank: Int, promoter: Player): Unit = {
|
||||
// send to all online players in outfit
|
||||
findPlayerByIdForOutfitAction(zones, promotedId, promoter).foreach { promoted =>
|
||||
PlayerControl.sendResponse(promoted.Zone, promoted.Name, OutfitMemberEvent(6418, promotedId, OutfitMemberEventAction.Unk0(promoted.Name, newRank, 1032432, 0, OutfitMemberEventAction.PacketType.Padding, 0)))
|
||||
PlayerControl.sendResponse(promoter.Zone, promoter.Name, OutfitMemberEvent(6418, promotedId, OutfitMemberEventAction.Unk0(promoted.Name, newRank, 1032432, 0, OutfitMemberEventAction.PacketType.Padding, 0)))
|
||||
}
|
||||
}
|
||||
|
||||
def HandleViewOutfitWindow(zones: Seq[Zone], player: Player, outfitId: Long): Unit = {
|
||||
val outfitDetailsF = for {
|
||||
outfitOpt <- ctx.run(getOutfitById(outfitId)).map(_.headOption)
|
||||
memberCount <- ctx.run(query[Outfitmember].filter(_.outfit_id == lift(outfitId)).size)
|
||||
pointsTotal <- ctx.run(querySchema[OutfitpointMv]("outfitpoint_mv").filter(_.outfit_id == lift(outfitId)))
|
||||
} yield (outfitOpt, memberCount, pointsTotal.headOption.map(_.points).getOrElse(0L))
|
||||
|
||||
val membersF = ctx.run(getOutfitMembersWithDetails(outfitId))
|
||||
|
||||
for {
|
||||
(outfitOpt, memberCount, totalPoints) <- outfitDetailsF
|
||||
members <- membersF
|
||||
} yield {
|
||||
outfitOpt.foreach { outfit =>
|
||||
val seconds: Long = outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000
|
||||
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitEvent(outfit.id, Unk0(OutfitInfo(
|
||||
outfit.name,
|
||||
totalPoints,
|
||||
totalPoints,
|
||||
memberCount,
|
||||
OutfitRankNames("", "", "", "", "", "", "", ""),
|
||||
outfit.motd.getOrElse(""),
|
||||
14, unk11 = true, 0, seconds, 0, 0, 0))))
|
||||
|
||||
members.foreach { case (avatarId, avatarName, points, rank, login) =>
|
||||
val lastLogin = findPlayerByIdForOutfitAction(zones, avatarId, player) match {
|
||||
case Some(_) => 0L
|
||||
case None if player.Name == avatarName => 0L
|
||||
case None => (System.currentTimeMillis() - login.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli) / 1000
|
||||
}
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitMemberEvent(outfit.id, avatarId,
|
||||
OutfitMemberEventAction.Unk0(
|
||||
avatarName,
|
||||
rank,
|
||||
points,
|
||||
lastLogin,
|
||||
OutfitMemberEventAction.PacketType.Padding, 0)))
|
||||
}
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitEvent(outfit.id, Unk1()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def HandleGetOutfitList(player: Player): Unit = {
|
||||
val q = getOutfitsByEmpire(player.Faction.id)
|
||||
val futureResult = ctx.run(q)
|
||||
|
||||
futureResult.onComplete {
|
||||
case Success(rows) =>
|
||||
rows.foreach { case (outfitId, points, name, leaderName, memberCount) =>
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
OutfitListEvent(
|
||||
OutfitListEventAction.ListElementOutfit(
|
||||
outfitId,
|
||||
points,
|
||||
memberCount,
|
||||
name,
|
||||
leaderName)))
|
||||
}
|
||||
|
||||
case Failure(_) =>
|
||||
PlayerControl.sendResponse(player.Zone, player.Name,
|
||||
ChatMsg(ChatMessageType.UNK_227, "Outfit list failed to return")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* supporting functions */
|
||||
|
||||
def sanitizeOutfitName(name: String): Option[String] = {
|
||||
val cleaned = name
|
||||
.replaceAll("""[^A-Za-z0-9\-="\;\[\]\(\)\. ]""", "") // Remove disallowed chars
|
||||
.replaceAll(" +", " ") // Collapse multiple spaces to one
|
||||
.trim // Remove leading/trailing spaces
|
||||
if (cleaned.length >= 2 && cleaned.length <= 32) Some(cleaned) else None
|
||||
}
|
||||
|
||||
def findPlayerByNameForOutfitAction(zones: Iterable[Zone], targetName: String, inviter: Player): Option[Player] = {
|
||||
zones
|
||||
.flatMap(_.LivePlayers)
|
||||
.find(p =>
|
||||
p.Name.equalsIgnoreCase(targetName) && p.Name != inviter.Name &&
|
||||
p.Faction == inviter.Faction && p.outfit_id == 0
|
||||
)
|
||||
}
|
||||
|
||||
def findPlayerByIdForOutfitAction(zones: Iterable[Zone], targetId: Long, initiator: Player): Option[Player] = {
|
||||
zones
|
||||
.flatMap(_.LivePlayers)
|
||||
.find(p =>
|
||||
p.CharId == targetId && p.Name != initiator.Name &&
|
||||
p.Faction == initiator.Faction && p.outfit_id == initiator.outfit_id
|
||||
)
|
||||
}
|
||||
|
||||
/* db actions */
|
||||
|
||||
def findOutfitByName(name: String): Quoted[EntityQuery[Outfit]] = quote {
|
||||
query[Outfit].filter(outfit => lift(name).toLowerCase == outfit.name.toLowerCase)
|
||||
}
|
||||
|
||||
def insertNewOutfit(name: String, faction: Int, owner_id: Long): Quoted[ActionReturning[Outfit, Outfit]] = quote {
|
||||
query[Outfit]
|
||||
.insert(_.name -> lift(name), _.faction -> lift(faction), _.owner_id -> lift(owner_id))
|
||||
.returning(outfit => outfit)
|
||||
}
|
||||
|
||||
def insertOutfitMember(outfit_id: Long, avatar_id: Long, rank: Int): Quoted[Insert[Outfitmember]] = quote {
|
||||
query[Outfitmember].insert(
|
||||
_.outfit_id -> lift(outfit_id),
|
||||
_.avatar_id -> lift(avatar_id),
|
||||
_.rank -> lift(rank)
|
||||
)
|
||||
}
|
||||
|
||||
def insertOutfitPoint(outfit_id: Long, avatar_id: Long): Quoted[Insert[Outfitpoint]] = quote {
|
||||
query[Outfitpoint].insert(
|
||||
_.outfit_id -> lift(outfit_id),
|
||||
_.avatar_id -> lift(avatar_id)
|
||||
)
|
||||
}
|
||||
|
||||
def createNewOutfit(name: String, faction: Int, owner_id: Long): Future[Outfit] = {
|
||||
ctx.transaction { implicit ec =>
|
||||
for {
|
||||
outfit <- ctx.run(insertNewOutfit(name, faction, owner_id))
|
||||
_ <- ctx.run(insertOutfitMember(outfit.id, owner_id, rank=7))
|
||||
_ <- ctx.run(insertOutfitPoint(outfit.id, owner_id))
|
||||
} yield outfit
|
||||
}
|
||||
}
|
||||
|
||||
def addMemberToOutfit(outfit_id: Long, avatar_id: Long): Future[Unit] = {
|
||||
ctx.transaction { implicit ec =>
|
||||
for {
|
||||
_ <- ctx.run(insertOutfitMember(outfit_id, avatar_id, rank=0))
|
||||
_ <- ctx.run(insertOutfitPoint(outfit_id, avatar_id))
|
||||
} yield ()
|
||||
}
|
||||
}
|
||||
|
||||
def getOutfitById(id: Long): Quoted[EntityQuery[Outfit]] = quote {
|
||||
query[Outfit].filter(_.id == lift(id))
|
||||
}
|
||||
|
||||
def getOutfitMemberCount(id: Long): Quoted[Long] = quote {
|
||||
query[Outfitmember].filter(_.outfit_id == lift(id)).size
|
||||
}
|
||||
|
||||
def getOutfitPoints(id: Long): Quoted[EntityQuery[OutfitpointMv]] = quote {
|
||||
querySchema[OutfitpointMv]("outfitpoint_mv").filter(_.outfit_id == lift(id))
|
||||
}
|
||||
|
||||
def getOutfitMembersWithDetails(outfitId: Long): Quoted[Query[(Long, String, Long, Int, LocalDateTime)]] = quote {
|
||||
query[Outfitmember]
|
||||
.filter(_.outfit_id == lift(outfitId))
|
||||
.join(query[Avatar]).on(_.avatar_id == _.id)
|
||||
.leftJoin(query[Outfitpoint]).on {
|
||||
case ((member, _), points) =>
|
||||
points.outfit_id == member.outfit_id && points.avatar_id == member.avatar_id
|
||||
}
|
||||
.map {
|
||||
case ((member, avatar), pointsOpt) =>
|
||||
(member.avatar_id, avatar.name, pointsOpt.map(_.points).getOrElse(0L), member.rank, avatar.last_login)
|
||||
}
|
||||
}
|
||||
|
||||
def getOutfitsByEmpire(playerEmpireId: Int): Quoted[Query[(Long, Long, String, String, Long)]] = quote {
|
||||
query[Outfit]
|
||||
.filter(_.faction == lift(playerEmpireId))
|
||||
.join(query[Avatar]).on((outfit, avatar) => outfit.owner_id == avatar.id)
|
||||
.leftJoin(
|
||||
query[Outfitmember]
|
||||
.groupBy(_.outfit_id)
|
||||
.map { case (oid, members) => (oid, members.size) }
|
||||
).on { case ((outfit, _), (oid, _)) => oid == outfit.id }
|
||||
.leftJoin(querySchema[OutfitpointMv]("outfitpoint_mv")).on {
|
||||
case (((outfit, _), _), points) => points.outfit_id == outfit.id
|
||||
}
|
||||
.map {
|
||||
case (((outfit, leader), memberCounts), points) =>
|
||||
(outfit.id, points.map(_.points).getOrElse(0L), outfit.name, leader.name, memberCounts.map(_._2).getOrElse(0L))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,8 @@ class Player(var avatar: Avatar)
|
|||
var silenced: Boolean = false
|
||||
var death_by: Int = 0
|
||||
var lastShotSeq_time: Int = -1
|
||||
var outfit_name: String = ""
|
||||
var outfit_id: Long = 0
|
||||
|
||||
/** From PlanetsideAttributeMessage */
|
||||
var PlanetsideAttribute: Array[Long] = Array.ofDim(120)
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ object AvatarConverter {
|
|||
0
|
||||
)
|
||||
val ab: (Boolean, Int) => CharacterAppearanceB = CharacterAppearanceB(
|
||||
0L,
|
||||
outfit_name = "",
|
||||
obj.outfit_id,
|
||||
obj.outfit_name,
|
||||
outfit_logo = 0,
|
||||
unk1 = false,
|
||||
obj.isBackpack,
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] {
|
|||
type Type = Value
|
||||
|
||||
val CreateResponse: PacketType.Value = Value(0)
|
||||
val Unk1: PacketType.Value = Value(1) // Info: Player has been invited / response to OutfitMembershipRequest Unk2 for that player
|
||||
val Invite: PacketType.Value = Value(1) // Info: Player has been invited / response to OutfitMembershipRequest Unk2 for that player
|
||||
val Unk2: PacketType.Value = Value(2) // Invited / Accepted / Added
|
||||
val Unk3: PacketType.Value = Value(3)
|
||||
val Unk4: PacketType.Value = Value(4)
|
||||
val Unk5: PacketType.Value = Value(5)
|
||||
val Kick: PacketType.Value = Value(5)
|
||||
val Unk6: PacketType.Value = Value(6) // 6 and 7 seen as failed decodes, validity unknown
|
||||
val Unk7: PacketType.Value = Value(7)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,3 +12,5 @@ final case class SquadChannel(guid: PlanetSideGUID) extends ChatChannel
|
|||
case object SpectatorChannel extends ChatChannel
|
||||
|
||||
case object CustomerServiceChannel extends ChatChannel
|
||||
|
||||
final case class OutfitChannel(id: Long) extends ChatChannel
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
|
|||
(channel, message.messageType) match {
|
||||
case (SquadChannel(_), CMT_SQUAD) => ()
|
||||
case (SquadChannel(_), CMT_VOICE) if message.contents.startsWith("SH") => ()
|
||||
case (OutfitChannel(_), CMT_OUTFIT) => ()
|
||||
case (DefaultChannel, messageType) if messageType != CMT_SQUAD => ()
|
||||
case (SpectatorChannel, messageType) if messageType != CMT_SQUAD => ()
|
||||
case _ =>
|
||||
|
|
@ -158,6 +159,9 @@ class ChatService(context: ActorContext[ChatService.Command]) extends AbstractBe
|
|||
case CMT_SQUAD =>
|
||||
subs.foreach(_.actor ! MessageResponse(session, message, channel))
|
||||
|
||||
case CMT_OUTFIT =>
|
||||
subs.foreach(_.actor ! MessageResponse(session, message, channel))
|
||||
|
||||
case CMT_NOTE =>
|
||||
subs
|
||||
.filter(_.sessionSource.session.player.Name == message.recipient)
|
||||
|
|
|
|||
Loading…
Reference in a new issue