From c84bf9ae74bbc8dc56f5332aef395aa9f03b1856 Mon Sep 17 00:00:00 2001 From: Resaec Date: Sun, 31 Aug 2025 03:05:31 +0200 Subject: [PATCH] evict players from the outfit. no longer shall you spy on our stale info! update packet types --- .../actors/session/csr/GeneralLogic.scala | 6 +- .../actors/session/normal/GeneralLogic.scala | 8 +-- .../support/SessionOutfitHandlers.scala | 51 +++++++------- .../psforever/packet/game/OutfitEvent.scala | 69 +++++++++++++------ .../packet/game/OutfitMemberEvent.scala | 50 ++++++++------ .../packet/game/OutfitMembershipRequest.scala | 18 ++--- .../game/OutfitMembershipResponse.scala | 6 +- .../psforever/packet/game/OutfitRequest.scala | 26 +++---- src/test/scala/game/OutfitEventTest.scala | 20 +++--- .../scala/game/OutfitMemberEventTest.scala | 12 ++-- .../game/OutfitMembershipResponseTest.scala | 8 +-- src/test/scala/game/OutfitRequestTest.scala | 8 +-- 12 files changed, 163 insertions(+), 119 deletions(-) diff --git a/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala index f32bdef95..a920f4fed 100644 --- a/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/csr/GeneralLogic.scala @@ -30,7 +30,7 @@ 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.OutfitEventAction.{OutfitInfo, OutfitRankNames, Unk0, Unk1} +import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Initial, 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} @@ -673,9 +673,9 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex def handleOutfitRequest(pkt: OutfitRequest): Unit = { pkt match { - case OutfitRequest(_, OutfitRequestAction.Unk3(true)) => + case OutfitRequest(_, OutfitRequestAction.OutfitWindowOpen(true)) => - case OutfitRequest(_, OutfitRequestAction.Unk3(false)) => + case OutfitRequest(_, OutfitRequestAction.OutfitWindowOpen(false)) => case _ => } diff --git a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala index 79c335505..a2f87cd16 100644 --- a/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala +++ b/src/main/scala/net/psforever/actors/session/normal/GeneralLogic.scala @@ -37,7 +37,7 @@ 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.OutfitEventAction.{OutfitInfo, OutfitRankNames, Unk2} +import net.psforever.packet.game.OutfitEventAction.{OutfitInfo, OutfitRankNames, Update} 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} @@ -836,12 +836,12 @@ class GeneralLogic(val ops: GeneralOperations, implicit val context: ActorContex case OutfitRequest(_, OutfitRequestAction.Ranks(List(r1, r2, r3, r4, r5, r6, r7, r8))) => SessionOutfitHandlers.HandleOutfitRank(zones, List(r1, r2, r3, r4, r5, r6, r7, r8), player) - case OutfitRequest(_, OutfitRequestAction.Unk3(true)) => + case OutfitRequest(_, OutfitRequestAction.OutfitWindowOpen(true)) => SessionOutfitHandlers.HandleViewOutfitWindow(zones, player, player.outfit_id) - case OutfitRequest(_, OutfitRequestAction.Unk3(false)) => + case OutfitRequest(_, OutfitRequestAction.OutfitWindowOpen(false)) => - case OutfitRequest(_, OutfitRequestAction.Unk4(true)) => + case OutfitRequest(_, OutfitRequestAction.OutfitListWindowOpen(true)) => SessionOutfitHandlers.HandleGetOutfitList(player) case _ => diff --git a/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala index 47ba310c9..bafd60be9 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionOutfitHandlers.scala @@ -5,7 +5,7 @@ import io.getquill.{ActionReturning, EntityQuery, Insert, PostgresJAsyncContext, 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.OutfitEventAction.{Leaving, OutfitInfo, OutfitRankNames, Initial, Unk1, Update, UpdateMemberCount} import net.psforever.packet.game.OutfitMembershipResponse.PacketType.CreateResponse import net.psforever.packet.game._ import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage} @@ -63,7 +63,7 @@ object SessionOutfitHandlers { outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000 PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfit.id, Unk2( + OutfitEvent(outfit.id, Update( OutfitInfo( outfit.name, 0, 0, 1, OutfitRankNames("", "", "", "", "", "", "", ""), @@ -143,16 +143,16 @@ object SessionOutfitHandlers { invited.CharId, outfitInvite.sentFrom.CharId, invited.Name, outfit.name, flag = true)) PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name, - OutfitEvent(outfitId, OutfitEventAction.Unk5(memberCount))) + OutfitEvent(outfitId, UpdateMemberCount(memberCount))) PlayerControl.sendResponse(outfitInvite.sentFrom.Zone, outfitInvite.sentFrom.Name, OutfitMemberEvent(outfitId, invited.CharId, - OutfitMemberEventAction.Unk0(invited.Name, 0, 0, 0, + OutfitMemberEventAction.Update(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( + OutfitEvent(outfitId, Initial(OutfitInfo( outfit.name, points, points, memberCount, OutfitRankNames("", "", "", "", "", "", "", ""), outfit.motd.getOrElse(""), @@ -211,12 +211,12 @@ object SessionOutfitHandlers { case (deleted, _) => if (deleted > 0) { PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1())) + OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) zones.filter(z => z.AllPlayers.nonEmpty).flatMap(_.AllPlayers) .filter(p => p.outfit_id == kickedBy.outfit_id).foreach(outfitMember => PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1())) + OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) ) session.chat.LeaveChannel(OutfitChannel(kickedBy.outfit_id)) @@ -238,8 +238,13 @@ object SessionOutfitHandlers { case (deleted, _) => if (deleted > 0) { findPlayerByIdForOutfitAction(zones, kickedId, kickedBy).foreach { kicked => + PlayerControl.sendResponse(kicked.Zone, kicked.Name, - OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Kick, 0, 1, + OutfitEvent(kickedBy.outfit_id, Leaving()) + ) + + PlayerControl.sendResponse(kicked.Zone, kicked.Name, + OutfitMembershipResponse(OutfitMembershipResponse.PacketType.YouGotKicked, 0, 1, kickedBy.CharId, kicked.CharId, kicked.Name, kickedBy.Name, flag = false)) kicked.Zone.AvatarEvents ! AvatarServiceMessage(kicked.Zone.id, @@ -251,7 +256,7 @@ object SessionOutfitHandlers { kicked.outfit_id = 0 kicked.outfit_name = "" PlayerControl.sendResponse(kicked.Zone, kicked.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1())) + OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) } val avatarName: Future[Option[String]] = ctx.run( @@ -260,15 +265,15 @@ object SessionOutfitHandlers { avatarName.foreach { case Some(name) => PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, - OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Kick, 0, 1, kickedBy.CharId, kickedId, name, "", flag = true)) + OutfitMembershipResponse(OutfitMembershipResponse.PacketType.YouKicked, 0, 1, kickedBy.CharId, kickedId, name, "", flag = true)) case None => PlayerControl.sendResponse(kickedBy.Zone, kickedBy.Name, - OutfitMembershipResponse(OutfitMembershipResponse.PacketType.Kick, 0, 1, kickedBy.CharId, kickedId, "NameNotFound", "", flag = true)) + OutfitMembershipResponse(OutfitMembershipResponse.PacketType.YouKicked, 0, 1, kickedBy.CharId, kickedId, "NameNotFound", "", flag = true)) } zones.filter(z => z.AllPlayers.nonEmpty).flatMap(_.AllPlayers) .filter(p => p.outfit_id == kickedBy.outfit_id).foreach(outfitMember => PlayerControl.sendResponse(outfitMember.Zone, outfitMember.Name, - OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Unk1())) + OutfitMemberEvent(kickedBy.outfit_id, kickedId, OutfitMemberEventAction.Kicked())) ) // this needs to be the kicked player // session.chat.LeaveChannel(OutfitChannel(kickedBy.outfit_id)) @@ -303,7 +308,7 @@ object SessionOutfitHandlers { PlayerControl.sendResponse( zone, outfitMember.Name, OutfitMemberEvent(outfit_id, promoter.avatar.id, - OutfitMemberEventAction.Unk0(promoter.Name, 6, owner_points, 0, OutfitMemberEventAction.PacketType.Padding, 0))) + OutfitMemberEventAction.Update(promoter.Name, 6, owner_points, 0, OutfitMemberEventAction.PacketType.Padding, 0))) }) }) } @@ -327,7 +332,7 @@ object SessionOutfitHandlers { PlayerControl.sendResponse( zone, player.Name, OutfitMemberEvent(outfit_id, promoted.avatar.id, - OutfitMemberEventAction.Unk0(promoted.Name, newRank, member_points, 0, OutfitMemberEventAction.PacketType.Padding, 0))) + OutfitMemberEventAction.Update(promoted.Name, newRank, member_points, 0, OutfitMemberEventAction.PacketType.Padding, 0))) }) }) } @@ -356,7 +361,7 @@ object SessionOutfitHandlers { val seconds: Long = outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000 PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfit.id, Unk0(OutfitInfo( + OutfitEvent(outfit.id, Initial(OutfitInfo( outfit.name, totalPoints, totalPoints, @@ -382,7 +387,7 @@ object SessionOutfitHandlers { } PlayerControl.sendResponse(player.Zone, player.Name, OutfitMemberEvent(outfit.id, avatarId, - OutfitMemberEventAction.Unk0( + OutfitMemberEventAction.Update( avatarName, rank, points, @@ -438,7 +443,7 @@ object SessionOutfitHandlers { // send to all online players in outfit val outfit_event = OutfitEvent( outfit_id, - Unk2( + Update( OutfitInfo( outfit_name = outfit.name, outfit_points1 = totalPoints, @@ -500,7 +505,7 @@ object SessionOutfitHandlers { // send to all online players in outfit val outfit_event = OutfitEvent( outfit_id, - Unk2( + Update( OutfitInfo( outfit_name = outfit.name, outfit_points1 = totalPoints, @@ -555,7 +560,7 @@ object SessionOutfitHandlers { val seconds: Long = outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000 PlayerControl.sendResponse(player.Zone, player.Name, - OutfitEvent(outfitId, Unk2(OutfitInfo( + OutfitEvent(outfitId, Update(OutfitInfo( outfit.name, points, points, memberCount, OutfitRankNames(outfit.rank0.getOrElse(""), outfit.rank1.getOrElse(""), outfit.rank2.getOrElse(""), outfit.rank3.getOrElse(""), outfit.rank4.getOrElse(""), outfit.rank5.getOrElse(""), @@ -744,7 +749,7 @@ object SessionOutfitHandlers { } } - def updateMemberRankById(outfit_id: Long, avatar_id: Long, rank: Int): Quoted[Update[Outfitmember]] = quote { + def updateMemberRankById(outfit_id: Long, avatar_id: Long, rank: Int): Quoted[io.getquill.Update[Outfitmember]] = quote { query[Outfitmember] .filter(_.outfit_id == lift(outfit_id)) .filter(_.avatar_id == lift(avatar_id)) @@ -759,7 +764,7 @@ object SessionOutfitHandlers { } } - def updateOutfitOwnerById(outfit_id: Long, owner_id: Long): Quoted[Update[Outfit]] = quote { + def updateOutfitOwnerById(outfit_id: Long, owner_id: Long): Quoted[io.getquill.Update[Outfit]] = quote { query[Outfit] .filter(_.id == lift(outfit_id)) .update(_.owner_id -> lift(owner_id)) @@ -775,7 +780,7 @@ object SessionOutfitHandlers { } } - def updateOutfitMotdById(outfit_id: Long, motd: Option[String]): Quoted[Update[Outfit]] = quote { + def updateOutfitMotdById(outfit_id: Long, motd: Option[String]): Quoted[io.getquill.Update[Outfit]] = quote { query[Outfit] .filter(_.id == lift(outfit_id)) .update(_.motd -> lift(motd)) @@ -789,7 +794,7 @@ object SessionOutfitHandlers { } } - def updateOutfitRanksById(outfit_id: Long, list: List[Option[String]]): Quoted[Update[Outfit]] = { + def updateOutfitRanksById(outfit_id: Long, list: List[Option[String]]): Quoted[io.getquill.Update[Outfit]] = { // Normalize: turn empty strings into None val normalized = list.map { diff --git a/src/main/scala/net/psforever/packet/game/OutfitEvent.scala b/src/main/scala/net/psforever/packet/game/OutfitEvent.scala index be267fce7..1732e4c85 100644 --- a/src/main/scala/net/psforever/packet/game/OutfitEvent.scala +++ b/src/main/scala/net/psforever/packet/game/OutfitEvent.scala @@ -50,26 +50,55 @@ object OutfitEventAction { unk25: Long, ) - final case class Unk0( + /** + * Initial + * + * Send at the start of an OutfitWindow info dump. + * + * Not always complete, seen as an initialization after login, join or while outfit is in formation. + * @param outfit_info + */ + final case class Initial( outfit_info: OutfitInfo ) extends OutfitEventAction(code = 0) final case class Unk1( ) extends OutfitEventAction(code = 1) - final case class Unk2( + /** + * Update + * + * Send after changing outfit Ranks, MOTD and other situations. + * @param outfit_info + */ + final case class Update( outfit_info: OutfitInfo, ) extends OutfitEventAction(code = 2) - final case class Unk3( + /** + * Send to players to tell them they left the outfit. + * + * Resets them to behave like they have no outfit. + * Will have them open the OutfitListWindow instead of the OutfitWindow. + */ + final case class Leaving( ) extends OutfitEventAction(code = 3) + /** + * Used to switch from the temporary "invalid" outfit ID used while formation to a valid ID used from that point on. + * @param new_outfit_id the new ID that represents this specific outfit in the DB + */ final case class UpdateOutfitId( new_outfit_id: Long, ) extends OutfitEventAction(code = 4) - final case class Unk5( - unk1: Long, + /** + * Used to tell outfit members that the member count changed. + * Send after InviteAccept or Kick actions + * @param member_count + */ + final case class UpdateMemberCount( + member_count: Long, ) extends OutfitEventAction(code = 5) final case class Unknown(badCode: Int, data: BitVector) extends OutfitEventAction(badCode) @@ -126,35 +155,35 @@ object OutfitEventAction { } ) - val Unk0Codec: Codec[Unk0] = ( + val Unk0Codec: Codec[Initial] = ( ("outfit_info" | InfoCodec) - ).xmap[Unk0]( + ).xmap[Initial]( { case info => - Unk0(info) + Initial(info) }, { - case Unk0(info) => + case Initial(info) => info } ) val Unk1Codec: Codec[Unk1] = PacketHelpers.emptyCodec(Unk1()) - val Unk2Codec: Codec[Unk2] = ( + val Unk2Codec: Codec[Update] = ( ("outfit_info" | InfoCodec) - ).xmap[Unk2]( + ).xmap[Update]( { case info => - Unk2(info) + Update(info) }, { - case Unk2(info) => + case Update(info) => info } ) - val Unk3Codec: Codec[Unk3] = PacketHelpers.emptyCodec(Unk3()) + val Unk3Codec: Codec[Leaving] = PacketHelpers.emptyCodec(Leaving()) val UpdateOutfitIdCodec: Codec[UpdateOutfitId] = ( // update outfit_id? // 2016.03.18 #10640 // after this packet the referenced id changes to the new one, old is not used again ("new_outfit_id" | uint32L) @@ -169,15 +198,15 @@ object OutfitEventAction { } ) - val Unk5Codec: Codec[Unk5] = ( + val UpdateMemberCountCodec: Codec[UpdateMemberCount] = ( ("" | uint32L) - ).xmap[Unk5]( + ).xmap[UpdateMemberCount]( { case u1 => - Unk5(u1) + UpdateMemberCount(u1) }, { - case Unk5(u1) => + case UpdateMemberCount(u1) => u1 } ) @@ -220,7 +249,7 @@ object OutfitEvent extends Marshallable[OutfitEvent] { val Unk2: PacketType.Value = Value(2) // send after creating an outfit // normal info, same as Unk0 val Unk3: PacketType.Value = Value(3) // below val UpdateOutfitId: PacketType.Value = Value(4) - val Unk5: PacketType.Value = Value(5) + val UpdateMemberCount: PacketType.Value = Value(5) val Unk6: PacketType.Value = Value(6) val Unk7: PacketType.Value = Value(7) @@ -237,7 +266,7 @@ object OutfitEvent extends Marshallable[OutfitEvent] { case 2 => Unk2Codec // sent after /outfitcreate and on login if in an outfit case 3 => Unk3Codec case 4 => UpdateOutfitIdCodec - case 5 => Unk5Codec + case 5 => UpdateMemberCountCodec case 6 => unknownCodec(action = code) case 7 => unknownCodec(action = code) diff --git a/src/main/scala/net/psforever/packet/game/OutfitMemberEvent.scala b/src/main/scala/net/psforever/packet/game/OutfitMemberEvent.scala index 0ae680853..a268a6d17 100644 --- a/src/main/scala/net/psforever/packet/game/OutfitMemberEvent.scala +++ b/src/main/scala/net/psforever/packet/game/OutfitMemberEvent.scala @@ -33,20 +33,30 @@ object OutfitMemberEventAction { } - /* - action is unimplemented! if action == 0 unk2 will contain one additional uint32L - padding contains one uint4L of padding. may contain uint32L of unknown data depending on action - */ - final case class Unk0( + /** + * + * Update + * + * Update is used to inform outfit members about a new member. + * Gets send after an InviteAccept or Rank changes. + * + * @param member_name + * @param rank + * @param points client divides this by 100 + * @param last_online seconds ago from current time, 0 if online + * @param action should always be 1, otherwise there will be actual data in padding. not implemented! + * @param padding should always be 0, 4 bits of padding // only contains data if action is 0 + */ + final case class Update( member_name: String, rank: Int, - points: Long, // client divides this by 100 - last_online: Long, // seconds ago from current time, 0 if online - action: PacketType.Type, // should always be 1, otherwise there will be actual data in padding. not implemented! - padding: Int // should always be 0, 4 bits of padding // only contains data if action is 0 + points: Long, + last_online: Long, + action: PacketType.Type, + padding: Int ) extends OutfitMemberEventAction(code = 0) - final case class Unk1( + final case class Kicked( ) extends OutfitMemberEventAction(code = 1) final case class Unknown(badCode: Int, data: BitVector) extends OutfitMemberEventAction(badCode) @@ -58,25 +68,25 @@ object OutfitMemberEventAction { object Codecs { private val everFailCondition = conditional(included = false, bool) - val Unk0Codec: Codec[Unk0] = ( - ("member_name" | PacketHelpers.encodedWideStringAligned(6)) :: // from here is packet_type == 0 only + val UpdateCodec: Codec[Update] = ( + ("member_name" | PacketHelpers.encodedWideStringAligned(6)) :: ("rank" | uint(3)) :: ("points" | uint32L) :: ("last_login" | uint32L) :: ("action" | OutfitMemberEventAction.PacketType.codec) :: ("padding" | uint4L) - ).xmap[Unk0]( + ).xmap[Update]( { case member_name :: rank :: points :: last_login :: action :: padding :: HNil => - Unk0(member_name, rank, points, last_login, action, padding) + Update(member_name, rank, points, last_login, action, padding) }, { - case Unk0(member_name, rank, points, last_login, action, padding) => + case Update(member_name, rank, points, last_login, action, padding) => member_name :: rank :: points :: last_login :: action :: padding :: HNil } ) - val Unk1Codec: Codec[Unk1] = PacketHelpers.emptyCodec(Unk1()) + val KickedCodec: Codec[Kicked] = PacketHelpers.emptyCodec(Kicked()) /** * A common form for known action code indexes with an unknown purpose and transformation is an "Unknown" object. @@ -109,8 +119,8 @@ object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] { object PacketType extends Enumeration { type Type = Value - val Unk0: PacketType.Value = Value(0) - val Unk1: PacketType.Value = Value(1) // Info: Player has been invited / response to OutfitMembershipRequest Unk2 for that player + val Update: PacketType.Value = Value(0) + val Kicked: PacketType.Value = Value(1) val Unk2: PacketType.Value = Value(2) val Unk3: PacketType.Value = Value(3) @@ -122,8 +132,8 @@ object OutfitMemberEvent extends Marshallable[OutfitMemberEvent] { import scala.annotation.switch ((code: @switch) match { - case 0 => Unk0Codec - case 1 => Unk1Codec + case 0 => UpdateCodec + case 1 => KickedCodec case 2 => unknownCodec(code) case 3 => unknownCodec(code) diff --git a/src/main/scala/net/psforever/packet/game/OutfitMembershipRequest.scala b/src/main/scala/net/psforever/packet/game/OutfitMembershipRequest.scala index 41cb6b176..8b12ec5b5 100644 --- a/src/main/scala/net/psforever/packet/game/OutfitMembershipRequest.scala +++ b/src/main/scala/net/psforever/packet/game/OutfitMembershipRequest.scala @@ -9,7 +9,7 @@ import scodec.codecs._ import shapeless.{::, HNil} final case class OutfitMembershipRequest( - outfit_id: Long, + requester_id: Long, action: OutfitMembershipRequestAction ) extends PlanetSideGamePacket { type Packet = OutfitMembershipRequest @@ -37,8 +37,8 @@ object OutfitMembershipRequestAction { ) extends OutfitMembershipRequestAction(code = 1) final case class Invite( - avatar_id: Long, - member_name: String, + target_id: Long, + target_name: String, ) extends OutfitMembershipRequestAction(code = 2) final case class AcceptInvite( @@ -50,19 +50,19 @@ object OutfitMembershipRequestAction { ) extends OutfitMembershipRequestAction(code = 4) final case class CancelInvite( - avatar_id: Long, - member_name: String, + target_id: Long, + target_name: String, ) extends OutfitMembershipRequestAction(code = 5) final case class Kick( - avatar_id: Long, - member_name: String, + target_id: Long, + target_name: String, ) extends OutfitMembershipRequestAction(code = 6) final case class SetRank( - avatar_id: Long, + target_id: Long, rank: Int, - member_name: String, + target_name: String, ) extends OutfitMembershipRequestAction(code = 7) final case class Unknown(badCode: Int, data: BitVector) extends OutfitMembershipRequestAction(badCode) diff --git a/src/main/scala/net/psforever/packet/game/OutfitMembershipResponse.scala b/src/main/scala/net/psforever/packet/game/OutfitMembershipResponse.scala index 90f592954..c38f1ba9f 100644 --- a/src/main/scala/net/psforever/packet/game/OutfitMembershipResponse.scala +++ b/src/main/scala/net/psforever/packet/game/OutfitMembershipResponse.scala @@ -12,7 +12,7 @@ final case class OutfitMembershipResponse( packet_type: OutfitMembershipResponse.PacketType.Type, unk0: Int, unk1: Int, - outfit_id: Long, + requester_id: Long, target_id: Long, str1: String, str2: String, @@ -34,8 +34,8 @@ object OutfitMembershipResponse extends Marshallable[OutfitMembershipResponse] { val Invite: PacketType.Value = Value(1) // response to OutfitMembershipRequest Unk2 for that player val InviteAccepted: PacketType.Value = Value(2) val InviteRejected: PacketType.Value = Value(3) - val Unk4: PacketType.Value = Value(4) - val Kick: PacketType.Value = Value(5) + val YouGotKicked: PacketType.Value = Value(4) + val YouKicked: 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) diff --git a/src/main/scala/net/psforever/packet/game/OutfitRequest.scala b/src/main/scala/net/psforever/packet/game/OutfitRequest.scala index 3e8d5f513..5520d98b8 100644 --- a/src/main/scala/net/psforever/packet/game/OutfitRequest.scala +++ b/src/main/scala/net/psforever/packet/game/OutfitRequest.scala @@ -8,7 +8,7 @@ import scodec.codecs._ import shapeless.{::, HNil} final case class OutfitRequest( - outfit_id: Long, + requester_id: Long, action: OutfitRequestAction ) extends PlanetSideGamePacket { type Packet = OutfitRequest @@ -42,13 +42,13 @@ object OutfitRequestAction { * na * @param unk na */ - final case class Unk3(menuOpen: Boolean) extends OutfitRequestAction(code = 3) + final case class OutfitWindowOpen(menuOpen: Boolean) extends OutfitRequestAction(code = 3) /** * na * @param unk na */ - final case class Unk4(menuOpen: Boolean) extends OutfitRequestAction(code = 4) + final case class OutfitListWindowOpen(menuOpen: Boolean) extends OutfitRequestAction(code = 4) /** * na @@ -116,24 +116,24 @@ object OutfitRequest extends Marshallable[OutfitRequest] { /** * na */ - private val unk3Codec: Codec[OutfitRequestAction] = bool.hlist.xmap[OutfitRequestAction] ( + private val OutfitWindowOpenCodec: Codec[OutfitRequestAction] = bool.hlist.xmap[OutfitRequestAction] ( { - case value :: HNil => OutfitRequestAction.Unk3(value) + case value :: HNil => OutfitRequestAction.OutfitWindowOpen(value) }, { - case OutfitRequestAction.Unk3(value) => value :: HNil + case OutfitRequestAction.OutfitWindowOpen(value) => value :: HNil } ) /** * na */ - private val unk4Codec: Codec[OutfitRequestAction] = bool.hlist.xmap[OutfitRequestAction] ( + private val OutfitListWindowOpenCodec: Codec[OutfitRequestAction] = bool.hlist.xmap[OutfitRequestAction] ( { - case value :: HNil => OutfitRequestAction.Unk4(value) + case value :: HNil => OutfitRequestAction.OutfitListWindowOpen(value) }, { - case OutfitRequestAction.Unk4(value) => value :: HNil + case OutfitRequestAction.OutfitListWindowOpen(value) => value :: HNil } ) @@ -151,8 +151,8 @@ object OutfitRequest extends Marshallable[OutfitRequest] { val Motd: PacketType.Value = Value(0) val Rank: PacketType.Value = Value(1) val Unk2: PacketType.Value = Value(2) - val Detail: PacketType.Value = Value(3) - val List: PacketType.Value = Value(4) // sent by client if menu is either open (true) or closed (false) + val OutfitWindowOpen: PacketType.Value = Value(3) + val OutfitListWindowOpen: PacketType.Value = Value(4) // sent by client if menu is either open (true) or closed (false) implicit val codec: Codec[Type] = PacketHelpers.createEnumerationCodec(this, uintL(3)) } @@ -165,8 +165,8 @@ object OutfitRequest extends Marshallable[OutfitRequest] { case 0 => MotdCodec case 1 => RankCodec case 2 => unk2Codec - case 3 => unk3Codec - case 4 => unk4Codec + case 3 => OutfitWindowOpenCodec + case 4 => OutfitListWindowOpenCodec case _ => failCodec(code) } } diff --git a/src/test/scala/game/OutfitEventTest.scala b/src/test/scala/game/OutfitEventTest.scala index 27039f043..d54cc16f9 100644 --- a/src/test/scala/game/OutfitEventTest.scala +++ b/src/test/scala/game/OutfitEventTest.scala @@ -53,7 +53,7 @@ class OutfitEventTest extends Specification { PacketCoding.decodePacket(unk0_ABC).require match { case OutfitEvent(outfit_guid, action) => outfit_guid mustEqual 25044 - action mustEqual Unk0( + action mustEqual Initial( OutfitInfo( outfit_name = "Black Armored Reapers", outfit_points1 = 223190045, @@ -78,7 +78,7 @@ class OutfitEventTest extends Specification { "encode Unk0 ABC" in { val msg = OutfitEvent( 25044, - Unk0( + Initial( OutfitInfo( outfit_name = "Black Armored Reapers", outfit_points1 = 223190045, @@ -125,7 +125,7 @@ class OutfitEventTest extends Specification { PacketCoding.decodePacket(unk2_ABC).require match { case OutfitEvent(outfit_guid, action) => outfit_guid mustEqual 2147418113L - action mustEqual Unk2(OutfitInfo( + action mustEqual Update(OutfitInfo( outfit_name = "PlanetSide_Forever_Vanu", outfit_points1 = 0, outfit_points2 = 0, @@ -148,7 +148,7 @@ class OutfitEventTest extends Specification { "encode Unk2 ABC" in { val msg = OutfitEvent( 2147418113L, - Unk2( + Update( OutfitInfo( outfit_name = "PlanetSide_Forever_Vanu", outfit_points1 = 0, @@ -175,7 +175,7 @@ class OutfitEventTest extends Specification { PacketCoding.decodePacket(unk3_ABC).require match { case OutfitEvent(outfit_guid, action) => outfit_guid mustEqual 2147418113L - action mustEqual Unk3() + action mustEqual Leaving() case _ => ko } @@ -184,7 +184,7 @@ class OutfitEventTest extends Specification { "encode Unk3 ABC" in { val msg = OutfitEvent( 2147418113L, - Unk3() + Leaving() ) val pkt = PacketCoding.encodePacket(msg).require.toByteVector @@ -219,8 +219,8 @@ class OutfitEventTest extends Specification { PacketCoding.decodePacket(unk5_ABC).require match { case OutfitEvent(outfit_guid, action) => outfit_guid mustEqual 2147418113L - action mustEqual Unk5( - unk1 = 2, + action mustEqual UpdateMemberCount( + member_count = 2, ) case _ => ko @@ -230,8 +230,8 @@ class OutfitEventTest extends Specification { "encode Unk5 ABC" in { val msg = OutfitEvent( 2147418113L, - Unk5( - unk1 = 2, + UpdateMemberCount( + member_count = 2, ) ) val pkt = PacketCoding.encodePacket(msg).require.toByteVector diff --git a/src/test/scala/game/OutfitMemberEventTest.scala b/src/test/scala/game/OutfitMemberEventTest.scala index 510e7278a..8e0bdf545 100644 --- a/src/test/scala/game/OutfitMemberEventTest.scala +++ b/src/test/scala/game/OutfitMemberEventTest.scala @@ -22,7 +22,7 @@ class OutfitMemberEventTest extends Specification { "decode Lazer padding" in { PacketCoding.decodePacket(Lazer).require match { - case OutfitMemberEvent(outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, padding)) => + case OutfitMemberEvent(outfit_id, member_id, Update(member_name, rank, points, last_login, action, padding)) => outfit_id mustEqual 6418 member_id mustEqual 705344 member_name mustEqual "Lazer1982" @@ -40,7 +40,7 @@ class OutfitMemberEventTest extends Specification { val msg = OutfitMemberEvent( outfit_id = 6418, member_id = 705344, - Unk0( + Update( member_name = "Lazer1982", rank = 7, points = 3134113, @@ -56,7 +56,7 @@ class OutfitMemberEventTest extends Specification { "decode OpolE padding" in { PacketCoding.decodePacket(OpolE).require match { - case OutfitMemberEvent(outfit_id, member_id, Unk0(member_name, rank, points, last_login, action, unk0_padding)) => + case OutfitMemberEvent(outfit_id, member_id, Update(member_name, rank, points, last_login, action, unk0_padding)) => outfit_id mustEqual 6418 member_id mustEqual 42644970 member_name mustEqual "OpolE" @@ -74,7 +74,7 @@ class OutfitMemberEventTest extends Specification { val msg = OutfitMemberEvent( outfit_id = 6418, member_id = 42644970, - Unk0( + Update( member_name = "OpolE", rank = 6, points = 461901, @@ -91,7 +91,7 @@ class OutfitMemberEventTest extends Specification { "decode Unk1" in { PacketCoding.decodePacket(unk1).require match { - case OutfitMemberEvent(outfit_id, member_id, Unk1()) => + case OutfitMemberEvent(outfit_id, member_id, Kicked()) => outfit_id mustEqual 529744 member_id mustEqual 41605263 case _ => @@ -103,7 +103,7 @@ class OutfitMemberEventTest extends Specification { val msg = OutfitMemberEvent( outfit_id = 529744, member_id = 41605263, - Unk1() + Kicked() ) val pkt = PacketCoding.encodePacket(msg).require.toByteVector diff --git a/src/test/scala/game/OutfitMembershipResponseTest.scala b/src/test/scala/game/OutfitMembershipResponseTest.scala index 948cd41cc..dae406cd7 100644 --- a/src/test/scala/game/OutfitMembershipResponseTest.scala +++ b/src/test/scala/game/OutfitMembershipResponseTest.scala @@ -111,7 +111,7 @@ class OutfitMembershipResponseTest extends Specification { "decode unk4" in { PacketCoding.decodePacket(unk4).require match { case OutfitMembershipResponse(packet_type, unk0, unk1, outfit_id, target_id, str1, str2, flag) => - packet_type mustEqual PacketType.Unk4 + packet_type mustEqual PacketType.YouGotKicked unk0 mustEqual 0 unk1 mustEqual 0 outfit_id mustEqual 41593365 @@ -125,7 +125,7 @@ class OutfitMembershipResponseTest extends Specification { } "encode unk4" in { - val msg = OutfitMembershipResponse(PacketType.Unk4, 0, 0, 41593365, 0, "", "", flag = true) + val msg = OutfitMembershipResponse(PacketType.YouGotKicked, 0, 0, 41593365, 0, "", "", flag = true) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual unk4 @@ -134,7 +134,7 @@ class OutfitMembershipResponseTest extends Specification { "decode unk5" in { PacketCoding.decodePacket(unk5).require match { case OutfitMembershipResponse(packet_type, unk0, unk1, outfit_id, target_id, str1, str2, flag) => - packet_type mustEqual PacketType.Kick + packet_type mustEqual PacketType.YouKicked unk0 mustEqual 0 unk1 mustEqual 1 outfit_id mustEqual 41593365 @@ -148,7 +148,7 @@ class OutfitMembershipResponseTest extends Specification { } "encode unk5" in { - val msg = OutfitMembershipResponse(PacketType.Kick, 0, 1, 41593365, 41605263, "PSFoutfittest1", "", flag = true) + val msg = OutfitMembershipResponse(PacketType.YouKicked, 0, 1, 41593365, 41605263, "PSFoutfittest1", "", flag = true) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual unk5 diff --git a/src/test/scala/game/OutfitRequestTest.scala b/src/test/scala/game/OutfitRequestTest.scala index 4c4adcb11..67cbd62a7 100644 --- a/src/test/scala/game/OutfitRequestTest.scala +++ b/src/test/scala/game/OutfitRequestTest.scala @@ -46,7 +46,7 @@ class OutfitRequestTest extends Specification { "decode Unk3" in { PacketCoding.decodePacket(string6).require match { - case OutfitRequest(id, OutfitRequestAction.Unk3(value)) => + case OutfitRequest(id, OutfitRequestAction.OutfitWindowOpen(value)) => id mustEqual 1176612L value mustEqual true case _ => @@ -56,7 +56,7 @@ class OutfitRequestTest extends Specification { "decode Unk4" in { PacketCoding.decodePacket(string8).require match { - case OutfitRequest(id, OutfitRequestAction.Unk4(value)) => + case OutfitRequest(id, OutfitRequestAction.OutfitListWindowOpen(value)) => id mustEqual 41588237L value mustEqual true case _ => @@ -88,14 +88,14 @@ class OutfitRequestTest extends Specification { } "encode Unk3" in { - val msg = OutfitRequest(1176612L, OutfitRequestAction.Unk3(true)) + val msg = OutfitRequest(1176612L, OutfitRequestAction.OutfitWindowOpen(true)) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual string6 } "encode Unk4" in { - val msg = OutfitRequest(41588237L, OutfitRequestAction.Unk4(true)) + val msg = OutfitRequest(41588237L, OutfitRequestAction.OutfitListWindowOpen(true)) val pkt = PacketCoding.encodePacket(msg).require.toByteVector pkt mustEqual string8