mirror of
https://github.com/psforever/PSF-LoginServer.git
synced 2026-01-19 18:44:45 +00:00
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:
parent
a5a232ffdc
commit
66f45edcd3
|
|
@ -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 =>
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]()
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) =>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ object InventoryData {
|
|||
}
|
||||
).xmap[InventoryData](
|
||||
{
|
||||
case _ :: 0 :: c :: HNil =>
|
||||
case _ :: _ :: c :: HNil =>
|
||||
InventoryData(c)
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 =>
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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(_) =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue