Deconstruct in Privacy (#1069)

* when deconstructing at a spawn tube, eliminate damage by hiding the player character from rendering through psm manipulation; adjusts the psm load balancing algorithm; add conditions for checking for cancelling the deconstruction flag when certain actions are taken

* condition for avoiding server-size (all) damage during deconstruction period

* mutually assured discetion
This commit is contained in:
Fate-JH 2023-04-26 12:25:08 -04:00 committed by GitHub
parent 90b7d2591d
commit 72572ad125
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 52 deletions

View file

@ -69,27 +69,27 @@ class SessionAvatarHandlers(
isJumping,
jumpThrust,
isCloaking,
spectating,
isNotRendered,
canSeeReallyFar
) if isNotSameTarget =>
val pstateToSave = pstate.copy(timestamp = 0)
val (lastMsg, lastTime, lastPosition, wasSpectating, wasVisible) = lastSeenStreamMessage(guid.guid) match {
case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, msg.spectator, visible)
case _ => (None, 0L, Vector3.Zero, false, false)
val (lastMsg, lastTime, lastPosition, wasVisible) = lastSeenStreamMessage(guid.guid) match {
case SessionAvatarHandlers.LastUpstream(Some(msg), visible, time) => (Some(msg), time, msg.pos, visible)
case _ => (None, 0L, Vector3.Zero, false)
}
val drawConfig = Config.app.game.playerDraw
val drawConfig = Config.app.game.playerDraw //m
val maxRange = drawConfig.rangeMax * drawConfig.rangeMax //sq.m
val ourPosition = player.Position
val ourPosition = player.Position //xyz
val currentDistance = Vector3.DistanceSquared(ourPosition, pos) //sq.m
val inVisibleRange = currentDistance <= maxRange
val wasInVisibleRange = Vector3.DistanceSquared(lastPosition, pos) <= maxRange
val comingIntoVisibleRange = inVisibleRange && !wasInVisibleRange
val inDrawableRange = currentDistance <= maxRange
val now = System.currentTimeMillis() //ms
val durationSince = now - lastTime //ms
val released = player.isReleased
if (!released &&
!spectating &&
(comingIntoVisibleRange || (inVisibleRange && !lastMsg.contains(pstateToSave)))) {
if (
sessionData.zoning.zoningStatus != Zoning.Status.Deconstructing &&
!isNotRendered && inDrawableRange
) {
//conditions where visibility is assured
val durationSince = now - lastTime //ms
lazy val previouslyInDrawableRange = Vector3.DistanceSquared(ourPosition, lastPosition) <= maxRange
lazy val targetDelay = {
val populationOver = math.max(
0,
@ -102,12 +102,17 @@ class SessionAvatarHandlers(
case index => drawConfig.delays(index)
}
} //ms
if (comingIntoVisibleRange ||
canSeeReallyFar ||
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
durationSince > drawConfig.delayMax ||
sessionData.canSeeReallyFar ||
durationSince > targetDelay) {
if (!wasVisible ||
!previouslyInDrawableRange ||
(!lastMsg.contains(pstateToSave) &&
(canSeeReallyFar ||
currentDistance < drawConfig.rangeMin * drawConfig.rangeMin ||
durationSince > drawConfig.delayMax ||
sessionData.canSeeReallyFar ||
durationSince > targetDelay
)
)
) {
//must draw
sendResponse(
PlayerStateMessage(
@ -126,28 +131,31 @@ class SessionAvatarHandlers(
)
lastSeenStreamMessage(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)
}
} else {
//conditions where the target is not currently visible
if (wasVisible) {
//the target was JUST PREVIOUSLY visible; one last draw to move target beyond a renderable distance
val lat = (1 + hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat
sendResponse(
PlayerStateMessage(
guid,
Vector3(1f, lat, 1f),
vel=None,
facingYaw=0f,
facingPitch=0f,
facingYawUpper=0f,
timestamp=0, //is this okay?
is_cloaked = isCloaking
)
)
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now)
} else {
//skip drawing altogether
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, lastTime)
}
} else if (
(!spectating && wasInVisibleRange && !inVisibleRange) ||
(inVisibleRange && !wasSpectating && spectating) ||
(wasVisible && released)
) {
//must hide
val lat = (1 + hidingPlayerRandomizer.nextInt(continent.map.scale.height.toInt)).toFloat
sendResponse(
PlayerStateMessage(
guid,
Vector3(1f, lat, 1f),
vel=None,
facingYaw=0f,
facingPitch=0f,
facingYawUpper=0f,
timestamp=0, //is this okay?
is_cloaked = isCloaking
)
)
lastSeenStreamMessage(guid.guid) = SessionAvatarHandlers.LastUpstream(Some(pstateToSave), visible=false, now)
}
case AvatarResponse.ObjectHeld(slot, _)
@ -157,6 +165,9 @@ class SessionAvatarHandlers(
continent.GUID(sessionData.terminals.usingMedicalTerminal).collect {
case term: Terminal with ProximityUnit => sessionData.terminals.StopUsingProximityUnit(term)
}
if (sessionData.zoning.zoningStatus == Zoning.Status.Deconstructing) {
sessionData.stopDeconstructing()
}
case AvatarResponse.ObjectHeld(slot, _)
if isSameTarget && slot > -1 =>

View file

@ -210,7 +210,11 @@ class SessionData(
val isMoving = WorldEntity.isMoving(vel)
val isMovingPlus = isMoving || isJumping || jumpThrust
if (isMovingPlus) {
zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion")
if (zoning.zoningStatus == Zoning.Status.Deconstructing) {
stopDeconstructing()
} else {
zoning.CancelZoningProcessWithDescriptiveReason("cancel_motion")
}
}
fallHeightTracker(pos.z)
// if (isCrouching && !player.Crouching) {
@ -258,6 +262,9 @@ class SessionData(
case None => ()
}
val eagleEye: Boolean = canSeeReallyFar
val isNotVisible: Boolean = player.spectator ||
zoning.zoningStatus == Zoning.Status.Deconstructing ||
(player.isAlive && zoning.spawn.deadState == DeadState.RespawnTime)
continent.AvatarEvents ! AvatarServiceMessage(
continent.id,
AvatarAction.PlayerState(
@ -272,7 +279,7 @@ class SessionData(
isJumping,
jumpThrust,
isCloaking,
player.spectator,
isNotVisible,
eagleEye
)
)
@ -516,15 +523,27 @@ class SessionData(
def handleAvatarImplant(pkt: AvatarImplantMessage): Unit = {
val AvatarImplantMessage(_, action, slot, status) = pkt
if (action == ImplantAction.Activation) {
zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant")
avatar.implants(slot) match {
case Some(implant) =>
if (status == 1) {
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
} else {
if (zoning.zoningStatus == Zoning.Status.Deconstructing) {
//do not activate; play deactivation sound instead
stopDeconstructing()
avatar.implants(slot).collect {
case implant if implant.active =>
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
}
case _ => log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot")
case implant =>
sendResponse(PlanetsideAttributeMessage(player.GUID, 28, implant.definition.implantType.value * 2))
}
} else {
zoning.CancelZoningProcessWithDescriptiveReason("cancel_implant")
avatar.implants(slot) match {
case Some(implant) =>
if (status == 1) {
avatarActor ! AvatarActor.ActivateImplant(implant.definition.implantType)
} else {
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
}
case _ =>
log.error(s"AvatarImplantMessage: ${player.Name} has an unknown implant in $slot")
}
}
}
}
@ -1422,11 +1441,10 @@ class SessionData(
sendUseGeneralEntityMessage(obj, item)
case None if player.Faction == obj.Faction =>
//deconstruction
log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns")
zoning.CancelZoningProcessWithDescriptiveReason("cancel_use")
playerActionsToCancel()
terminals.CancelAllProximityUnits()
zoning.spawn.GoToDeploymentMap()
startDeconstructing(obj)
case _ => ()
}
}
@ -2751,6 +2769,34 @@ class SessionData(
sendResponse(ChatMsg(ChatMessageType.UNK_227, wideContents=false, "", "@charsaved", None))
}
def startDeconstructing(obj: SpawnTube): Unit = {
log.info(s"${player.Name} is deconstructing at the ${obj.Owner.Definition.Name}'s spawns")
avatar.implants.collect {
case Some(implant) if implant.active && !implant.definition.Passive =>
avatarActor ! AvatarActor.DeactivateImplant(implant.definition.implantType)
}
if (player.ExoSuit != ExoSuitType.MAX) {
player.Actor ! PlayerControl.ObjectHeld(Player.HandsDownSlot, updateMyHolsterArm = true)
}
zoning.spawn.nextSpawnPoint = Some(obj) //set fallback
zoning.zoningStatus = Zoning.Status.Deconstructing
player.allowInteraction = false
if (player.death_by == 0) {
player.death_by = 1
}
zoning.spawn.GoToDeploymentMap()
}
def stopDeconstructing(): Unit = {
zoning.zoningStatus = Zoning.Status.None
player.death_by = math.min(player.death_by, 0)
player.allowInteraction = true
zoning.spawn.nextSpawnPoint.foreach { tube =>
sendResponse(PlayerStateShiftMessage(ShiftState(0, tube.Position, tube.Orientation.z)))
zoning.spawn.nextSpawnPoint = None
}
}
def failWithError(error: String): Unit = {
log.error(error)
middlewareActor ! MiddlewareActor.Teardown()

View file

@ -1920,6 +1920,9 @@ class ZoningOperations(
TurnCounterDuringInterim
}
sessionData.keepAliveFunc = NormalKeepAlive
if (zoningStatus == Zoning.Status.Deconstructing) {
sessionData.stopDeconstructing()
}
upstreamMessageCount = 0
setAvatar = false
sessionData.persist()
@ -1950,6 +1953,9 @@ class ZoningOperations(
TurnCounterDuringInterim
}
sessionData.keepAliveFunc = NormalKeepAlive
if (zoningStatus == Zoning.Status.Deconstructing) {
sessionData.stopDeconstructing()
}
upstreamMessageCount = 0
setAvatar = false
sessionData.persist()
@ -2080,6 +2086,7 @@ class ZoningOperations(
player.Health = health
player.Armor = armor
}
player.death_by = math.min(player.death_by, 0)
sessionData.vehicles.GetKnownVehicleAndSeat() match {
case (Some(vehicle: Vehicle), Some(seat: Int)) =>
//if the vehicle is the cargo of another vehicle in this zone

View file

@ -547,6 +547,10 @@ class Player(var avatar: Avatar)
ZoningRequest
}
override def CanDamage: Boolean = {
death_by < 1 && super.CanDamage
}
def DamageModel: DamageResistanceModel = exosuit.asInstanceOf[DamageResistanceModel]
def canEqual(other: Any): Boolean = other.isInstanceOf[Player]

View file

@ -25,6 +25,7 @@ object Zoning {
val values: IndexedSeq[Status] = findValues
case object None extends Status(value = "None")
case object Deconstructing extends Status(value = "Deconstructing")
case object Request extends Status(value = "Request")
case object Countdown extends Status(value = "Countdown")
}