From c6eff22df7a01df0bdd7a517b75588ba34b44eeb Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 5 Jun 2018 19:43:44 +0100 Subject: [PATCH 1/4] Add Hackable trait to all terminals / IFF locks / lockers --- .../serverobject/hackable/Hackable.scala | 41 ++++++++++++++++++ .../objects/serverobject/locks/IFFLock.scala | 42 +------------------ .../serverobject/mblocker/Locker.scala | 3 +- .../serverobject/mblocker/LockerControl.scala | 6 +++ .../terminals/ProximityTerminalControl.scala | 6 +++ .../serverobject/terminals/Terminal.scala | 39 ++--------------- .../terminals/TerminalControl.scala | 7 ++++ 7 files changed, 68 insertions(+), 76 deletions(-) create mode 100644 common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala diff --git a/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala b/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala new file mode 100644 index 00000000..1ffb34f6 --- /dev/null +++ b/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala @@ -0,0 +1,41 @@ +package net.psforever.objects.serverobject.hackable + +import net.psforever.objects.Player +import net.psforever.packet.game.PlanetSideGUID +import net.psforever.types.Vector3 + +trait Hackable { + /** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */ + private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None + + def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy + + def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent)) + + /** + * Set the hack state of this object by recording important information about the player that caused it. + * Set the hack state if there is no current hack state. + * Override the hack state with a new hack state if the new user has different faction affiliation. + * @param agent a `Player`, or no player + * @return the player hack entry + */ + def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = { + hackedBy match { + case None => + //set the hack state if there is no current hack state + if(agent.isDefined) { + hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) + } + case Some(_) => + //clear the hack state + if(agent.isEmpty) { + hackedBy = None + } + //override the hack state with a new hack state if the new user has different faction affiliation + else if(agent.get.Faction != hackedBy.get._1.Faction) { + hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) + } + } + HackedBy + } +} diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala index 8142fd9a..b22ae76d 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala @@ -1,10 +1,8 @@ // Copyright (c) 2017 PSForever package net.psforever.objects.serverobject.locks -import net.psforever.objects.Player +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity -import net.psforever.packet.game.PlanetSideGUID -import net.psforever.types.Vector3 /** * A structure-owned server object that is a "door lock."
@@ -15,43 +13,7 @@ import net.psforever.types.Vector3 * The `IFFLock` is ideally associated with a server map object - a `Door` - to which it acts as a gatekeeper. * @param idef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class IFFLock(private val idef : IFFLockDefinition) extends Amenity { - /** - * An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. - */ - private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None - - def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy - - def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent)) - - /** - * Set the hack state of this object by recording important information about the player that caused it. - * Set the hack state if there is no current hack state. - * Override the hack state with a new hack state if the new user has different faction affiliation. - * @param agent a `Player`, or no player - * @return the player hack entry - */ - def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = { - hackedBy match { - case None => - //set the hack state if there is no current hack state - if(agent.isDefined) { - hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) - } - case Some(_) => - //clear the hack state - if(agent.isEmpty) { - hackedBy = None - } - //override the hack state with a new hack state if the new user has different faction affiliation - else if(agent.get.Faction != hackedBy.get._1.Faction) { - hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) - } - } - HackedBy - } - +class IFFLock(private val idef : IFFLockDefinition) extends Amenity with Hackable { def Definition : IFFLockDefinition = idef } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala index d614b53a..39906ac7 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala @@ -3,9 +3,10 @@ package net.psforever.objects.serverobject.mblocker import akka.actor.{ActorContext, Props} import net.psforever.objects.GlobalDefinitions +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity -class Locker extends Amenity { +class Locker extends Amenity with Hackable { def Definition : LockerDefinition = GlobalDefinitions.mb_locker } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala index 2e037e64..75df5730 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.mblocker import akka.actor.Actor +import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** @@ -12,6 +13,11 @@ class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior. def FactionObject : FactionAffinity = locker def receive : Receive = checkBehavior.orElse { + case CommonMessages.Hack(player) => + locker.HackedBy = player + + case CommonMessages.ClearHack() => + locker.HackedBy = None case _ => ; } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 753cdb65..540da72b 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor +import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** @@ -18,6 +19,11 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor def receive : Receive = checkBehavior .orElse(proximityBehavior) .orElse { + case CommonMessages.Hack(player) => + term.HackedBy = player + + case CommonMessages.ClearHack() => + term.HackedBy = None case _ => ; } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala index 7a8676bb..03323cc5 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala @@ -3,48 +3,17 @@ package net.psforever.objects.serverobject.terminals import net.psforever.objects.Player import net.psforever.objects.definition.VehicleDefinition +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity -import net.psforever.packet.game.{ItemTransactionMessage, PlanetSideGUID} -import net.psforever.types.{TransactionType, Vector3} +import net.psforever.packet.game.{ItemTransactionMessage} +import net.psforever.types.{TransactionType} /** * A structure-owned server object that is a "terminal" that can be accessed for amenities and services. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ -class Terminal(tdef : TerminalDefinition) extends Amenity { - /** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */ - private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None +class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable { - def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy - - def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent)) - - /** - * Set the hack state of this object by recording important information about the player that caused it. - * Set the hack state if there is no current hack state. - * Override the hack state with a new hack state if the new user has different faction affiliation. - * @param agent a `Player`, or no player - * @return the player hack entry - */ - def HackedBy_=(agent : Option[Player]) : Option[(Player, PlanetSideGUID, Vector3)] = { - hackedBy match { - case None => - //set the hack state if there is no current hack state - if(agent.isDefined) { - hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) - } - case Some(_) => - //clear the hack state - if(agent.isEmpty) { - hackedBy = None - } - //override the hack state with a new hack state if the new user has different faction affiliation - else if(agent.get.Faction != hackedBy.get._1.Faction) { - hackedBy = Some(agent.get, agent.get.GUID, agent.get.Position) - } - } - HackedBy - } //the following fields and related methods are neither finalized nor integrated; GOTO Request private var health : Int = 100 //TODO not real health value diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index 4efa324e..e226fad6 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -2,6 +2,7 @@ package net.psforever.objects.serverobject.terminals import akka.actor.Actor +import net.psforever.objects.serverobject.CommonMessages import net.psforever.objects.serverobject.affinity.{FactionAffinity, FactionAffinityBehavior} /** @@ -15,6 +16,12 @@ class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavio case Terminal.Request(player, msg) => sender ! Terminal.TerminalMessage(player, msg, term.Request(player, msg)) + case CommonMessages.Hack(player) => + term.HackedBy = player + + case CommonMessages.ClearHack() => + term.HackedBy = None + case _ => ; } From 21b0f07fb7a1aeacb24ada795ea056259cd2813d Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 5 Jun 2018 19:49:43 +0100 Subject: [PATCH 2/4] Documentation update --- .../game/PlanetsideAttributeMessage.scala | 1 + .../packet/game/UseItemMessage.scala | 6 +-- .../src/main/scala/WorldSessionActor.scala | 39 +++++++++++++------ 3 files changed, 32 insertions(+), 14 deletions(-) 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 5d0dc875..eb6f8e2f 100644 --- a/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/PlanetsideAttributeMessage.scala @@ -118,6 +118,7 @@ import scodec.codecs._ * `77 - Cavern Facility Captures. Value is the number of captures`
* `78 - Cavern Kills. Value is the number of kills`
* `106 - Custom Head`
+ * `116 - Apply colour to REK beam and REK icon above players (0 = yellow, 1 = red, 2 = purple, 3 = blue)`
* Client to Server :
* `106 - Custom Head`
*
diff --git a/common/src/main/scala/net/psforever/packet/game/UseItemMessage.scala b/common/src/main/scala/net/psforever/packet/game/UseItemMessage.scala index 0df5c38f..b66180b9 100644 --- a/common/src/main/scala/net/psforever/packet/game/UseItemMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/UseItemMessage.scala @@ -9,7 +9,7 @@ import scodec.codecs._ /** * (Where the child object was before it was moved is not specified or important.)
* @param avatar_guid the player. - * @param unk1 dont know how call that. It's the "item" ID when use a rek to hack or a medkit to heal. + * @param item_used_guid The "item" GUID used e.g. a rek to hack or a medkit to heal. * @param object_guid can be : Door, Terminal, Avatar (medkit). * @param unk2 ??? * @param unk3 ??? true when use a rek (false when door, medkit or open equip term) @@ -21,7 +21,7 @@ import scodec.codecs._ * @param itemType object ID from game_objects.adb (ex 612 is an equipment terminal, for medkit we have 121 (avatar)) */ final case class UseItemMessage(avatar_guid : PlanetSideGUID, - unk1 : Int, + item_used_guid : Int, object_guid : PlanetSideGUID, unk2 : Long, unk3 : Boolean, @@ -40,7 +40,7 @@ final case class UseItemMessage(avatar_guid : PlanetSideGUID, object UseItemMessage extends Marshallable[UseItemMessage] { implicit val codec : Codec[UseItemMessage] = ( ("avatar_guid" | PlanetSideGUID.codec) :: - ("unk1" | uint16L) :: + ("item_used_guid" | uint16L) :: ("object_guid" | PlanetSideGUID.codec) :: ("unk2" | uint32L) :: ("unk3" | bool) :: diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 8ec79f16..2ae07b74 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -2410,7 +2410,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case msg @ AvatarImplantMessage(_, _, _, _) => //(player_guid, unk1, unk2, implant) => log.info("AvatarImplantMessage: " + msg) - case msg @ UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) => + case msg @ UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType) => log.info("UseItem: " + msg) // TODO: Not all fields in the response are identical to source in real packet logs (but seems to be ok) // TODO: Not all incoming UseItemMessage's respond with another UseItemMessage (i.e. doors only send out GenericObjectStateMsg) @@ -2444,11 +2444,11 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(obj : Player) => if(obj.isBackpack) { log.info(s"UseItem: $player looting the corpse of $obj") - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) accessedContainer = Some(obj) } else if(!unk3) { //potential kit use - continent.GUID(unk1) match { + continent.GUID(item_used_guid) match { case Some(kit : Kit) => player.Find(kit) match { case Some(index) => @@ -2464,7 +2464,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(index) => whenUsedLastKit = System.currentTimeMillis player.Slot(index).Equipment = None //remove from slot immediately; must exist on client for next packet - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, 0, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) sendResponse(ObjectDeleteMessage(kit.GUID, 0)) taskResolver ! GUIDTask.UnregisterEquipment(kit)(continent.GUID) //TODO better health/damage control workflow @@ -2486,7 +2486,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(item) => log.warn(s"UseItem: looking for Kit to use, but found $item instead") case None => - log.warn(s"UseItem: anticipated a Kit $unk1, but can't find it") + log.warn(s"UseItem: anticipated a Kit $item_used_guid, but can't find it") } } @@ -2495,7 +2495,7 @@ class WorldSessionActor extends Actor with MDCContextAware { log.info(s"UseItem: $player accessing a locker") val container = player.Locker accessedContainer = Some(container) - sendResponse(UseItemMessage(avatar_guid, unk1, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, container.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, 456)) } else { log.info(s"UseItem: not $player's locker") @@ -2517,7 +2517,7 @@ class WorldSessionActor extends Actor with MDCContextAware { obj.AccessingTrunk = player.GUID accessedContainer = Some(obj) AccessContents(obj) - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) } else { log.info(s"UseItem: $player can not cut in line while player ${obj.AccessingTrunk.get} is using $obj's trunk") @@ -2550,14 +2550,31 @@ class WorldSessionActor extends Actor with MDCContextAware { else if(obj.Definition.isInstanceOf[RepairRearmSiloDefinition]) { FindLocalVehicle match { case Some(vehicle) => - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) - sendResponse(UseItemMessage(avatar_guid, unk1, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, vehicle.GUID, unk2, unk3, unk4, unk5, unk6, unk7, unk8, vehicle.Definition.ObjectId)) case None => log.error("UseItem: expected seated vehicle, but found none") } } else { - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) { + log.warn(s"${obj.Faction} ${player.Faction} ${obj.HackedBy}") + player.Slot(player.DrawnSlot).Equipment match { + case Some(tool: SimpleItem) => + if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { + //TODO get player hack level (for now, presume 15s in intervals of 4/s) + progressBarValue = Some(-GetPlayerHackSpeed()) + self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHackingTerminal(obj, 3212836864L)) + log.info("Hacking a terminal") + } + case _ => ; + } + } else if (obj.Faction == player.Faction || !obj.HackedBy.isEmpty) { + // If hacked only allow access to the faction that hacked it + // Otherwise allow the faction that owns the terminal to use it + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + } + } case Some(obj : SpawnTube) => @@ -2571,7 +2588,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(obj) => log.warn(s"UseItem: don't know how to handle $obj; taking a shot in the dark") - sendResponse(UseItemMessage(avatar_guid, unk1, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) case None => log.error(s"UseItem: can not find object $object_guid") From 0c964ddfca22b64d642c07e3f8f5f1f36560c463 Mon Sep 17 00:00:00 2001 From: Mazo Date: Tue, 5 Jun 2018 19:59:53 +0100 Subject: [PATCH 3/4] Locker hacking Terminal hacking (cert / equipment / medical) Hacking speed based on certification level REK beam colour based on hacking level QoL change - show hacked object as belonging to faction that hacked it for the duration of the hack (only for that faction's players) --- .../src/main/scala/WorldSessionActor.scala | 106 ++++++++++++++++-- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 2ae07b74..85a18e21 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -24,6 +24,7 @@ import net.psforever.objects.serverobject.affinity.FactionAffinity import net.psforever.objects.serverobject.deploy.Deployment import net.psforever.objects.serverobject.{CommonMessages, PlanetSideServerObject} import net.psforever.objects.serverobject.doors.Door +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.implantmech.ImplantTerminalMech import net.psforever.objects.serverobject.locks.IFFLock import net.psforever.objects.serverobject.mblocker.Locker @@ -416,13 +417,23 @@ class WorldSessionActor extends Actor with MDCContextAware { sendResponse(GenericObjectStateMsg(door_guid, 17)) case LocalResponse.HackClear(target_guid, unk1, unk2) => + // Reset hack state for all players sendResponse(HackMessage(0, target_guid, guid, 0, unk1, HackState.HackCleared, unk2)) + // Set the object faction displayed back to it's original owner faction + sendResponse(SetEmpireMessage(target_guid, continent.GUID(target_guid).get.asInstanceOf[FactionAffinity].Faction)) case LocalResponse.HackObject(target_guid, unk1, unk2) => - if(player.GUID != guid) { + if(player.GUID != guid && continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction != player.Faction) { + // If the player is not in the faction that hacked this object then send the packet that it's been hacked, so they can either unhack it or use the hacked object + // Don't send this to the faction that hacked the object, otherwise it will interfere with the new SetEmpireMessage QoL change that changes the object colour to their faction (but only visible to that faction) sendResponse(HackMessage(0, target_guid, guid, 100, unk1, HackState.Hacked, unk2)) } + if(continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get._1.Faction == player.Faction){ + // Make the hacked object look like it belongs to the hacking empire, but only for that empire's players (so that infiltrators on stealth missions won't be given away to opposing factions) + sendResponse(SetEmpireMessage(target_guid, player.Faction)) + } + case LocalResponse.ProximityTerminalEffect(object_guid, effectState) => if(player.GUID != guid) { sendResponse(ProximityTerminalUseMessage(PlanetSideGUID(0), object_guid, effectState)) @@ -1509,7 +1520,7 @@ class WorldSessionActor extends Actor with MDCContextAware { if(progressBarVal > 100) { //done progressBarValue = None log.info(s"Hacked a $target") - sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L)) +// sendResponse(HackMessage(0, target.GUID, player.GUID, 100, 1114636288L, HackState.Hacked, 8L)) completeAction() } else { //continue next tick @@ -2238,6 +2249,23 @@ class WorldSessionActor extends Actor with MDCContextAware { } else if((player.DrawnSlot = held_holsters) != before) { avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.ObjectHeld(player.GUID, player.LastDrawnSlot)) + + + // Ignore non-equipment holsters + //todo: check current suit holster slots? + if(held_holsters >= 0 && held_holsters < 5) { + player.Holsters()(held_holsters).Equipment match { + case Some(unholsteredItem : Equipment) => + if(unholsteredItem.Definition == GlobalDefinitions.remote_electronics_kit) { + // Player has ulholstered a REK - we need to set an atttribute on the REK itself to change the beam/icon colour to the correct one for the player's hack level + avatarService ! AvatarServiceMessage(player.Continent, AvatarAction.PlanetsideAttribute(unholsteredItem.GUID, 116, GetPlayerHackData().hackLevel)) + } + case None => ; + } + + } + + // Stop using proximity terminals if player unholsters a weapon (which should re-trigger the proximity effect and re-holster the weapon) if(player.VisibleSlots.contains(held_holsters)) { usingMedicalTerminal match { case Some(term_guid) => @@ -2432,9 +2460,8 @@ class WorldSessionActor extends Actor with MDCContextAware { player.Slot(player.DrawnSlot).Equipment match { case Some(tool : SimpleItem) => if(tool.Definition == GlobalDefinitions.remote_electronics_kit) { - //TODO get player hack level (for now, presume 15s in intervals of 4/s) - progressBarValue = Some(-2.66f) - self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, 2.66f, FinishHackingDoor(panel, 1114636288L)) + progressBarValue = Some(-GetPlayerHackSpeed()) + self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHackingDoor(panel, 1114636288L)) log.info("Hacking a door~") } case _ => ; @@ -2491,7 +2518,17 @@ class WorldSessionActor extends Actor with MDCContextAware { } case Some(obj : Locker) => - if(player.Faction == obj.Faction) { + if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) { + player.Slot(player.DrawnSlot).Equipment match { + case Some(tool: SimpleItem) => + if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { + progressBarValue = Some(-GetPlayerHackSpeed()) + self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHackingLocker(obj, 3212836864L)) + log.info("Hacking a locker") + } + case _ => ; + } + } else if(player.Faction == obj.Faction || !obj.HackedBy.isEmpty) { log.info(s"UseItem: $player accessing a locker") val container = player.Locker accessedContainer = Some(container) @@ -2558,11 +2595,9 @@ class WorldSessionActor extends Actor with MDCContextAware { } else { if(obj.Faction != player.Faction && obj.HackedBy.isEmpty) { - log.warn(s"${obj.Faction} ${player.Faction} ${obj.HackedBy}") player.Slot(player.DrawnSlot).Equipment match { case Some(tool: SimpleItem) => if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { - //TODO get player hack level (for now, presume 15s in intervals of 4/s) progressBarValue = Some(-GetPlayerHackSpeed()) self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHackingTerminal(obj, 3212836864L)) log.info("Hacking a terminal") @@ -3398,13 +3433,42 @@ class WorldSessionActor extends Actor with MDCContextAware { * @see `HackMessage` */ //TODO add params here depending on which params in HackMessage are important - //TODO sound should be centered on IFFLock, not on player private def FinishHackingDoor(target : IFFLock, unk : Long)() : Unit = { target.Actor ! CommonMessages.Hack(player) localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) } + /** + * The process of hacking a terminal + * Pass the message onto the terminal and onto the local events system. + * @param target the `terminal` being hacked + * @param unk na; + * used by `HackingMessage` as `unk5` + * @see `HackMessage` + */ + //TODO add params here depending on which params in HackMessage are important + private def FinishHackingTerminal(target : Terminal, unk : Long)() : Unit = { + target.Actor ! CommonMessages.Hack(player) + localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) + localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) + } + + /** + * The process of hacking a locker + * Pass the message onto the locker and onto the local events system. + * @param target the `locker` being hacked + * @param unk na; + * used by `HackingMessage` as `unk5` + * @see `HackMessage` + */ + private def FinishHackingLocker(target : Locker, unk : Long)() : Unit = { + target.Actor ! CommonMessages.Hack(player) + localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) + localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) + } + + /** * Temporary function that iterates over vehicle permissions and turns them into `PlanetsideAttributeMessage` packets.
*
@@ -4801,6 +4865,30 @@ class WorldSessionActor extends Actor with MDCContextAware { log.trace("WORLD SEND RAW: " + pkt) sendResponse(RawPacket(pkt)) } + + def GetPlayerHackSpeed(): Float = { + if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) { + 10.64f + } else if(player.Certifications.contains(CertificationType.AdvancedHacking)) { + 5.32f + } else { + 2.66f + } + } + + def GetPlayerHackData(): PlayerHackData = { + if(player.Certifications.contains(CertificationType.ExpertHacking) || player.Certifications.contains(CertificationType.ElectronicsExpert)) { + PlayerHackData(3, 10.64f) + } else if(player.Certifications.contains(CertificationType.AdvancedHacking)) { + PlayerHackData(2, 7.98f) + } else if (player.Certifications.contains(CertificationType.Hacking)) { + PlayerHackData(1, 5.32f) + } else { + PlayerHackData(0, 2.66f) + } + } + + case class PlayerHackData(hackLevel: Int, hackSpeed: Float) } object WorldSessionActor { From ca8d72cc4cfec4f8ad3f242055145622dc08a9ae Mon Sep 17 00:00:00 2001 From: Mazo Date: Mon, 11 Jun 2018 18:27:28 +0100 Subject: [PATCH 4/4] Added correct sounds for hacking terminals/lockers & consolidated FinishHacking function Wait for target actor to set HackedBy property before sending LocalAction.HackTemporarily to fix crash when run in the wrong order --- .../serverobject/hackable/Hackable.scala | 10 +++- .../objects/serverobject/locks/IFFLock.scala | 2 + .../serverobject/locks/IFFLockControl.scala | 2 +- .../serverobject/mblocker/Locker.scala | 2 + .../serverobject/mblocker/LockerControl.scala | 2 +- .../terminals/ProximityTerminalControl.scala | 2 +- .../serverobject/terminals/Terminal.scala | 6 +-- .../terminals/TerminalControl.scala | 2 +- .../packet/game/TriggerSoundMessage.scala | 4 +- .../src/main/scala/WorldSessionActor.scala | 54 ++++++------------- 10 files changed, 38 insertions(+), 48 deletions(-) diff --git a/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala b/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala index 1ffb34f6..ef5bbd3f 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/hackable/Hackable.scala @@ -1,13 +1,15 @@ package net.psforever.objects.serverobject.hackable import net.psforever.objects.Player -import net.psforever.packet.game.PlanetSideGUID +import net.psforever.packet.game.{PlanetSideGUID, TriggeredSound} import net.psforever.types.Vector3 trait Hackable { /** An entry that maintains a reference to the `Player`, and the player's GUID and location when the message was received. */ private var hackedBy : Option[(Player, PlanetSideGUID, Vector3)] = None + private var hackSound : TriggeredSound.Value = TriggeredSound.HackDoor + def HackedBy : Option[(Player, PlanetSideGUID, Vector3)] = hackedBy def HackedBy_=(agent : Player) : Option[(Player, PlanetSideGUID, Vector3)] = HackedBy_=(Some(agent)) @@ -38,4 +40,10 @@ trait Hackable { } HackedBy } + + def HackSound : TriggeredSound.Value = hackSound + def HackSound_=(sound : TriggeredSound.Value) : TriggeredSound.Value = { + hackSound = sound + hackSound + } } diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala index b22ae76d..6c21bdc4 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLock.scala @@ -3,6 +3,7 @@ package net.psforever.objects.serverobject.locks import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity +import net.psforever.packet.game.TriggeredSound /** * A structure-owned server object that is a "door lock."
@@ -15,6 +16,7 @@ import net.psforever.objects.serverobject.structures.Amenity */ class IFFLock(private val idef : IFFLockDefinition) extends Amenity with Hackable { def Definition : IFFLockDefinition = idef + HackSound = TriggeredSound.HackDoor } object IFFLock { diff --git a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala index 0af05811..960a1c3c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/locks/IFFLockControl.scala @@ -16,7 +16,7 @@ class IFFLockControl(lock : IFFLock) extends Actor with FactionAffinityBehavior. def receive : Receive = checkBehavior.orElse { case CommonMessages.Hack(player) => lock.HackedBy = player - + sender ! true case CommonMessages.ClearHack() => lock.HackedBy = None diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala index 39906ac7..c91c787d 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/Locker.scala @@ -5,9 +5,11 @@ import akka.actor.{ActorContext, Props} import net.psforever.objects.GlobalDefinitions import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity +import net.psforever.packet.game.TriggeredSound class Locker extends Amenity with Hackable { def Definition : LockerDefinition = GlobalDefinitions.mb_locker + HackSound = TriggeredSound.HackTerminal } object Locker { diff --git a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala index 75df5730..7bced8d8 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/mblocker/LockerControl.scala @@ -15,7 +15,7 @@ class LockerControl(locker : Locker) extends Actor with FactionAffinityBehavior. def receive : Receive = checkBehavior.orElse { case CommonMessages.Hack(player) => locker.HackedBy = player - + sender ! true case CommonMessages.ClearHack() => locker.HackedBy = None case _ => ; diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala index 540da72b..fcc8c073 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/ProximityTerminalControl.scala @@ -21,7 +21,7 @@ class ProximityTerminalControl(term : Terminal with ProximityUnit) extends Actor .orElse { case CommonMessages.Hack(player) => term.HackedBy = player - + sender ! true case CommonMessages.ClearHack() => term.HackedBy = None case _ => ; diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala index 03323cc5..61f587e7 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/Terminal.scala @@ -5,15 +5,15 @@ import net.psforever.objects.Player import net.psforever.objects.definition.VehicleDefinition import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.serverobject.structures.Amenity -import net.psforever.packet.game.{ItemTransactionMessage} -import net.psforever.types.{TransactionType} +import net.psforever.packet.game.{ItemTransactionMessage, TriggeredSound} +import net.psforever.types.TransactionType /** * A structure-owned server object that is a "terminal" that can be accessed for amenities and services. * @param tdef the `ObjectDefinition` that constructs this object and maintains some of its immutable fields */ class Terminal(tdef : TerminalDefinition) extends Amenity with Hackable { - + HackSound = TriggeredSound.HackTerminal //the following fields and related methods are neither finalized nor integrated; GOTO Request private var health : Int = 100 //TODO not real health value diff --git a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala index e226fad6..7474706c 100644 --- a/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala +++ b/common/src/main/scala/net/psforever/objects/serverobject/terminals/TerminalControl.scala @@ -18,7 +18,7 @@ class TerminalControl(term : Terminal) extends Actor with FactionAffinityBehavio case CommonMessages.Hack(player) => term.HackedBy = player - + sender ! true case CommonMessages.ClearHack() => term.HackedBy = None diff --git a/common/src/main/scala/net/psforever/packet/game/TriggerSoundMessage.scala b/common/src/main/scala/net/psforever/packet/game/TriggerSoundMessage.scala index 24cc5fa9..68bda057 100644 --- a/common/src/main/scala/net/psforever/packet/game/TriggerSoundMessage.scala +++ b/common/src/main/scala/net/psforever/packet/game/TriggerSoundMessage.scala @@ -16,8 +16,8 @@ object TriggeredSound extends Enumeration { val SpawnInTube, - Unknown1, - Hack, + HackTerminal, + HackVehicle, HackDoor, Unknown4, LockedOut, diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index 1372b856..5ecd81c6 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -45,8 +45,10 @@ import services.vehicle.VehicleAction.UnstowEquipment import services.vehicle.{VehicleAction, VehicleResponse, VehicleServiceMessage, VehicleServiceResponse} import scala.annotation.tailrec +import scala.concurrent.Future import scala.concurrent.duration._ import scala.util.Success +import akka.pattern.ask class WorldSessionActor extends Actor with MDCContextAware { import WorldSessionActor._ @@ -2469,7 +2471,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(tool : SimpleItem) => if(tool.Definition == GlobalDefinitions.remote_electronics_kit) { progressBarValue = Some(-GetPlayerHackSpeed()) - self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHackingDoor(panel, 1114636288L)) + self ! WorldSessionActor.ItemHacking(player, panel, tool.GUID, GetPlayerHackSpeed(), FinishHacking(panel, 1114636288L)) log.info("Hacking a door~") } case _ => ; @@ -2531,7 +2533,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(tool: SimpleItem) => if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { progressBarValue = Some(-GetPlayerHackSpeed()) - self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHackingLocker(obj, 3212836864L)) + self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L)) log.info("Hacking a locker") } case _ => ; @@ -2607,7 +2609,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(tool: SimpleItem) => if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { progressBarValue = Some(-GetPlayerHackSpeed()) - self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHackingTerminal(obj, 3212836864L)) + self ! WorldSessionActor.ItemHacking(player, obj, tool.GUID, GetPlayerHackSpeed(), FinishHacking(obj, 3212836864L)) log.info("Hacking a terminal") } case _ => ; @@ -3433,47 +3435,23 @@ class WorldSessionActor extends Actor with MDCContextAware { } /** - * The process of hacking the `Door` `IFFLock` is completed. - * Pass the message onto the lock and onto the local events system. - * @param target the `IFFLock` belonging to the door that is being hacked + * The process of hacking an object is completed + * Pass the message onto the hackable object and onto the local events system. + * @param target the `Hackable` object that has been hacked * @param unk na; - * used by `HackingMessage` as `unk5` + * used by `HackMessage` as `unk5` * @see `HackMessage` */ //TODO add params here depending on which params in HackMessage are important - private def FinishHackingDoor(target : IFFLock, unk : Long)() : Unit = { - target.Actor ! CommonMessages.Hack(player) - localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) + private def FinishHacking(target : PlanetSideServerObject with Hackable, unk : Long)() : Unit = { + // Wait for the target actor to set the HackedBy property, otherwise LocalAction.HackTemporarily will not complete properly + import scala.concurrent.ExecutionContext.Implicits.global + ask(target.Actor, CommonMessages.Hack(player))(1 second).mapTo[Boolean].onComplete { + case Success(_) => + localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, target.HackSound, player.Position, 30, 0.49803925f)) localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) + case scala.util.Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}") } - - /** - * The process of hacking a terminal - * Pass the message onto the terminal and onto the local events system. - * @param target the `terminal` being hacked - * @param unk na; - * used by `HackingMessage` as `unk5` - * @see `HackMessage` - */ - //TODO add params here depending on which params in HackMessage are important - private def FinishHackingTerminal(target : Terminal, unk : Long)() : Unit = { - target.Actor ! CommonMessages.Hack(player) - localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) - localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) - } - - /** - * The process of hacking a locker - * Pass the message onto the locker and onto the local events system. - * @param target the `locker` being hacked - * @param unk na; - * used by `HackingMessage` as `unk5` - * @see `HackMessage` - */ - private def FinishHackingLocker(target : Locker, unk : Long)() : Unit = { - target.Actor ! CommonMessages.Hack(player) - localService ! LocalServiceMessage(continent.Id, LocalAction.TriggerSound(player.GUID, TriggeredSound.HackDoor, player.Position, 30, 0.49803925f)) - localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk)) }