Vehicle Gating with Passengers (#1072)

* guarding against match errors in various places; increased frequency of psm array purge; fixed issues with seated passengers after vehicle gating

* due to increased frequency of clearing psm data, changed from Array to LongMap
This commit is contained in:
Fate-JH 2023-05-02 15:09:17 -04:00 committed by GitHub
parent a5a232ffdc
commit 66f45edcd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 61 additions and 44 deletions

View file

@ -717,6 +717,7 @@ class ChatActor(
sessionActor ! SessionActor.SendResponse(
message.copy(messageType = UNK_229, contents = "@CMT_ZONE_usage")
)
case _ => ()
}
case (CMT_WARP, _, contents) if gmCommandAllowed =>

View file

@ -5,6 +5,7 @@ import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, typed}
import net.psforever.services.Service
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
//
@ -32,9 +33,8 @@ class SessionAvatarHandlers(
implicit val context: ActorContext
) extends CommonSessionInterfacingFunctionality {
//TODO player characters only exist within a certain range of GUIDs for a given zone; this is overkill
private[support] var lastSeenStreamMessage: Array[SessionAvatarHandlers.LastUpstream] = {
SessionAvatarHandlers.blankUpstreamMessages(65535)
}
private[support] var lastSeenStreamMessage: mutable.LongMap[SessionAvatarHandlers.LastUpstream] =
mutable.LongMap[SessionAvatarHandlers.LastUpstream]()
private[this] val hidingPlayerRandomizer = new scala.util.Random
/**
@ -73,8 +73,8 @@ class SessionAvatarHandlers(
canSeeReallyFar
) if isNotSameTarget =>
val pstateToSave = pstate.copy(timestamp = 0)
val (lastMsg, lastTime, lastPosition, wasVisible) = lastSeenStreamMessage(guid.guid) match {
case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, visible)
val (lastMsg, lastTime, lastPosition, wasVisible) = lastSeenStreamMessage.get(guid.guid) match {
case Some(SessionAvatarHandlers.LastUpstream(Some(msg), visible, time)) => (Some(msg), time, msg.pos, visible)
case _ => (None, 0L, Vector3.Zero, false)
}
val drawConfig = Config.app.game.playerDraw //m
@ -129,10 +129,10 @@ class SessionAvatarHandlers(
isCloaking
)
)
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, now)
lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, now))
} else {
//is visible, but skip reinforcement
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, lastTime)
lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=true, lastTime))
}
} else {
//conditions where the target is not currently visible
@ -151,10 +151,10 @@ class SessionAvatarHandlers(
is_cloaked = isCloaking
)
)
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now)
lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now))
} else {
//skip drawing altogether
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, lastTime)
lastSeenStreamMessage.put(guid.guid, SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, lastTime))
}
}
@ -549,7 +549,7 @@ class SessionAvatarHandlers(
continent.GUID(weaponGuid).collect {
case tool: Tool if tool.Magazine == 0 =>
// check that the magazine is still empty before sending WeaponDryFireMessage
// if it has been reloaded since then, other clients not see it firing
// if it has been reloaded since then, other clients will not see it firing
sendResponse(WeaponDryFireMessage(weaponGuid))
case _ =>
sendResponse(WeaponDryFireMessage(weaponGuid))
@ -562,8 +562,4 @@ class SessionAvatarHandlers(
object SessionAvatarHandlers {
private[support] case class LastUpstream(msg: Option[AvatarResponse.PlayerState], visible: Boolean, time: Long)
private[support] def blankUpstreamMessages(n: Int): Array[LastUpstream] = {
Array.fill[SessionAvatarHandlers.LastUpstream](n)(elem = LastUpstream(None, visible=false, 0L))
}
}

View file

@ -732,18 +732,19 @@ class ZoningOperations(
(manifest.passengers.find {
_.name.equals(playerName)
} match {
case Some(entry) if vehicle.Seats(entry.mount).occupant.isEmpty =>
case Some(entry) if vehicle.Seat(entry.mount).flatMap { _.occupant }.isEmpty =>
player.VehicleSeated = None
vehicle.Seats(entry.mount).mount(player)
player.VehicleSeated = vehicle.GUID
Some(vehicle)
case Some(entry) if vehicle.Seats(entry.mount).occupant.contains(player) =>
Some(vehicle)
Some((None, Some(vehicle)))
case Some(entry) if vehicle.Seat(entry.mount).flatMap { _.occupant }.contains(player) =>
player.VehicleSeated = vehicle.GUID
Some((None, Some(vehicle)))
case Some(entry) =>
log.warn(
s"TransferPassenger: $playerName tried to mount seat ${entry.mount} during summoning, but it was already occupied, and ${player.Sex.pronounSubject} was rebuked"
)
None
Some((None, None))
case None =>
//log.warn(s"TransferPassenger: $playerName is missing from the manifest of a summoning ${vehicle.Definition.Name} from ${vehicle.Zone.id}")
None
@ -753,8 +754,8 @@ class ZoningOperations(
} match {
case Some(entry) =>
vehicle.CargoHolds(entry.mount).occupant match {
case out@Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
out
case out @ Some(cargo) if cargo.Seats(0).occupants.exists(_.Name.equals(playerName)) =>
Some((Some(vehicle), out))
case _ =>
None
}
@ -762,13 +763,11 @@ class ZoningOperations(
None
}
} match {
case Some(cargo: Vehicle) =>
galaxyService ! Service.Leave(Some(temp_channel)) //temporary vehicle-specific channel (see above)
spawn.deadState = DeadState.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true))
interstellarFerry = Some(cargo) //on the other continent and registered to that continent's GUID system
cargo.MountedIn = vehicle.GUID
spawn.LoadZonePhysicalSpawnPoint(cargo.Continent, cargo.Position, cargo.Orientation, respawnTime = 1 seconds, None)
case Some((Some(ferry), Some(cargo))) =>
cargo.MountedIn = ferry.GUID
handleTransferPassengerVehicle(cargo, temp_channel)
case Some((None, Some(_: Vehicle))) =>
handleTransferPassengerVehicle(vehicle, temp_channel)
case _ =>
interstellarFerry match {
case None =>
@ -779,6 +778,14 @@ class ZoningOperations(
}
}
private def handleTransferPassengerVehicle(vehicle: Vehicle, temporaryChannel: String): Unit = {
galaxyService ! Service.Leave(Some(temporaryChannel)) //temporary vehicle-specific channel (see above)
spawn.deadState = DeadState.Release
sendResponse(AvatarDeadStateMessage(DeadState.Release, 0, 0, player.Position, player.Faction, unk5=true))
interstellarFerry = Some(vehicle) //on the other continent and registered to that continent's GUID system
spawn.LoadZonePhysicalSpawnPoint(vehicle.Continent, vehicle.Position, vehicle.Orientation, respawnTime = 1 seconds, None)
}
def handleDroppodLaunchDenial(errorCode: DroppodError): Unit = {
sendResponse(DroppodLaunchResponseMessage(errorCode, player.GUID))
}
@ -1446,7 +1453,6 @@ class ZoningOperations(
Deployables.Disown(continent, avatar, context.self)
spawn.drawDeloyableIcon = spawn.RedrawDeployableIcons //important for when SetCurrentAvatar initializes the UI next zone
sessionData.squad.squadSetup = sessionData.squad.ZoneChangeSquadSetup
sessionData.avatarResponse.lastSeenStreamMessage = SessionAvatarHandlers.blankUpstreamMessages(65535)
}
/**
@ -1923,6 +1929,7 @@ class ZoningOperations(
if (zoningStatus == Zoning.Status.Deconstructing) {
sessionData.stopDeconstructing()
}
sessionData.avatarResponse.lastSeenStreamMessage.clear()
upstreamMessageCount = 0
setAvatar = false
sessionData.persist()
@ -1956,6 +1963,7 @@ class ZoningOperations(
if (zoningStatus == Zoning.Status.Deconstructing) {
sessionData.stopDeconstructing()
}
sessionData.avatarResponse.lastSeenStreamMessage.clear()
upstreamMessageCount = 0
setAvatar = false
sessionData.persist()

View file

@ -804,8 +804,8 @@ object WorldSession {
val name = definition.Name
GlobalDefinitions.isGrenade(definition) && (name.contains("frag") || name.contains("plasma"))
} match {
case Some(InventoryItem(equipment, slotNum)) =>Some(equipment.asInstanceOf[Tool], slotNum)
case None => None
case Some(InventoryItem(equipment, slotNum)) => Some(equipment.asInstanceOf[Tool], slotNum)
case _ => None
}
}
}

View file

@ -86,7 +86,7 @@ class GridInventory extends Container {
items.values.find({ case InventoryItem(obj, _) => obj.HasGUID && obj.GUID == guid }) match {
case Some(InventoryItem(_, index)) =>
Some(index)
case None =>
case _ =>
None
}
}

View file

@ -69,7 +69,7 @@ trait SectorTraits {
* @param eqFunc a custom equivalence function to distinguish between the entities in the list
* @tparam A the type of object that will be the entities stored in the list
*/
class SectorListOf[A](eqFunc: (A, A) => Boolean = (a: A, b: A) => a equals b) {
class SectorListOf[A](eqFunc: (A, A) => Boolean = (a: A, b: A) => a == b) {
private val internalList: ListBuffer[A] = ListBuffer[A]()
/**

View file

@ -59,6 +59,7 @@ object LoginMessage extends Marshallable[LoginMessage] {
a match {
case username :: Some(password) :: None :: HNil => Left(username :: password :: HNil)
case username :: None :: Some(token) :: HNil => Right(token :: username :: HNil)
case username :: _ :: _ :: HNil => Right(username :: "" :: HNil) //this will fail
}
either(bool, passwordPath, tokenPath).xmap[Struct](from, to)

View file

@ -166,6 +166,9 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] {
case target :: _ :: Some(first) :: Some(other) :: HNil =>
GamePropertyTarget(target, first +: other)
case target :: _ :: _ :: Some(other) :: HNil =>
GamePropertyTarget(target, other)
},
{
case GamePropertyTarget(target, list) =>
@ -202,6 +205,9 @@ object PropertyOverrideMessage extends Marshallable[PropertyOverrideMessage] {
case zone :: _ :: Some(first) :: Some(other) :: HNil =>
GamePropertyScope(zone, first +: other)
case zone :: _ :: None :: Some(other) :: HNil =>
GamePropertyScope(zone, other)
},
{
case GamePropertyScope(zone, list) =>

View file

@ -48,7 +48,7 @@ object InventoryData {
}
).xmap[InventoryData](
{
case _ :: 0 :: c :: HNil =>
case _ :: _ :: c :: HNil =>
InventoryData(c)
},
{

View file

@ -29,7 +29,7 @@ object LittleBuddyProjectileData extends Marshallable[LittleBuddyProjectileData]
("unk4" | bool)
).exmap[LittleBuddyProjectileData](
{
case data :: u2 :: 31 :: u4 :: HNil =>
case data :: u2 :: _ :: u4 :: HNil =>
Attempt.successful(LittleBuddyProjectileData(data, u2, u4))
},
{

View file

@ -3,10 +3,10 @@ package net.psforever.packet.game.objectcreate
import net.psforever.packet.PacketHelpers
import net.psforever.types.PlanetSideGUID
import scodec.Attempt.Successful
import scodec.Codec
import scodec.Attempt.{Failure, Successful}
import scodec.{Codec, Err}
import scodec.codecs._
import shapeless.HNil //note: do not import shapeless.:: at top level; it messes up List's :: functionality
import shapeless.HNil
import scala.collection.mutable.ListBuffer
@ -222,6 +222,9 @@ object MountableInventory {
case _ :: slot :: Some(next) :: HNil =>
Successful(Some(InventorySeat(slot, next)))
case _ =>
Failure(Err("bad seat decoding"))
},
{
case None =>

View file

@ -147,7 +147,7 @@ object ObjectCreateBase {
cls :: guid :: None :: HNil
},
{
case cls :: guid :: None :: HNil =>
case cls :: guid :: _ :: HNil =>
cls :: guid :: HNil
}
)
@ -157,17 +157,19 @@ object ObjectCreateBase {
*/
private val parent: Codec[parentPattern] = (
("parentGuid" | PlanetSideGUID.codec) :: //16u
("objectClass" | uintL(0xb)) :: //11u
("objectClass" | uintL(0xb)) :: //11u
("guid" | PlanetSideGUID.codec) :: //16u
("parentSlotIndex" | PacketHelpers.encodedStringSize) //8u or 16u
).xmap[parentPattern](
).exmap[parentPattern](
{
case pguid :: cls :: guid :: slot :: HNil =>
cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil
Attempt.Successful(cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil)
},
{
case cls :: guid :: Some(ObjectCreateMessageParent(pguid, slot)) :: HNil =>
pguid :: cls :: guid :: slot :: HNil
Attempt.Successful(pguid :: cls :: guid :: slot :: HNil)
case _ =>
Attempt.Failure(Err("expecting parent data, but found none"))
}
)

View file

@ -642,7 +642,7 @@ class SquadService extends Actor {
val squad = features.Squad
LeaveSquad(squad.Membership(position).CharId, features)
out
case None =>
case _ =>
None
}
case FindLfsSoldiersForRole(_) =>