diff --git a/src/main/scala/net/psforever/actors/session/ChatActor.scala b/src/main/scala/net/psforever/actors/session/ChatActor.scala index f8953a03c..8a41c36ab 100644 --- a/src/main/scala/net/psforever/actors/session/ChatActor.scala +++ b/src/main/scala/net/psforever/actors/session/ChatActor.scala @@ -508,6 +508,12 @@ class ChatActor( CavernRotationService.HurryNextRotation }) + case (_, _, content) if content.startsWith("!suicide") => + //this is like CMT_SUICIDE but it ignores checks and forces a suicide state + val tplayer = session.player + tplayer.Revive + tplayer.Actor ! Player.Die() + case _ => // unknown ! commands are ignored } diff --git a/src/main/scala/net/psforever/actors/session/SessionActor.scala b/src/main/scala/net/psforever/actors/session/SessionActor.scala index b86a58953..d12c4dd13 100644 --- a/src/main/scala/net/psforever/actors/session/SessionActor.scala +++ b/src/main/scala/net/psforever/actors/session/SessionActor.scala @@ -14,7 +14,7 @@ import net.psforever.objects.ballistics._ import net.psforever.objects.ce._ import net.psforever.objects.definition._ import net.psforever.objects.definition.converter.{CorpseConverter, DestroyedVehicleConverter} -import net.psforever.objects.entity.{SimpleWorldEntity, WorldEntity} +import net.psforever.objects.entity.{NoGUIDException, SimpleWorldEntity, WorldEntity} import net.psforever.objects.equipment._ import net.psforever.objects.guid._ import net.psforever.objects.inventory.{Container, GridInventory, InventoryItem} @@ -293,31 +293,122 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con override def supervisorStrategy: SupervisorStrategy = { import net.psforever.objects.inventory.InventoryDisarrayException - import java.io.{StringWriter, PrintWriter} - OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { + OneForOneStrategy(maxNrOfRetries = -1, withinTimeRange = 1 minute) { + case nge: NoGUIDException => + nge.getEntity match { + case p: Player => + continent.GUID(p.VehicleSeated) match { + case Some(v: Vehicle) => + attemptRecoveryFromNoGuidExceptionAsVehicle(v, nge) + case _ => + attemptRecoveryFromNoGuidExceptionAsPlayer(p, nge) + } + + case v: Vehicle => + attemptRecoveryFromNoGuidExceptionAsVehicle(v, nge) + + case e: Equipment => + ( + player.Holsters().zipWithIndex.flatMap { case (o, i) => + o.Equipment match { + case Some(e) => Some((player, InventoryItem(e, i))) + case None => None + } + }.toList ++ + player.Inventory.Items.map { o => (player, o) } ++ + { + player.FreeHand.Equipment match { + case Some(o) => List((player, InventoryItem(e, Player.FreeHandSlot))) + case _ => Nil + } + } ++ + (ValidObject(player.VehicleSeated) match { + case Some(v: Vehicle) => v.Trunk.Items.map{ o => (v, o) } + case _ => Nil + }) + ) + .find { case (_, InventoryItem(o, _)) => o eq e } match { + case Some((c: Container, InventoryItem(obj, index))) => + if (!obj.HasGUID) { + c.Slot(index).Equipment = None + } + c match { + case _: Player => + attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) + case v: Vehicle => + if (v.PassengerInSeat(player).contains(0)) { + attemptRecoveryFromNoGuidExceptionAsPlayer(player, nge) + } + SupervisorStrategy.resume + case _ => + writeLogExceptionAndStop(nge) + } + case _ => + //did not discover or resolve the situation + writeLogExceptionAndStop(nge) + } + + case _ => + SupervisorStrategy.resume + } + case ide: InventoryDisarrayException => - attemptRecoverFromInventoryDisarrayException(ide.inventory) + attemptRecoveryFromInventoryDisarrayException(ide.inventory) //re-evaluate results if (ide.inventory.ElementsOnGridMatchList() > 0) { - val sw = new StringWriter - ide.printStackTrace(new PrintWriter(sw)) - log.error(sw.toString) - ImmediateDisconnect() - SupervisorStrategy.stop + writeLogExceptionAndStop(ide) } else { SupervisorStrategy.resume } case e => - val sw = new StringWriter - e.printStackTrace(new PrintWriter(sw)) - log.error(sw.toString) - ImmediateDisconnect() - SupervisorStrategy.stop + writeLogExceptionAndStop(e) } } - def attemptRecoverFromInventoryDisarrayException(inv: GridInventory): Unit = { + def attemptRecoveryFromNoGuidExceptionAsVehicle(v: Vehicle, e: Throwable): SupervisorStrategy.Directive = { + val entry = v.Seats.find { case (_, s) => s.occupants.contains(player) } + entry match { + case Some((index, _)) => + player.VehicleSeated = None + v.Seats(0).unmount(player) + player.Position = v.Position + player.Orientation = v.Orientation + interstellarFerry = None + interstellarFerryTopLevelGUID = None + attemptRecoveryFromNoGuidExceptionAsPlayer(player, e) + case None => + writeLogException(e) + } + } + + def attemptRecoveryFromNoGuidExceptionAsPlayer(p: Player, e: Throwable): SupervisorStrategy.Directive = { + if (p eq player) { + val hasGUID = p.HasGUID + zoneLoaded match { + case Some(true) if hasGUID => + AvatarCreate() //this will probably work? + SupervisorStrategy.resume + case Some(false) => + RequestSanctuaryZoneSpawn(p, continent.Number) + SupervisorStrategy.resume + case None => + if (player.Zone eq Zone.Nowhere) { + RequestSanctuaryZoneSpawn(p, continent.Number) + } else { + zoneReload = true + LoadZoneAsPlayer(player, player.Zone.id) + } + SupervisorStrategy.resume + case _ => + writeLogExceptionAndStop(e) + } + } else { + SupervisorStrategy.resume + } + } + + def attemptRecoveryFromInventoryDisarrayException(inv: GridInventory): Unit = { inv.ElementsInListCollideInGrid() match { case Nil => ; case overlaps => @@ -403,6 +494,20 @@ class SessionActor(middlewareActor: typed.ActorRef[MiddlewareActor.Command], con } } + def writeLogException(e: Throwable): SupervisorStrategy.Directive = { + import java.io.{PrintWriter, StringWriter} + val sw = new StringWriter + e.printStackTrace(new PrintWriter(sw)) + log.error(sw.toString) + SupervisorStrategy.Resume + } + + def writeLogExceptionAndStop(e: Throwable): SupervisorStrategy.Directive = { + writeLogException(e) + ImmediateDisconnect() + SupervisorStrategy.stop + } + def session: Session = _session def session_=(session: Session): Unit = { diff --git a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala index 9d04fcf12..497009791 100644 --- a/src/main/scala/net/psforever/objects/GlobalDefinitions.scala +++ b/src/main/scala/net/psforever/objects/GlobalDefinitions.scala @@ -1334,7 +1334,7 @@ object GlobalDefinitions { val hst = new WarpGateDefinition(402) hst.Name = "hst" - hst.UseRadius = 64.96882005f + hst.UseRadius = 44.96882005f hst.SOIRadius = 82 hst.VehicleAllowance = true hst.NoWarp += dropship @@ -1346,28 +1346,28 @@ object GlobalDefinitions { hst.NoWarp += colossus_flight hst.NoWarp += peregrine_gunner hst.NoWarp += peregrine_flight - hst.SpecificPointFunc = SpawnPoint.SmallGate(innerRadius = 5f) + hst.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 6f) val warpgate = new WarpGateDefinition(993) warpgate.Name = "warpgate" - warpgate.UseRadius = 67.81070029f //301.8713f - warpgate.SOIRadius = 302 + warpgate.UseRadius = 67.81070029f + warpgate.SOIRadius = 302 //301.8713f warpgate.VehicleAllowance = true warpgate.SpecificPointFunc = SpawnPoint.Gate val warpgate_cavern = new WarpGateDefinition(994) warpgate_cavern.Name = "warpgate_cavern" - warpgate_cavern.UseRadius = 20.72639434f - warpgate_cavern.SOIRadius = 52 + warpgate_cavern.UseRadius = 19.72639434f + warpgate_cavern.SOIRadius = 41 warpgate_cavern.VehicleAllowance = true - warpgate_cavern.SpecificPointFunc = SpawnPoint.CavernGate + warpgate_cavern.SpecificPointFunc = SpawnPoint.CavernGate(innerRadius = 4.5f) val warpgate_small = new WarpGateDefinition(995) warpgate_small.Name = "warpgate_small" warpgate_small.UseRadius = 69.03687655f warpgate_small.SOIRadius = 103 warpgate_small.VehicleAllowance = true - warpgate_small.SpecificPointFunc = SpawnPoint.SmallGate(innerRadius = 27.60654127f) + warpgate_small.SpecificPointFunc = SpawnPoint.SmallGate(innerRadius = 27.60654127f, flightlessZOffset = 0.5f) val bunker_gauntlet = new BuildingDefinition(150) { Name = "bunker_gauntlet" } val bunker_lg = new BuildingDefinition(151) { Name = "bunker_lg" } @@ -1581,7 +1581,7 @@ object GlobalDefinitions { def isMaxArms(tdef: ToolDefinition): Boolean = { tdef match { case `trhev_dualcycler` | `nchev_scattercannon` | `vshev_quasar` | `trhev_pounder` | `nchev_falcon` | - `vshev_comet` | `trhev_burster` | `nchev_sparrow` | `vshev_starfire` => + `vshev_comet` | `trhev_burster` | `nchev_sparrow` | `vshev_starfire` => true case _ => false @@ -1691,13 +1691,13 @@ object GlobalDefinitions { def isFactionWeapon(edef: EquipmentDefinition): PlanetSideEmpire.Value = { edef match { case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | - `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` => + `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` => PlanetSideEmpire.TR case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `nchev_scattercannon` | - `nchev_falcon` | `nchev_sparrow` => + `nchev_falcon` | `nchev_sparrow` => PlanetSideEmpire.NC case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `vshev_quasar` | - `vshev_comet` | `vshev_starfire` => + `vshev_comet` | `vshev_starfire` => PlanetSideEmpire.VS case _ => PlanetSideEmpire.NEUTRAL @@ -1713,16 +1713,16 @@ object GlobalDefinitions { def isFactionEquipment(edef: EquipmentDefinition): PlanetSideEmpire.Value = { edef match { case `chainblade` | `repeater` | `anniversary_guna` | `cycler` | `mini_chaingun` | `striker` | - `striker_missile_ammo` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` | `dualcycler_ammo` | - `pounder_ammo` | `burster_ammo` => + `striker_missile_ammo` | `trhev_dualcycler` | `trhev_pounder` | `trhev_burster` | `dualcycler_ammo` | + `pounder_ammo` | `burster_ammo` => PlanetSideEmpire.TR case `magcutter` | `isp` | `anniversary_gun` | `gauss` | `r_shotgun` | `hunterseeker` | `hunter_seeker_missile` | - `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` | `scattercannon_ammo` | `falcon_ammo` | - `sparrow_ammo` => + `nchev_scattercannon` | `nchev_falcon` | `nchev_sparrow` | `scattercannon_ammo` | `falcon_ammo` | + `sparrow_ammo` => PlanetSideEmpire.NC case `forceblade` | `beamer` | `anniversary_gunb` | `pulsar` | `lasher` | `lancer` | `energy_cell` | - `lancer_cartridge` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` | `quasar_ammo` | `comet_ammo` | - `starfire_ammo` => + `lancer_cartridge` | `vshev_quasar` | `vshev_comet` | `vshev_starfire` | `quasar_ammo` | `comet_ammo` | + `starfire_ammo` => PlanetSideEmpire.VS case _ => PlanetSideEmpire.NEUTRAL @@ -1905,7 +1905,7 @@ object GlobalDefinitions { def isFlightVehicle(vdef: VehicleDefinition): Boolean = { vdef match { case `mosquito` | `lightgunship` | `wasp` | `liberator` | `vulture` | `phantasm` | `lodestar` | `dropship` | - `galaxy_gunship` => + `galaxy_gunship` => true case _ => false diff --git a/src/main/scala/net/psforever/objects/SpawnPoint.scala b/src/main/scala/net/psforever/objects/SpawnPoint.scala index 3a762c683..500126850 100644 --- a/src/main/scala/net/psforever/objects/SpawnPoint.scala +++ b/src/main/scala/net/psforever/objects/SpawnPoint.scala @@ -3,6 +3,7 @@ package net.psforever.objects import net.psforever.objects.definition.{ObjectDefinition, VehicleDefinition} import net.psforever.objects.serverobject.PlanetSideServerObject +import net.psforever.objects.serverobject.mount.MountableEntity import net.psforever.types.{PlanetSideGUID, Vector3} import scala.collection.mutable @@ -93,10 +94,10 @@ object SpawnPoint { ) * (3 * side).toFloat //x=sin, y=cos because compass-0 is East, not North ( obj.Position + shift + (if (x >= 330) { //ams leaning to the left - Vector3.z(xsin) - } else { //ams leaning to the right - Vector3.z(-xsin) - }), + Vector3.z(xsin) + } else { //ams leaning to the right + Vector3.z(-xsin) + }), if (side == 1) { Vector3.z(zrot) } else { @@ -111,8 +112,8 @@ object SpawnPoint { val ori = target.Orientation val zrad = math.toRadians(ori.z) val radius = - scala.math.random().toFloat * (d.UseRadius - innerRadius) + innerRadius - val shift = Vector3(math.sin(zrad).toFloat, math.cos(zrad).toFloat, 0) * radius + scala.math.random().toFloat * 0.5f * (d.UseRadius - innerRadius) + innerRadius + val shift = Vector3(math.sin(zrad).toFloat, math.cos(zrad).toFloat, 0) * radius val altitudeShift = target.Definition match { case vdef: VehicleDefinition if GlobalDefinitions.isFlightVehicle(vdef) => Vector3.z(scala.math.random().toFloat * d.UseRadius) @@ -136,23 +137,26 @@ object SpawnPoint { ) } - def CavernGate(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = { - val (a, b) = metaGate(obj, target, innerRadius = 5f) + def CavernGate(innerRadius: Float)(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = { + val (a, b) = metaGate(obj, target, innerRadius) target match { case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) => (a.xy + Vector3.z((target.Position.z + a.z) * 0.5f), b) + case m: MountableEntity => + m.BailProtection = true + (a + Vector3.z(obj.Definition.UseRadius * 0.5f), b) case _ => - (a + Vector3.z(value = 3f), b) + (a, b) } } - def SmallGate(innerRadius: Float)(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = { + def SmallGate(innerRadius: Float, flightlessZOffset: Float)(obj: SpawnPoint, target: PlanetSideGameObject): (Vector3, Vector3) = { val (a, b) = metaGate(obj, target, innerRadius) target match { case v: Vehicle if GlobalDefinitions.isFlightVehicle(v.Definition) => (a.xy + Vector3.z((target.Position.z + a.z) * 0.5f), b) case _ => - (a + Vector3.z(value = 0.5f), b) + (a + Vector3.z(flightlessZOffset), b) } } }