door hacking now clears in 60s

This commit is contained in:
FateJH 2017-10-12 21:05:38 -04:00
parent 74b718c536
commit 0a4bac8ab5
5 changed files with 143 additions and 29 deletions

View file

@ -3,7 +3,6 @@ package net.psforever.objects.serverobject.locks
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.doors.Door
class IFFLockControl(lock : IFFLock) extends Actor {
def receive : Receive = {
@ -11,8 +10,7 @@ class IFFLockControl(lock : IFFLock) extends Actor {
lock.HackedBy = player
case CommonMessages.ClearHack() =>
lock.HackedBy = None
case _ =>
sender ! Door.NoEvent()
case _ => ;
}
}

View file

@ -0,0 +1,87 @@
// Copyright (c) 2017 PSForever
package net.psforever.objects.zones
import akka.actor.{Actor, Cancellable}
import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject}
import net.psforever.packet.game.PlanetSideGUID
import scala.annotation.tailrec
import scala.concurrent.duration._
class HackClearActor() extends Actor {
import HackClearActor._
private var clearTrigger : Cancellable = DefaultClearer
private var hackedObjects : List[HackEntry] = Nil
//private[this] val log = org.log4s.getLogger
def receive : Receive = {
case ObjectIsHacked(door, zone, unk1, unk2, time) =>
hackedObjects = hackedObjects :+ HackEntry(door, zone, unk1, unk2, time)
if(hackedObjects.size == 1) {
import scala.concurrent.ExecutionContext.Implicits.global
clearTrigger = context.system.scheduler.scheduleOnce(timeout, self, HackClearActor.TryClearHacks())
}
case TryClearHacks() =>
clearTrigger.cancel
val now : Long = System.nanoTime
//TODO we can just walk across the list of doors and extract only the first few entries
val (unhackObjects, stillHackedObjects) = recursivePartitionHacks(hackedObjects.iterator, now)
hackedObjects = stillHackedObjects
unhackObjects.foreach(entry => {
entry.target.Actor ! CommonMessages.ClearHack()
context.parent ! HackClearActor.ClearTheHack(entry.target.GUID, entry.zone.Id, entry.unk1, entry.unk2)
})
if(stillHackedObjects.nonEmpty) {
val short_timeout : FiniteDuration = math.max(1, timeout_time - (now - stillHackedObjects.head.hacked_at_time)) nanoseconds
import scala.concurrent.ExecutionContext.Implicits.global
clearTrigger = context.system.scheduler.scheduleOnce(short_timeout, self, HackClearActor.TryClearHacks())
}
case _ => ;
}
/**
* na
* @param iter na
* @param now na
* @param list na
* @see `List.partition`
* @return a `Tuple` of two `Lists`:
* the entries for all objects that are no longer hacked,
* and the entries for all objects that are still hacked
*/
@tailrec private def recursivePartitionHacks(iter : Iterator[HackEntry], now : Long, list : List[HackEntry] = Nil) : (List[HackEntry], List[HackEntry]) = {
if(!iter.hasNext) {
(list, iter.toList)
}
else {
val entry = iter.next()
if(now - entry.hacked_at_time >= timeout_time) {
recursivePartitionHacks(iter, now, list :+ entry)
}
else {
(list, entry +: iter.toList)
}
}
}
}
object HackClearActor {
private final val timeout_time : Long = 60000000000L //nanoseconds (1 minute)
private final val timeout : FiniteDuration = timeout_time nanoseconds
private final val DefaultClearer : Cancellable = new Cancellable() {
override def cancel : Boolean = true
override def isCancelled : Boolean = true
}
final case class ObjectIsHacked(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, hacked_at_time : Long = System.nanoTime())
final case class ClearTheHack(door_guid : PlanetSideGUID, zone_id : String, unk1 : Long, unk2 : Long)
private final case class HackEntry(target : PlanetSideServerObject, zone : Zone, unk1 : Long, unk2 : Long, hacked_at_time : Long)
private final case class TryClearHacks()
}

View file

@ -1,7 +1,6 @@
// Copyright (c) 2017 PSForever
import akka.actor.Actor
import net.psforever.objects.equipment.Equipment
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.packet.game.objectcreate.ConstructorData
import net.psforever.types.ExoSuitType
import net.psforever.packet.game.{PlanetSideGUID, PlayerStateMessageUpstream}
@ -14,7 +13,6 @@ object AvatarAction {
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Action
final case class EquipmentInHand(player_guid : PlanetSideGUID, slot : Int, item : Equipment) extends Action
final case class EquipmentOnGround(player_guid : PlanetSideGUID, pos : Vector3, orient : Vector3, item : Equipment) extends Action
final case class Hack(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class LoadPlayer(player_guid : PlanetSideGUID, pdata : ConstructorData) extends Action
// final case class LoadMap(msg : PlanetSideGUID) extends Action
// final case class unLoadMap(msg : PlanetSideGUID) extends Action
@ -36,7 +34,6 @@ object AvatarServiceResponse {
//final case class DropItem(pos : Vector3, orient : Vector3, item : PlanetSideGUID) extends Response
final case class EquipmentInHand(slot : Int, item : Equipment) extends Response
final case class EquipmentOnGround(pos : Vector3, orient : Vector3, item : Equipment) extends Response
final case class Hack(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long = 8L) extends Response
final case class LoadPlayer(pdata : ConstructorData) extends Response
// final case class unLoadMap() extends Response
// final case class LoadMap() extends Response
@ -96,10 +93,6 @@ class AvatarService extends Actor {
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.EquipmentOnGround(pos, orient, obj))
)
case AvatarAction.Hack(player_guid, target, unk1, unk2) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.Hack(target.GUID, unk1, unk2))
)
case AvatarAction.LoadPlayer(player_guid, pdata) =>
AvatarEvents.publish(
AvatarServiceResponse(s"/$forChannel/Avatar", player_guid, AvatarServiceResponse.LoadPlayer(pdata))

View file

@ -1,7 +1,8 @@
// Copyright (c) 2017 PSForever
import akka.actor.{Actor, Props}
import net.psforever.objects.serverobject.PlanetSideServerObject
import net.psforever.objects.serverobject.doors.Door
import net.psforever.objects.zones.{DoorCloseActor, Zone}
import net.psforever.objects.zones.{DoorCloseActor, HackClearActor, Zone}
import net.psforever.packet.game.PlanetSideGUID
object LocalAction {
@ -9,6 +10,8 @@ object LocalAction {
final case class DoorOpens(player_guid : PlanetSideGUID, continent : Zone, door : Door) extends Action
final case class DoorCloses(player_guid : PlanetSideGUID, door_guid : PlanetSideGUID) extends Action
final case class HackClear(player_guid : PlanetSideGUID, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
final case class HackTemporarily(player_guid : PlanetSideGUID, continent : Zone, target : PlanetSideServerObject, unk1 : Long, unk2 : Long = 8L) extends Action
}
object LocalServiceResponse {
@ -16,6 +19,8 @@ object LocalServiceResponse {
final case class DoorOpens(door_guid : PlanetSideGUID) extends Response
final case class DoorCloses(door_guid : PlanetSideGUID) extends Response
final case class HackClear(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
final case class HackObject(target_guid : PlanetSideGUID, unk1 : Long, unk2 : Long) extends Response
}
final case class LocalServiceMessage(forChannel : String, actionMessage : LocalAction.Action)
@ -29,6 +34,7 @@ final case class LocalServiceResponse(toChannel : String, avatar_guid : PlanetSi
class LocalService extends Actor {
//import LocalService._
private val doorCloser = context.actorOf(Props[DoorCloseActor], "local-door-closer")
private val hackClearer = context.actorOf(Props[HackClearActor], "local-hack-clearer")
private [this] val log = org.log4s.getLogger
override def preStart = {
@ -55,21 +61,39 @@ class LocalService extends Actor {
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorOpens(door.GUID))
)
case LocalAction.DoorCloses(player_guid, door_guid) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.DoorCloses(door_guid))
)
case LocalAction.HackClear(player_guid, target, unk1, unk2) =>
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/LocalEnvironment", player_guid, LocalServiceResponse.HackClear(target.GUID, unk1, unk2))
)
case LocalAction.HackTemporarily(player_guid, zone, target, unk1, unk2) =>
hackClearer ! HackClearActor.ObjectIsHacked(target, zone, unk1, unk2)
LocalEvents.publish(
LocalServiceResponse(s"/$forChannel/Avatar", player_guid, LocalServiceResponse.HackObject(target.GUID, unk1, unk2))
)
case _ => ;
}
//response from DoorCloseActor
case DoorCloseActor.CloseTheDoor(door_guid, zone_id) =>
LocalEvents.publish(
LocalServiceResponse(s"/$zone_id/LocalEnvironment", PlanetSideGUID(0), LocalServiceResponse.DoorCloses(door_guid))
LocalServiceResponse(s"/$zone_id/LocalEnvironment", LocalService.defaultPlayerGUID, LocalServiceResponse.DoorCloses(door_guid))
)
//response from HackClearActor
case HackClearActor.ClearTheHack(target_guid, zone_id, unk1, unk2) =>
LocalEvents.publish(
LocalServiceResponse(s"/$zone_id/LocalEnvironment", LocalService.defaultPlayerGUID, LocalServiceResponse.HackClear(target_guid, unk1, unk2))
)
case msg =>
log.info(s"Unhandled message $msg from $sender")
}
}
object LocalService {
final val defaultPlayerGUID : PlanetSideGUID = PlanetSideGUID(0)
}

View file

@ -144,11 +144,6 @@ class WorldSessionActor extends Actor with MDCContextAware {
)
}
case AvatarServiceResponse.Hack(target_guid, unk1, unk2) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid.guid, guid.guid, 100, unk1, 6, unk2)))
}
case AvatarServiceResponse.LoadPlayer(pdata) =>
if(player.GUID != guid) {
sendResponse(
@ -232,6 +227,15 @@ class WorldSessionActor extends Actor with MDCContextAware {
case LocalServiceResponse.DoorCloses(door_guid) => //door closes for everyone
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 17)))
case LocalServiceResponse.HackClear(target_guid, unk1, unk2) =>
log.info(s"Clear hack of $target_guid")
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid.guid, guid.guid, 0, unk1, 7, unk2)))
case LocalServiceResponse.HackObject(target_guid, unk1, unk2) =>
if(player.GUID != guid) {
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target_guid.guid, guid.guid, 100, unk1, 6, unk2)))
}
}
case Door.DoorMessage(tplayer, msg, order) =>
@ -241,7 +245,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.GUID(door_guid) match {
case Some(door : Door) =>
sendResponse(PacketCoding.CreateGamePacket(0, GenericObjectStateMsg(door_guid, 16)))
localService ! LocalServiceMessage (continent.Id, LocalAction.DoorOpens (tplayer.GUID, continent, door) )
localService ! LocalServiceMessage(continent.Id, LocalAction.DoorOpens (tplayer.GUID, continent, door) )
case _ =>
log.warn(s"door $door_guid wanted to be opened but could not be found")
@ -578,7 +582,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
continent.Actor ! Zone.DropItemOnGround(item, item.Position, item.Orientation) //restore
}
case ItemHacking(tplayer, target, tool_guid, delta) =>
case ItemHacking(tplayer, target, tool_guid, delta, completeAction, tickAction) =>
progressBarUpdate.cancel
if(progressBarValue.isDefined) {
val progressBarVal : Float = progressBarValue.get + delta
@ -594,17 +598,16 @@ class WorldSessionActor extends Actor with MDCContextAware {
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(1, target.GUID.guid, player.GUID.guid, progressBarVal.toInt, 0L, vis, 8L)))
if(progressBarVal > 100) {
progressBarValue = None
log.info(s"We've hacked the item $target! Now what?")
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target.GUID.guid, player.GUID.guid, 100, 1114636288, 6, 8L)))
target.Actor ! CommonMessages.Hack(tplayer)
avatarService ! AvatarServiceMessage(tplayer.Continent, AvatarAction.Hack(tplayer.GUID, target, 1114636288))
//TODO now what?
log.info(s"Hacked a $target")
sendResponse(PacketCoding.CreateGamePacket(0, HackMessage(0, target.GUID.guid, player.GUID.guid, 100, 1114636288L, 6, 8L)))
completeAction()
}
else {
tickAction.getOrElse(() => Unit)()
progressBarValue = Some(progressBarVal)
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, ItemHacking(tplayer, target, tool_guid, delta))
progressBarUpdate = context.system.scheduler.scheduleOnce(250 milliseconds, self, ItemHacking(tplayer, target, tool_guid, delta, completeAction))
}
}
@ -1052,7 +1055,7 @@ class WorldSessionActor extends Actor with MDCContextAware {
case Some(tool : SimpleItem) =>
if(tool.Definition == GlobalDefinitions.remote_electronics_kit) {
progressBarValue = Some(-2.66f)
self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f)
self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, HackTemporary(panel))
}
case _ => ;
}
@ -1622,6 +1625,11 @@ class WorldSessionActor extends Actor with MDCContextAware {
}
}
private def HackTemporary(target : PlanetSideServerObject)() : Unit = {
target.Actor ! CommonMessages.Hack(player)
localService ! LocalServiceMessage(player.Continent, LocalAction.HackTemporarily(player.GUID, continent, target, 1114636288L))
}
def failWithError(error : String) = {
log.error(error)
sendResponse(PacketCoding.CreateControlPacket(ConnectionClose()))
@ -1652,8 +1660,12 @@ object WorldSessionActor {
private final case class PlayerFailedToLoad(tplayer : Player)
private final case class ListAccountCharacters()
private final case class SetCurrentAvatar(tplayer : Player)
private final case class ItemHacking(tplayer : Player, target : PlanetSideServerObject, tool_guid : PlanetSideGUID, delta : Float)
private final case class ItemHacking(tplayer : Player,
target : PlanetSideServerObject,
tool_guid : PlanetSideGUID,
delta : Float,
completeAction : () => Unit,
tickAction : Option[() => Unit] = None)
/**
* A placeholder `Cancellable` object.
*/