outfit persistence and minor fixes

This commit is contained in:
ScrawnyRonnie 2025-08-29 22:05:40 -04:00
parent 18dd426d13
commit 16900cd918
5 changed files with 79 additions and 19 deletions

View file

@ -10,7 +10,7 @@ import net.psforever.objects.serverobject.containable.ContainableBehavior
import net.psforever.objects.serverobject.mount.Mountable
import net.psforever.objects.sourcing.PlayerSource
import net.psforever.objects.vital.interaction.Adversarial
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction}
import net.psforever.packet.game.{AvatarImplantMessage, CreateShortcutMessage, ImplantAction, PlanetsideStringAttributeMessage}
import net.psforever.services.avatar.{AvatarAction, AvatarServiceMessage}
import net.psforever.types.ImplantType
@ -252,6 +252,9 @@ class AvatarHandlerLogic(val ops: SessionAvatarHandlers, implicit val context: A
case AvatarResponse.PlanetsideAttributeSelf(attributeType, attributeValue) if isSameTarget =>
sendResponse(PlanetsideAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.PlanetsideStringAttribute(attributeType, attributeValue) =>
sendResponse(PlanetsideStringAttributeMessage(guid, attributeType, attributeValue))
case AvatarResponse.GenericObjectAction(objectGuid, actionCode) if isNotSameTarget =>
sendResponse(GenericObjectActionMessage(objectGuid, actionCode))

View file

@ -83,10 +83,10 @@ object SessionOutfitHandlers {
player.outfit_name = outfit.name
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id,
AvatarAction.PlanetsideAttributeToAll(player.GUID, 39, player.outfit_id))
AvatarAction.PlanetsideAttributeToAll(player.GUID, 39, outfit.id))
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id,
AvatarAction.PlanetsideStringAttribute(player.GUID, 0, player.outfit_name))
AvatarAction.PlanetsideStringAttribute(player.GUID, 0, outfit.name))
session.chat.JoinChannel(OutfitChannel(player.outfit_id))
}
@ -168,10 +168,10 @@ object SessionOutfitHandlers {
invited.outfit_name = outfit.name
invited.Zone.AvatarEvents ! AvatarServiceMessage(invited.Zone.id,
AvatarAction.PlanetsideAttributeToAll(invited.GUID, 39, invited.outfit_id))
AvatarAction.PlanetsideAttributeToAll(invited.GUID, 39, outfit.id))
invited.Zone.AvatarEvents ! AvatarServiceMessage(invited.Zone.id,
AvatarAction.PlanetsideStringAttribute(invited.GUID, 0, invited.outfit_name))
AvatarAction.PlanetsideStringAttribute(invited.GUID, 0, outfit.name))
case (None, _, _) =>
PlayerControl.sendResponse(invited.Zone, invited.Name,
@ -223,14 +223,11 @@ object SessionOutfitHandlers {
kickedBy.outfit_name = ""
kickedBy.outfit_id = 0
val pZone = zones.filter(z => z.id == kickedBy.Zone.id).head
pZone.AllPlayers.filter(p => p.Faction == kickedBy.Faction).foreach { friendly =>
PlayerControl.sendResponse(friendly.Zone, friendly.Name,
PlanetsideAttributeMessage(kickedBy.GUID, 39, 0))
kickedBy.Zone.AvatarEvents ! AvatarServiceMessage(kickedBy.Zone.id,
AvatarAction.PlanetsideAttributeToAll(kickedBy.GUID, 39, 0))
PlayerControl.sendResponse(friendly.Zone, friendly.Name,
PlanetsideStringAttributeMessage(kickedBy.GUID, 0, ""))
}
kickedBy.Zone.AvatarEvents ! AvatarServiceMessage(kickedBy.Zone.id,
AvatarAction.PlanetsideStringAttribute(kickedBy.GUID, 0, ""))
}
}.recover { case e =>
e.printStackTrace()
@ -426,11 +423,8 @@ object SessionOutfitHandlers {
val outfit_id = player.outfit_id
// update MOTD
updateOutfitMotd(outfit_id, message)
// TODO this does not notify clients with open windows. Do they update in the first place?
val outfitDetails = for {
_ <- updateOutfitMotd(outfit_id, message)
outfitOpt <- ctx.run(getOutfitById(outfit_id)).map(_.headOption)
memberCount <- ctx.run(query[Outfitmember].filter(_.outfit_id == lift(outfit_id)).size)
pointsTotal <- ctx.run(querySchema[OutfitpointMv]("outfitpoint_mv").filter(_.outfit_id == lift(outfit_id)))
@ -487,6 +481,56 @@ object SessionOutfitHandlers {
// S >> C OutfitEvent(Unk2, 529744, Unk2(OutfitInfo(PlanetSide_Forever_Vanu, 0, 0, 3, OutfitRankNames(, , , , , , , ), Vanu outfit for the planetside forever project! -find out more about the PSEMU project at PSforever.net, 0, 1, 0, 1458331641, 0, 0, 0)))
}
def HandleLoginOutfitCheck(player: Player, session: SessionData): Unit = {
ctx.run(getOutfitOnLogin(player.avatar.id)).flatMap { memberships =>
memberships.headOption match {
case Some(membership) =>
val outfitId = membership.outfit_id
(for {
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) =>
val seconds: Long = outfit.created.atZone(java.time.ZoneOffset.UTC).toInstant.toEpochMilli / 1000
PlayerControl.sendResponse(player.Zone, player.Name,
OutfitEvent(outfitId, Unk2(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(""),
outfit.rank6.getOrElse(""), outfit.rank7.getOrElse("")),
outfit.motd.getOrElse(""),
14, unk11 = true, 0, seconds, 0, 0, 0))))
PlayerControl.sendResponse(player.Zone, player.Name,
OutfitMemberUpdate(outfit.id, player.CharId, membership.rank, flag = true))
session.chat.JoinChannel(OutfitChannel(outfit.id))
player.outfit_id = outfit.id
player.outfit_name = outfit.name
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id,
AvatarAction.PlanetsideAttributeToAll(player.GUID, 39, outfit.id))
player.Zone.AvatarEvents ! AvatarServiceMessage(player.Zone.id,
AvatarAction.PlanetsideStringAttribute(player.GUID, 0, outfit.name))
case (None, _, _) =>
PlayerControl.sendResponse(player.Zone, player.Name,
ChatMsg(ChatMessageType.UNK_227, "Failed to load outfit"))
}
.recover { case _ =>
PlayerControl.sendResponse(player.Zone, player.Name,
ChatMsg(ChatMessageType.UNK_227, "Failed to load outfit"))
}
case None =>
Future.successful(())
}
}
}
/* supporting functions */
def sanitizeOutfitName(name: String): Option[String] = {
@ -605,6 +649,10 @@ object SessionOutfitHandlers {
querySchema[OutfitpointMv]("outfitpoint_mv").filter(_.outfit_id == lift(id))
}
def getOutfitOnLogin(avatarId: Long): Quoted[EntityQuery[Outfitmember]] = quote {
query[Outfitmember].filter(_.avatar_id == lift(avatarId))
}
def getOutfitMembersWithDetails(outfitId: Long): Quoted[Query[(Long, String, Long, Int, LocalDateTime)]] = quote {
query[Outfitmember]
.filter(_.outfit_id == lift(outfitId))

View file

@ -2532,6 +2532,7 @@ class ZoningOperations(
sessionLogic.general.toggleTeleportSystem(obj, TelepadLike.AppraiseTeleportationSystem(obj, continent))
}
}
SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic)
//make weather happen
sendResponse(WeatherMessage(List(),List(
StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217),
@ -2655,6 +2656,7 @@ class ZoningOperations(
log.debug(s"AvatarRejoin: ${player.Name} - $guid -> $data")
}
setupAvatarFunc = AvatarCreate
SessionOutfitHandlers.HandleLoginOutfitCheck(player, sessionLogic)
//make weather happen
sendResponse(WeatherMessage(List(),List(
StormInfo(Vector3(0.1f, 0.15f, 0.0f), 240, 217),
@ -3196,8 +3198,6 @@ class ZoningOperations(
continent.AllPlayers.filter(_.ExoSuit == ExoSuitType.MAX).foreach(max => sendResponse(PlanetsideAttributeMessage(max.GUID, 4, max.Armor)))
// AvatarAwardMessage
//populateAvatarAwardRibbonsFunc(1, 20L)
sendResponse(PlanetsideStringAttributeMessage(guid, 0, "Outfit Name"))
//squad stuff (loadouts, assignment)
sessionLogic.squad.squadSetup()
//MapObjectStateBlockMessage and ObjectCreateMessage?

View file

@ -198,6 +198,14 @@ class AvatarService(zone: Zone) extends Actor {
AvatarResponse.PlanetsideAttributeSelf(attribute_type, attribute_value)
)
)
case AvatarAction.PlanetsideStringAttribute(guid, attribute_type, attribute_value) =>
AvatarEvents.publish(
AvatarServiceResponse(
s"/$forChannel/Avatar",
guid,
AvatarResponse.PlanetsideStringAttribute(attribute_type, attribute_value)
)
)
case AvatarAction.PlayerState(
guid,
pos,

View file

@ -47,7 +47,7 @@ object AvatarResponse {
final case class EquipmentInHand(pkt: ObjectCreateMessage) extends Response
final case class GenericObjectAction(object_guid: PlanetSideGUID, action_code: Int) extends Response
final case class HitHint(source_guid: PlanetSideGUID) extends Response
final case class Killed(cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends Response
final case class Killed(cause: DamageResult, mount_guid: Option[PlanetSideGUID]) extends Response
final case class LoadPlayer(pkt: ObjectCreateMessage) extends Response
final case class LoadProjectile(pkt: ObjectCreateMessage) extends Response
final case class ObjectDelete(item_guid: PlanetSideGUID, unk: Int) extends Response
@ -56,6 +56,7 @@ object AvatarResponse {
final case class PlanetsideAttribute(attribute_type: Int, attribute_value: Long) extends Response
final case class PlanetsideAttributeToAll(attribute_type: Int, attribute_value: Long) extends Response
final case class PlanetsideAttributeSelf(attribute_type: Int, attribute_value: Long) extends Response
final case class PlanetsideStringAttribute(attribute_type: Int, attribute_value: String) extends Response
final case class PlayerState(
pos: Vector3,
vel: Option[Vector3],