You Can (Not) Hold (#1034)

* dropping the medical applicator on rejoin logins; not actually a bug fix, but a bug mitigation

* narrowing the scope of the guard boolean; correcting an issue with purchase timers
This commit is contained in:
Fate-JH 2023-02-23 00:26:40 -05:00 committed by GitHub
parent 690d7ec948
commit 0b8ff5a4ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 55 deletions

View file

@ -1446,9 +1446,8 @@ class AvatarActor(
name, name,
cooldown.toSeconds, cooldown.toSeconds,
item match { item match {
case t: ToolDefinition => GlobalDefinitions.isMaxArms(t) case _: KitDefinition => false
case _: VehicleDefinition => true case _ => true
case _ => false
} }
) )
case _ => ; case _ => ;
@ -2573,9 +2572,8 @@ class AvatarActor(
name, name,
cooldown.toSeconds - secondsSincePurchase, cooldown.toSeconds - secondsSincePurchase,
obj match { obj match {
case t: ToolDefinition => GlobalDefinitions.isMaxArms(t) case _: KitDefinition => false
case _: VehicleDefinition => true case _ => true
case _ => false
} }
) )
@ -2591,9 +2589,9 @@ class AvatarActor(
} }
} }
def updatePurchaseTimer(name: String, time: Long, isActuallyAMachine: Boolean): Unit = { def updatePurchaseTimer(name: String, time: Long, isNotAMedKit: Boolean): Unit = {
sessionActor ! SessionActor.SendResponse( sessionActor ! SessionActor.SendResponse(
AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isActuallyAMachine) AvatarVehicleTimerMessage(session.get.player.GUID, name, time, isNotAMedKit)
) )
} }

View file

@ -6,6 +6,8 @@ import akka.actor.typed.scaladsl.adapter._
import akka.actor.{ActorContext, ActorRef, Cancellable, typed} import akka.actor.{ActorContext, ActorRef, Cancellable, typed}
import akka.pattern.ask import akka.pattern.ask
import akka.util.Timeout import akka.util.Timeout
import net.psforever.login.WorldSession
import net.psforever.objects.inventory.InventoryItem
import net.psforever.objects.serverobject.mount.Seat import net.psforever.objects.serverobject.mount.Seat
import net.psforever.objects.serverobject.tube.SpawnTube import net.psforever.objects.serverobject.tube.SpawnTube
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource} import net.psforever.objects.sourcing.{PlayerSource, SourceEntry, VehicleSource}
@ -559,19 +561,15 @@ class ZoningOperations(
zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) }) zone.Buildings.foreach({ case (_, building) => initBuilding(continentNumber, building.MapId, building) })
sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO)) sendResponse(ZonePopulationUpdateMessage(continentNumber, 414, 138, popTR, 138, popNC, 138, popVS, 138, popBO))
//TODO should actually not claim that the sanctuary or VR zones are locked by their respective empire
if (continentNumber == 11) if (continentNumber == 11)
sendResponse( sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC))
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NC)
) // "The NC have captured the NC Sanctuary."
else if (continentNumber == 12) else if (continentNumber == 12)
sendResponse( sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR))
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.TR)
) // "The TR have captured the TR Sanctuary."
else if (continentNumber == 13) else if (continentNumber == 13)
sendResponse( sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS))
ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.VS) else
) // "The VS have captured the VS Sanctuary." sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
else sendResponse(ContinentalLockUpdateMessage(continentNumber, PlanetSideEmpire.NEUTRAL))
//CaptureFlagUpdateMessage() //CaptureFlagUpdateMessage()
//VanuModuleUpdateMessage() //VanuModuleUpdateMessage()
//ModuleLimitsMessage() //ModuleLimitsMessage()
@ -1804,6 +1802,7 @@ class ZoningOperations(
session = session.copy(player = p, avatar = a) session = session.copy(player = p, avatar = a)
sessionData.persist() sessionData.persist()
setupAvatarFunc = AvatarRejoin setupAvatarFunc = AvatarRejoin
dropMedicalApplicators(p)
avatarActor ! AvatarActor.ReplaceAvatar(a) avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a) avatarLoginResponse(a)
@ -1813,6 +1812,7 @@ class ZoningOperations(
deadState = DeadState.Dead deadState = DeadState.Dead
session = session.copy(player = p, avatar = a) session = session.copy(player = p, avatar = a)
sessionData.persist() sessionData.persist()
dropMedicalApplicators(p)
HandleReleaseAvatar(p, inZone) HandleReleaseAvatar(p, inZone)
avatarActor ! AvatarActor.ReplaceAvatar(a) avatarActor ! AvatarActor.ReplaceAvatar(a)
avatarLoginResponse(a) avatarLoginResponse(a)
@ -1894,8 +1894,12 @@ class ZoningOperations(
} }
def handleNewPlayerLoaded(tplayer: Player): Unit = { def handleNewPlayerLoaded(tplayer: Player): Unit = {
//new zone /* new zone, might be on `tplayer.Zone` but should definitely be on `session` */
log.info(s"${tplayer.Name} has spawned into ${session.zone.id}") val zone = session.zone
val id = zone.id
val map = zone.map
val mapName = map.name
log.info(s"${tplayer.Name} has spawned into $id")
sessionData.oldRefsMap.clear() sessionData.oldRefsMap.clear()
sessionData.persist = UpdatePersistenceAndRefs sessionData.persist = UpdatePersistenceAndRefs
tplayer.avatar = avatar tplayer.avatar = avatar
@ -1903,18 +1907,8 @@ class ZoningOperations(
avatarActor ! AvatarActor.CreateImplants() avatarActor ! AvatarActor.CreateImplants()
avatarActor ! AvatarActor.InitializeImplants() avatarActor ! AvatarActor.InitializeImplants()
//LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar //LoadMapMessage causes the client to send BeginZoningMessage, eventually leading to SetCurrentAvatar
val weaponsEnabled = val weaponsEnabled = !(mapName.equals("map11") || mapName.equals("map12") || mapName.equals("map13"))
session.zone.map.name != "map11" && session.zone.map.name != "map12" && session.zone.map.name != "map13" sendResponse(LoadMapMessage(mapName, id, 40100, 25, weaponsEnabled, map.checksum))
sendResponse(
LoadMapMessage(
session.zone.map.name,
session.zone.id,
40100,
25,
weaponsEnabled,
session.zone.map.checksum
)
)
if (isAcceptableNextSpawnPoint) { if (isAcceptableNextSpawnPoint) {
//important! the LoadMapMessage must be processed by the client before the avatar is created //important! the LoadMapMessage must be processed by the client before the avatar is created
setupAvatarFunc() setupAvatarFunc()
@ -1934,7 +1928,7 @@ class ZoningOperations(
} else { } else {
//look for different spawn point in same zone //look for different spawn point in same zone
cluster ! ICS.GetNearbySpawnPoint( cluster ! ICS.GetNearbySpawnPoint(
session.zone.Number, zone.Number,
tplayer, tplayer,
Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS), Seq(SpawnGroup.Facility, SpawnGroup.Tower, SpawnGroup.AMS),
context.self context.self
@ -1974,6 +1968,25 @@ class ZoningOperations(
/* support functions */ /* support functions */
private def dropMedicalApplicators(p: Player): Unit = {
WorldSession.DropLeftovers(p)(
(p.Holsters().zipWithIndex.collect { case (slot, index) if slot.Equipment.nonEmpty => InventoryItem(slot.Equipment.get, index) } ++
p.Inventory.Items ++
p.FreeHand.Equipment.flatMap { item => Some(InventoryItem(item, Player.FreeHandSlot)) }.toList)
.collect {
case entry @ InventoryItem(equipment, index)
if equipment.Definition == GlobalDefinitions.medicalapplicator && p.DrawnSlot == index =>
p.Slot(index).Equipment = None
p.DrawnSlot = Player.HandsDownSlot
entry
case entry @ InventoryItem(equipment, index)
if equipment.Definition == GlobalDefinitions.medicalapplicator =>
p.Slot(index).Equipment = None
entry
}
)
}
def isAcceptableNextSpawnPoint: Boolean = isAcceptableSpawnPoint(nextSpawnPoint) def isAcceptableNextSpawnPoint: Boolean = isAcceptableSpawnPoint(nextSpawnPoint)
def isAcceptableSpawnPoint(spawnPoint: SpawnPoint): Boolean = isAcceptableSpawnPoint(Some(spawnPoint)) def isAcceptableSpawnPoint(spawnPoint: SpawnPoint): Boolean = isAcceptableSpawnPoint(Some(spawnPoint))

View file

@ -959,7 +959,7 @@ object WorldSession {
* @param container the original object that contained the items * @param container the original object that contained the items
* @param drops the items to be dropped on the ground * @param drops the items to be dropped on the ground
*/ */
def DropLeftovers(container: PlanetSideServerObject with Container)(drops: List[InventoryItem]): Unit = { def DropLeftovers(container: PlanetSideServerObject with Container)(drops: Iterable[InventoryItem]): Unit = {
//drop or retire //drop or retire
val zone = container.Zone val zone = container.Zone
val pos = container.Position val pos = container.Position

View file

@ -7,10 +7,13 @@ import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/** /**
* @param player_guid player guid * @param player_guid player guid
* @param text internal name of the item or vehicle name, e.g., medkit, fury, trhev_antipersonnel * @param text internal name of the item or vehicle name, e.g., medkit, fury, trhev_antipersonnel
* @param time cooldown/delay in seconds * @param time cooldown/delay in seconds
* @param unk `true` for vehicles and max exo-suits; `false` for other items * @param unk unk;
* most likely has to do with the visibility of the timer in equipment purchasing;
* `false` for kit items;
* `true` for almost everything else
*/ */
final case class AvatarVehicleTimerMessage(player_guid: PlanetSideGUID, text: String, time: Long, unk: Boolean) final case class AvatarVehicleTimerMessage(player_guid: PlanetSideGUID, text: String, time: Long, unk: Boolean)
extends PlanetSideGamePacket { extends PlanetSideGamePacket {

View file

@ -6,22 +6,29 @@ import scodec.Codec
import scodec.codecs._ import scodec.codecs._
/** /**
* map_name and nav_map_name should match (unless you want to be lost :)) * Dispatched from server to client to instigate a zone change.
* * The client should respond with a `BeginZoningMessage` packet.
* ex: * `map_name` and `zone_id` should correspond or the final product will be disorienting, even if it works.
* map13 & home3 = vs sanc * @param map_name designation of the physical zone;
* map10 & z10 = amerish * determines the (deployment) map screen
* map07 & z7 = esamir * @param zone_id designation of the entirety of the zone;
*/ * determines the loading screen
* @param unk1 na;
* seems to match the initial projectile index (that can be assigned)
* @param unk2 na;
* seems to match the total number of unique projectile indices (that can be assigned) (before looping)
* @param weapons_unlocked live fire is permissible;
* restricts all actions instigated by that key bind
* @param checksum challenge number so that client can confirm server is using the correct version of this zone
*/
final case class LoadMapMessage( final case class LoadMapMessage(
map_name: String, map_name: String,
nav_map_name: String, // Also determines loading screen zone_id: String, // Also determines loading screen
unk1: Int, unk1: Int,
unk2: Long, unk2: Long,
weapons_unlocked: Boolean, weapons_unlocked: Boolean,
checksum: Long checksum: Long
) //? ) extends PlanetSideGamePacket {
extends PlanetSideGamePacket {
type Packet = LoadMapMessage type Packet = LoadMapMessage
def opcode = GamePacketOpcode.LoadMapMessage def opcode = GamePacketOpcode.LoadMapMessage
def encode = LoadMapMessage.encode(this) def encode = LoadMapMessage.encode(this)
@ -29,8 +36,8 @@ final case class LoadMapMessage(
object LoadMapMessage extends Marshallable[LoadMapMessage] { object LoadMapMessage extends Marshallable[LoadMapMessage] {
implicit val codec: Codec[LoadMapMessage] = ( implicit val codec: Codec[LoadMapMessage] = (
("map_name" | PacketHelpers.encodedString) :: // TODO: Implement encodedStringWithLimit ("map_name" | PacketHelpers.encodedString) :: // TODO: Implement encodedStringWithLimit
("nav_map_name" | PacketHelpers.encodedString) :: //TODO: Implement encodedStringWithLimit ("zone_id" | PacketHelpers.encodedString) :: //TODO: Implement encodedStringWithLimit
("unk1" | uint16L) :: ("unk1" | uint16L) ::
("unk2" | uint32L) :: ("unk2" | uint32L) ::
("weapons_unlocked" | bool) :: ("weapons_unlocked" | bool) ::