diff --git a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala index 2a1d09965..ff3c29155 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/structures/Building.scala @@ -1,6 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.structures +import java.util.concurrent.TimeUnit + import akka.actor.ActorContext import net.psforever.objects.GlobalDefinitions import net.psforever.objects.definition.ObjectDefinition @@ -66,12 +68,13 @@ class Building(private val building_guid : Int, private val map_id : Int, privat case _ => //we have no silo; we have unlimited power 10 } - //if we have a capture terminal, get the hack status & time from control console if it exists + //if we have a capture terminal, get the hack status & time (in milliseconds) from control console if it exists val (hacking, hackingFaction, hackTime) : (Boolean, PlanetSideEmpire.Value, Long) = amenities.find(_.Definition == GlobalDefinitions.capture_terminal) match { case Some(obj: CaptureTerminal with Hackable) => obj.HackedBy match { case Some(Hackable.HackInfo(_, _, hfaction, _, start, length)) => - (true, hfaction, math.max(0, start + length - System.nanoTime)) + val hack_time_remaining_ms = TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS) + (true, hfaction, hack_time_remaining_ms) case _ => (false, PlanetSideEmpire.NEUTRAL, 0L) } diff --git a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala index 4e10dca37..eec5c3124 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -66,6 +66,7 @@ import scodec.codecs._ *
  • 196608 - 262143 - VS
  • *
  • 17039360 - CC Resecured
  • * + *
    These values seem to correspond to the following data structure: Time left - 2 bytes, faction - 1 byte (1-4), isResecured - 1 byte (0-1)
    * `24 - Learn certifications with value :`
    * 01 : Medium Assault
    * 02 : Heavy Assault
    diff --git a/common/src/main/scala/services/local/LocalService.scala b/common/src/main/scala/services/local/LocalService.scala index 07f743877..d5907c2c3 100644 --- a/common/src/main/scala/services/local/LocalService.scala +++ b/common/src/main/scala/services/local/LocalService.scala @@ -208,9 +208,6 @@ class LocalService extends Actor { case scala.util.Failure(_) => log.warn(s"LocalService Failed to get zone when hack timeout was reached") } - case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) => - hackCapturer forward HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) - //message to Engineer case LocalServiceMessage.Deployables(msg) => engineer forward msg diff --git a/common/src/main/scala/services/local/support/HackCaptureActor.scala b/common/src/main/scala/services/local/support/HackCaptureActor.scala index fd8966351..0a0246160 100644 --- a/common/src/main/scala/services/local/support/HackCaptureActor.scala +++ b/common/src/main/scala/services/local/support/HackCaptureActor.scala @@ -71,16 +71,6 @@ class HackCaptureActor extends Actor { // Restart the timer in case the object we just removed was the next one scheduled RestartTimer() - - case HackCaptureActor.GetHackTimeRemainingNanos(capture_console_guid) => - hackedObjects.find(_.target.GUID == capture_console_guid) match { - case Some(obj: HackCaptureActor.HackEntry) => - val time_left: Long = obj.duration.toNanos - (System.nanoTime - obj.hack_timestamp) - sender ! time_left - case _ => - log.warn(s"Couldn't find capture terminal guid $capture_console_guid in hackedObjects list") - sender ! 0L - } case _ => ; } @@ -110,9 +100,6 @@ object HackCaptureActor { final case class ClearHack(target : CaptureTerminal, zone : Zone) - final case class GetHackTimeRemainingNanos(capture_console_guid: PlanetSideGUID) - - private final case class ProcessCompleteHacks() private final case class HackEntry(target : PlanetSideServerObject with Hackable, zone : Zone, unk1 : Long, unk2 : Long, duration: FiniteDuration, hack_timestamp : Long) diff --git a/common/src/main/scala/services/local/support/HackClearActor.scala b/common/src/main/scala/services/local/support/HackClearActor.scala index 1399733d7..f1c9f6e1e 100644 --- a/common/src/main/scala/services/local/support/HackClearActor.scala +++ b/common/src/main/scala/services/local/support/HackClearActor.scala @@ -66,11 +66,17 @@ class HackClearActor() extends Actor { if(hackedObjects.length != 0) { val now = System.nanoTime() val (unhackObjects, stillHackedObjects) = PartitionEntries(hackedObjects, now) - val short_timeout : FiniteDuration = math.max(1, stillHackedObjects.head.duration - (now - stillHackedObjects.head.time)) nanoseconds - log.warn(s"Still items left in hacked objects list. Checking again in ${short_timeout.toSeconds} seconds") - import scala.concurrent.ExecutionContext.Implicits.global - clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks()) + stillHackedObjects.headOption match { + case Some(hackEntry) => + val short_timeout : FiniteDuration = math.max(1, hackEntry.duration - (now - hackEntry.time)) nanoseconds + + log.info(s"HackClearActor: Still items left in hacked objects list. Checking again in ${short_timeout.toSeconds} seconds") + import scala.concurrent.ExecutionContext.Implicits.global + clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks()) + case None => log.info("HackClearActor: No objects left in hacked objects list. Not rescheduling check.") + } + } } diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 4a0e9e838..7b63bb88b 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1284,23 +1284,27 @@ class WorldSessionActor extends Actor with MDCContextAware { value = 17039360L } else { - import scala.concurrent.ExecutionContext.Implicits.global - val future = ask(localService, HackCaptureActor.GetHackTimeRemainingNanos(target_guid))(1 second) - val time = Await.result(future, 1 second).asInstanceOf[Long] - // todo: blocking call. Not good. - val hack_time_remaining_ms = TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS) - val deciseconds_remaining = (hack_time_remaining_ms / 100) - val hacking_faction = continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get.hackerFaction - // See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated - val start_num = hacking_faction match { - case PlanetSideEmpire.TR => 65536L - case PlanetSideEmpire.NC => 131072L - case PlanetSideEmpire.VS => 196608L - } - value = start_num + deciseconds_remaining - } - sendResponse(PlanetsideAttributeMessage(target_guid, 20, value)) + continent.GUID(target_guid) match { + case Some(capture_terminal: Hackable) => + capture_terminal.HackedBy match { + case Some(Hackable.HackInfo(_, _, hfaction, _, start, length)) => + val hack_time_remaining_ms = TimeUnit.MILLISECONDS.convert(math.max(0, start + length - System.nanoTime), TimeUnit.NANOSECONDS) + val deciseconds_remaining = (hack_time_remaining_ms / 100) + // See PlanetSideAttributeMessage #20 documentation for an explanation of how the timer is calculated + val start_num = hfaction match { + case PlanetSideEmpire.TR => 65536L + case PlanetSideEmpire.NC => 131072L + case PlanetSideEmpire.VS => 196608L + } + value = start_num + deciseconds_remaining + + sendResponse(PlanetsideAttributeMessage(target_guid, 20, value)) + case _ => log.warn("LocalResponse.HackCaptureTerminal: HackedBy not defined") + } + case _ => log.warn(s"LocalResponse.HackCaptureTerminal: Couldn't find capture terminal with GUID ${target_guid} in zone ${continent.Id}") + } + } case LocalResponse.ObjectDelete(object_guid, unk) => if(tplayer_guid != guid) { sendResponse(ObjectDeleteMessage(object_guid, unk)) @@ -3211,7 +3215,10 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(container) => //just in case if(vel.isDefined) { val guid = player.GUID - sendResponse(UnuseItemMessage(guid, container.GUID)) + // If the container is a corpse and gets removed just as this runs it can cause a client disconnect, so we'll check the container has a GUID first. + if(container.HasGUID) { + sendResponse(UnuseItemMessage(guid, container.GUID)) + } sendResponse(UnuseItemMessage(guid, guid)) accessedContainer = None }