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 81069f1f..2a1d0996 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 @@ -39,6 +39,14 @@ class Building(private val building_guid : Int, private val map_id : Int, privat amenities } + def CaptureConsoleIsHacked : Boolean = { + Amenities.filter(x => x.Definition == GlobalDefinitions.capture_terminal).headOption.asInstanceOf[Option[CaptureTerminal]] match { + case Some(obj: CaptureTerminal) => + obj.HackedBy.isDefined + case None => false + } + } + def Zone : Zone = zone def Info : ( diff --git a/common/src/main/scala/services/local/LocalService.scala b/common/src/main/scala/services/local/LocalService.scala index c8bf10f7..07f74387 100644 --- a/common/src/main/scala/services/local/LocalService.scala +++ b/common/src/main/scala/services/local/LocalService.scala @@ -4,7 +4,7 @@ package services.local import akka.actor.{Actor, ActorRef, Props} import net.psforever.objects.ce.Deployable import net.psforever.objects.serverobject.resourcesilo.ResourceSilo -import net.psforever.objects.serverobject.structures.Building +import net.psforever.objects.serverobject.structures.{Amenity, Building} import net.psforever.objects.serverobject.terminals.{CaptureTerminal, ProximityUnit, Terminal} import net.psforever.objects.zones.{InterstellarCluster, Zone} import net.psforever.objects._ @@ -18,6 +18,7 @@ import services.{GenericEventBus, RemoverActor, Service, ServiceManager} import scala.util.Success import scala.concurrent.duration._ import akka.pattern.ask +import net.psforever.objects.serverobject.hackable.Hackable import net.psforever.objects.vehicles.{Utility, UtilityType} import services.ServiceManager.Lookup import services.support.SupportActor @@ -95,6 +96,12 @@ class LocalService extends Actor { hackClearer ! HackClearActor.ObjectIsResecured(target) case LocalAction.HackCaptureTerminal(player_guid, zone, target, unk1, unk2, isResecured) => + // When a CC is hacked (or resecured) all amenities for the base should be unhacked + val hackableAmenities = target.Owner.asInstanceOf[Building].Amenities.filter(x => x.isInstanceOf[Hackable]).map(x => x.asInstanceOf[Amenity with Hackable]) + hackableAmenities.foreach(amenity => + if(amenity.HackedBy.isDefined) { hackClearer ! HackClearActor.ObjectIsResecured(amenity) } + ) + if(isResecured){ hackCapturer ! HackCaptureActor.ClearHack(target, zone) } else { diff --git a/common/src/main/scala/services/local/support/HackCaptureActor.scala b/common/src/main/scala/services/local/support/HackCaptureActor.scala index f0174a15..fd896635 100644 --- a/common/src/main/scala/services/local/support/HackCaptureActor.scala +++ b/common/src/main/scala/services/local/support/HackCaptureActor.scala @@ -66,7 +66,8 @@ class HackCaptureActor extends Actor { case HackCaptureActor.ClearHack(target, _) => hackedObjects = hackedObjects.filterNot(x => x.target == target) - target.Owner.Actor ! Building.SendMapUpdate(all_clients = true) + + if(target.isInstanceOf[CaptureTerminal]) { target.Owner.Actor ! Building.SendMapUpdate(all_clients = true) } // Restart the timer in case the object we just removed was the next one scheduled RestartTimer() diff --git a/pslogin/src/main/scala/WorldSessionActor.scala b/pslogin/src/main/scala/WorldSessionActor.scala index dee89edb..7e7f3a68 100644 --- a/pslogin/src/main/scala/WorldSessionActor.scala +++ b/pslogin/src/main/scala/WorldSessionActor.scala @@ -1253,24 +1253,8 @@ class WorldSessionActor extends Actor with MDCContextAware { log.trace(s"Clearing hack for ${target_guid}") // 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 - - continent.GUID(target_guid) match { - case Some(obj) => - sendResponse(SetEmpireMessage(target_guid, obj.asInstanceOf[FactionAffinity].Faction)) - case None => ; - } - case LocalResponse.HackObject(target_guid, unk1, unk2) => - if(tplayer_guid != guid && continent.GUID(target_guid).get.asInstanceOf[Hackable].HackedBy.get.hackerFaction != 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.hackerFaction == 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.HackCaptureTerminal(target_guid, unk1, unk2, isResecured) => var value = 0L if(isResecured) { @@ -3802,13 +3786,6 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(lock_guid) => val lock = continent.GUID(lock_guid).get.asInstanceOf[IFFLock] - var baseIsHacked = false - lock.Owner.asInstanceOf[Building].Amenities.filter(x => x.Definition == GlobalDefinitions.capture_terminal).headOption.asInstanceOf[Option[CaptureTerminal]] match { - case Some(obj: CaptureTerminal) => - baseIsHacked = obj.HackedBy.isDefined - case None => ; - } - val playerIsOnInside = Vector3.ScalarProjection(lock.Outwards, player.Position - door.Position) < 0f // If an IFF lock exists and the IFF lock faction doesn't match the current player and one of the following conditions are met open the door: @@ -3816,7 +3793,7 @@ class WorldSessionActor extends Actor with MDCContextAware { // A base is hacked // The lock is hacked // The player is on the inside of the door, determined by the lock orientation - lock.HackedBy.isDefined || baseIsHacked || lock.Faction == PlanetSideEmpire.NEUTRAL || playerIsOnInside + lock.HackedBy.isDefined || lock.Owner.asInstanceOf[Building].CaptureConsoleIsHacked || lock.Faction == PlanetSideEmpire.NEUTRAL || playerIsOnInside case None => !door.isOpen // If there's no linked IFF lock just open the door if it's closed. })) { door.Actor ! Door.Use(player, msg) @@ -3861,6 +3838,10 @@ class WorldSessionActor extends Actor with MDCContextAware { } case _ => ; } + } else { + log.warn("IFF lock is being hacked, but don't know how to handle this state") + log.warn(s"Lock - HackedBy.isDefined: ${panel.HackedBy.isDefined} Faction: ${panel.Faction} HackedBy.isEmpty: ${panel.HackedBy.isEmpty}") + log.warn(s"Hacking player - Faction: ${player.Faction}") } case Some(obj : Player) => @@ -4043,53 +4024,73 @@ class WorldSessionActor extends Actor with MDCContextAware { case Some(terminal : Terminal) => val tdef = terminal.Definition - val owned = terminal.Faction == player.Faction - val hacked = terminal.HackedBy.nonEmpty - if(owned) { - if(tdef.isInstanceOf[MatrixTerminalDefinition]) { - //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) - sendResponse(BindPlayerMessage(BindStatus.Bind, "", true, true, SpawnGroup.Sanctuary, 0, 0, terminal.Position)) - } - else if(tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal || - tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal) { - FindLocalVehicle match { - case Some(vehicle) => - 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 if(tdef == GlobalDefinitions.teleportpad_terminal) { - //explicit request - terminal.Actor ! Terminal.Request( - player, - ItemTransactionMessage(object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0)) - ) - } - else { - sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) - } + + // If the base this terminal belongs to has been hacked the owning faction needs to be able to hack it to gain access + val ownerIsHacked = terminal.Owner match { + case b: Building => b.CaptureConsoleIsHacked + case _ => false } - else if(hacked) { - sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) - } - else { - player.Slot(player.DrawnSlot).Equipment match { - case Some(tool: SimpleItem) => - if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { + var playerIsHacking = false + + player.Slot(player.DrawnSlot).Equipment match { + case Some(tool: SimpleItem) => + if (tool.Definition == GlobalDefinitions.remote_electronics_kit) { + if (!terminal.HackedBy.isEmpty) { + log.warn("Player tried to hack a terminal that is already hacked") + log.warn(s"Player faction ${player.Faction} terminal faction: ${terminal.Faction} terminal hacked: ${terminal.HackedBy.isDefined} owner hacked: ${ownerIsHacked}") + } + else if (terminal.Faction != player.Faction || ownerIsHacked) { val hackSpeed = GetPlayerHackSpeed(terminal) - if(hackSpeed > 0) { + if (hackSpeed > 0) { progressBarValue = Some(-hackSpeed) self ! WorldSessionActor.HackingProgress(progressType = 1, player, terminal, tool.GUID, hackSpeed, FinishHacking(terminal, 3212836864L)) + playerIsHacking = true log.info("Hacking a terminal") } } - case _ => ; - } + } + case _ => ; } + if(!playerIsHacking) { + if (terminal.Faction == player.Faction) { + if (tdef.isInstanceOf[MatrixTerminalDefinition]) { + //TODO matrix spawn point; for now, just blindly bind to show work (and hope nothing breaks) + sendResponse(BindPlayerMessage(BindStatus.Bind, "", true, true, SpawnGroup.Sanctuary, 0, 0, terminal.Position)) + } + else if (tdef == GlobalDefinitions.multivehicle_rearm_terminal || tdef == GlobalDefinitions.bfr_rearm_terminal || + tdef == GlobalDefinitions.air_rearm_terminal || tdef == GlobalDefinitions.ground_rearm_terminal) { + FindLocalVehicle match { + case Some(vehicle) => + 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 if (tdef == GlobalDefinitions.teleportpad_terminal) { + //explicit request + terminal.Actor ! Terminal.Request( + player, + ItemTransactionMessage(object_guid, TransactionType.Buy, 0, "router_telepad", 0, PlanetSideGUID(0)) + ) + } + else if (!ownerIsHacked || (ownerIsHacked && terminal.HackedBy.isDefined)) { + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + } + else { + log.warn("Tried to use a terminal, but can't handle this case") + log.warn(s"Terminal - isHacked ${terminal.HackedBy.isDefined} ownerIsHacked ${ownerIsHacked}") + } + } + else if (terminal.HackedBy.isDefined) { + sendResponse(UseItemMessage(avatar_guid, item_used_guid, object_guid, unk2, unk3, unk4, unk5, unk6, unk7, unk8, itemType)) + } else { + log.warn("Tried to use a terminal that doesn't belong to this faction and isn't hacked") + log.warn(s"Player faction ${player.Faction} terminal faction: ${terminal.Faction} terminal hacked: ${terminal.HackedBy.isDefined} owner hacked: ${ownerIsHacked}") + } + } case Some(obj : SpawnTube) => //deconstruction PlayerActionsToCancel() @@ -5157,7 +5158,7 @@ class WorldSessionActor extends Actor with MDCContextAware { case term : CaptureTerminal => val isResecured = player.Faction == target.Faction localService ! LocalServiceMessage(continent.Id, LocalAction.HackCaptureTerminal(player.GUID, continent, term, unk, 8L, isResecured)) - case _ =>localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk, target.HackEffectDuration(GetPlayerHackLevel()))) + case _ => localService ! LocalServiceMessage(continent.Id, LocalAction.HackTemporarily(player.GUID, continent, target, unk, target.HackEffectDuration(GetPlayerHackLevel()))) } case scala.util.Failure(_) => log.warn(s"Hack message failed on target guid: ${target.GUID}") }