diff --git a/src/main/scala/net/psforever/actors/session/ChatActor.scala b/src/main/scala/net/psforever/actors/session/ChatActor.scala index e15ec2861..c8c06bf0d 100644 --- a/src/main/scala/net/psforever/actors/session/ChatActor.scala +++ b/src/main/scala/net/psforever/actors/session/ChatActor.scala @@ -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 => diff --git a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala index 06d7e44f1..0a9d2de4e 100644 --- a/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala +++ b/src/main/scala/net/psforever/actors/session/support/SessionAvatarHandlers.scala @@ -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)) - } } diff --git a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala index 41264f420..856eaf444 100644 --- a/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala +++ b/src/main/scala/net/psforever/actors/session/support/ZoningOperations.scala @@ -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() diff --git a/src/main/scala/net/psforever/login/WorldSession.scala b/src/main/scala/net/psforever/login/WorldSession.scala index 8701d74fe..4a03211b4 100644 --- a/src/main/scala/net/psforever/login/WorldSession.scala +++ b/src/main/scala/net/psforever/login/WorldSession.scala @@ -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 } } } diff --git a/src/main/scala/net/psforever/objects/inventory/GridInventory.scala b/src/main/scala/net/psforever/objects/inventory/GridInventory.scala index 1a51ae709..eafc880bb 100644 --- a/src/main/scala/net/psforever/objects/inventory/GridInventory.scala +++ b/src/main/scala/net/psforever/objects/inventory/GridInventory.scala @@ -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 } } diff --git a/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala b/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala index 47e122a2f..60ce74e65 100644 --- a/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala +++ b/src/main/scala/net/psforever/objects/zones/blockmap/Sector.scala @@ -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]() /** diff --git a/src/main/scala/net/psforever/packet/game/LoginMessage.scala b/src/main/scala/net/psforever/packet/game/LoginMessage.scala index c5fcd8be3..c3dafce7f 100644 --- a/src/main/scala/net/psforever/packet/game/LoginMessage.scala +++ b/src/main/scala/net/psforever/packet/game/LoginMessage.scala @@ -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) diff --git a/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala b/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala index 4ed688d80..7ff89ec07 100644 --- a/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala +++ b/src/main/scala/net/psforever/packet/game/PropertyOverrideMessage.scala @@ -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) => diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala index 414e27fb6..fc8835c72 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/InventoryData.scala @@ -48,7 +48,7 @@ object InventoryData { } ).xmap[InventoryData]( { - case _ :: 0 :: c :: HNil => + case _ :: _ :: c :: HNil => InventoryData(c) }, { diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala b/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala index a47930061..af167b64c 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/LittleBuddyProjectileData.scala @@ -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)) }, { diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/MountableInventory.scala b/src/main/scala/net/psforever/packet/game/objectcreate/MountableInventory.scala index b9e69f2d1..8e883c7ba 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/MountableInventory.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/MountableInventory.scala @@ -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 => diff --git a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectCreateBase.scala b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectCreateBase.scala index 3ba1ed4b9..f3e0c893c 100644 --- a/src/main/scala/net/psforever/packet/game/objectcreate/ObjectCreateBase.scala +++ b/src/main/scala/net/psforever/packet/game/objectcreate/ObjectCreateBase.scala @@ -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")) } ) diff --git a/src/main/scala/net/psforever/services/teamwork/SquadService.scala b/src/main/scala/net/psforever/services/teamwork/SquadService.scala index b8810deeb..b2149fa84 100644 --- a/src/main/scala/net/psforever/services/teamwork/SquadService.scala +++ b/src/main/scala/net/psforever/services/teamwork/SquadService.scala @@ -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(_) =>